feat(web): better timelines, add use obj, add notifs
This commit is contained in:
parent
cc45de7e6d
commit
d275ce7f04
8 changed files with 88 additions and 43 deletions
|
@ -4,6 +4,44 @@ use crate::prelude::*;
|
|||
|
||||
use leptos_use::{storage::use_local_storage, use_cookie, utils::{FromToStringCodec, JsonCodec}};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Feeds {
|
||||
// object feeds
|
||||
pub home: Timeline,
|
||||
pub global: Timeline,
|
||||
// notification feeds
|
||||
pub private: Timeline,
|
||||
pub public: Timeline,
|
||||
// exploration feeds
|
||||
pub user: Timeline,
|
||||
pub server: Timeline,
|
||||
pub context: Timeline,
|
||||
}
|
||||
|
||||
impl Feeds {
|
||||
pub fn new(username: &str) -> Self {
|
||||
Feeds {
|
||||
home: Timeline::new(format!("{URL_BASE}/actors/{username}/feed/page")),
|
||||
global: Timeline::new(format!("{URL_BASE}/feed/page")),
|
||||
private: Timeline::new(format!("{URL_BASE}/actors/{username}/inbox/page")),
|
||||
public: Timeline::new(format!("{URL_BASE}/inbox/page")),
|
||||
user: Timeline::new(format!("{URL_BASE}/actors/{username}/outbox/page")),
|
||||
server: Timeline::new(format!("{URL_BASE}/outbox/page")),
|
||||
context: Timeline::new(format!("{URL_BASE}/outbox/page")), // TODO ehhh
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
self.home.reset(None);
|
||||
self.global.reset(None);
|
||||
self.private.reset(None);
|
||||
self.public.reset(None);
|
||||
self.user.reset(None);
|
||||
self.server.reset(None);
|
||||
self.context.reset(None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
|
@ -12,18 +50,16 @@ pub fn App() -> impl IntoView {
|
|||
let (config, set_config, _) = use_local_storage::<crate::Config, JsonCodec>("config");
|
||||
|
||||
let auth = Auth { token, userid };
|
||||
provide_context(auth);
|
||||
provide_context(config);
|
||||
|
||||
let username = auth.userid.get_untracked()
|
||||
.map(|x| x.split('/').last().unwrap_or_default().to_string())
|
||||
.unwrap_or_default();
|
||||
let home_tl = Timeline::new(format!("{URL_BASE}/actors/{username}/inbox/page"));
|
||||
let user_tl = Timeline::new(format!("{URL_BASE}/actors/{username}/outbox/page"));
|
||||
let server_tl = Timeline::new(format!("{URL_BASE}/inbox/page"));
|
||||
let local_tl = Timeline::new(format!("{URL_BASE}/outbox/page"));
|
||||
|
||||
let context_tl = Timeline::new(format!("{URL_BASE}/outbox/page")); // TODO ehhh
|
||||
let feeds = Feeds::new(&username);
|
||||
|
||||
provide_context(auth);
|
||||
provide_context(config);
|
||||
provide_context(feeds);
|
||||
|
||||
let reply_controls = ReplyControls::default();
|
||||
provide_context(reply_controls);
|
||||
|
@ -35,13 +71,14 @@ pub fn App() -> impl IntoView {
|
|||
|
||||
let title_target = move || if auth.present() { "/web/home" } else { "/web/server" };
|
||||
|
||||
local_tl.more(auth); // public outbox never contains private posts
|
||||
spawn_local(async move {
|
||||
// refresh token first, or verify that we're still authed
|
||||
if Auth::refresh(auth.token, set_token, set_userid).await {
|
||||
home_tl.more(auth); // home inbox requires auth to be read
|
||||
feeds.home.more(auth); // home inbox requires auth to be read
|
||||
feeds.private.more(auth);
|
||||
}
|
||||
server_tl.more(auth); // server inbox may contain private posts
|
||||
feeds.global.more(auth);
|
||||
feeds.public.more(auth); // server inbox may contain private posts
|
||||
});
|
||||
|
||||
|
||||
|
@ -67,8 +104,6 @@ pub fn App() -> impl IntoView {
|
|||
<LoginBox
|
||||
token_tx=set_token
|
||||
userid_tx=set_userid
|
||||
home_tl=home_tl
|
||||
server_tl=server_tl
|
||||
/>
|
||||
<hr class="mt-1 mb-1" />
|
||||
<div class:hidden=move || !auth.present() >
|
||||
|
@ -105,16 +140,17 @@ pub fn App() -> impl IntoView {
|
|||
}
|
||||
/>
|
||||
|
||||
<Route path="/web/home" view=move || view! { <TimelinePage name="home" tl=home_tl /> } />
|
||||
<Route path="/web/server" view=move || view! { <TimelinePage name="server" tl=server_tl /> } />
|
||||
<Route path="/web/local" view=move || view! { <TimelinePage name="local" tl=local_tl /> } />
|
||||
<Route path="/web/home" view=move || view! { <TimelinePage name="home" tl=feeds.home /> } />
|
||||
<Route path="/web/server" view=move || view! { <TimelinePage name="server" tl=feeds.global /> } />
|
||||
<Route path="/web/local" view=move || view! { <TimelinePage name="local" tl=feeds.server /> } />
|
||||
<Route path="/web/inbox" view=move || view! { <TimelinePage name="inbox" tl=feeds.private /> } />
|
||||
|
||||
<Route path="/web/about" view=AboutPage />
|
||||
<Route path="/web/config" view=move || view! { <ConfigPage setter=set_config /> } />
|
||||
<Route path="/web/config/dev" view=DebugPage />
|
||||
|
||||
<Route path="/web/actors/:id" view=move || view! { <UserPage tl=user_tl /> } />
|
||||
<Route path="/web/objects/:id" view=move || view! { <ObjectPage tl=context_tl /> } />
|
||||
<Route path="/web/actors/:id" view=UserPage />
|
||||
<Route path="/web/objects/:id" view=ObjectPage />
|
||||
// <Route path="/web/activities/:id" view=move || view! { <ActivityPage tl=context_tl /> } />
|
||||
|
||||
<Route path="/web/search" view=SearchPage />
|
||||
|
|
|
@ -5,21 +5,20 @@ use crate::prelude::*;
|
|||
pub fn LoginBox(
|
||||
token_tx: WriteSignal<Option<String>>,
|
||||
userid_tx: WriteSignal<Option<String>>,
|
||||
home_tl: Timeline,
|
||||
server_tl: Timeline,
|
||||
) -> impl IntoView {
|
||||
let auth = use_context::<Auth>().expect("missing auth context");
|
||||
let username_ref: NodeRef<html::Input> = create_node_ref();
|
||||
let password_ref: NodeRef<html::Input> = create_node_ref();
|
||||
let feeds = use_context::<Feeds>().expect("missing feeds context");
|
||||
view! {
|
||||
<div>
|
||||
<div class="w-100" class:hidden=move || !auth.present() >
|
||||
"hi "<a href={move || Uri::web(U::Actor, &auth.username() )} >{move || auth.username() }</a>
|
||||
<input style="float:right" type="submit" value="logout" on:click=move |_| {
|
||||
token_tx.set(None);
|
||||
home_tl.reset(format!("{URL_BASE}/outbox/page"));
|
||||
server_tl.reset(format!("{URL_BASE}/inbox/page"));
|
||||
server_tl.more(auth);
|
||||
feeds.reset();
|
||||
feeds.global.more(auth);
|
||||
feeds.public.more(auth);
|
||||
} />
|
||||
</div>
|
||||
<div class:hidden=move || auth.present() >
|
||||
|
@ -45,11 +44,15 @@ pub fn LoginBox(
|
|||
userid_tx.set(Some(auth_response.user));
|
||||
token_tx.set(Some(auth_response.token));
|
||||
// reset home feed and point it to our user's inbox
|
||||
home_tl.reset(format!("{URL_BASE}/actors/{}/inbox/page", username));
|
||||
home_tl.more(auth);
|
||||
feeds.home.reset(Some(format!("{URL_BASE}/actors/{username}/feed/page")));
|
||||
feeds.home.more(auth);
|
||||
feeds.private.reset(Some(format!("{URL_BASE}/actors/{username}/inbox/page")));
|
||||
feeds.private.more(auth);
|
||||
// reset server feed: there may be more content now that we're authed
|
||||
server_tl.reset(format!("{URL_BASE}/inbox/page"));
|
||||
server_tl.more(auth);
|
||||
feeds.global.reset(Some(format!("{URL_BASE}/feed/page")));
|
||||
feeds.global.more(auth);
|
||||
feeds.server.reset(Some(format!("{URL_BASE}/inbox/page")));
|
||||
feeds.server.more(auth);
|
||||
});
|
||||
} >
|
||||
<table class="w-100 align">
|
||||
|
|
|
@ -40,6 +40,7 @@ pub fn Navigator() -> impl IntoView {
|
|||
<tr><td colspan="2"><a href="/web/home"><input class="w-100" type="submit" class:hidden=move || !auth.present() value="home timeline" /></a></td></tr>
|
||||
<tr><td colspan="2"><a href="/web/server"><input class="w-100" type="submit" value="server timeline" /></a></td></tr>
|
||||
<tr><td colspan="2"><a href="/web/local"><input class="w-100" type="submit" value="local timeline" /></a></td></tr>
|
||||
<tr><td colspan="2"><a href="/web/inbox"><input class="w-100" type="submit" class:hidden=move || !auth.present() value="notifications" /></a></td></tr>
|
||||
<tr>
|
||||
<td class="w-50"><a href="/web/about"><input class="w-100" type="submit" value="about" /></a></td>
|
||||
<td class="w-50"><a href="/web/config"><input class="w-100" type="submit" value="config" /></a></td>
|
||||
|
|
|
@ -30,10 +30,12 @@ impl Timeline {
|
|||
self.feed.get().is_empty()
|
||||
}
|
||||
|
||||
pub fn reset(&self, url: String) {
|
||||
pub fn reset(&self, url: Option<String>) {
|
||||
self.feed.set(vec![]);
|
||||
self.next.set(url);
|
||||
self.over.set(false);
|
||||
if let Some(url) = url {
|
||||
self.next.set(url);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn more(&self, auth: Auth) {
|
||||
|
|
|
@ -7,9 +7,10 @@ use crate::prelude::*;
|
|||
use apb::{Base, Object};
|
||||
|
||||
#[component]
|
||||
pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
||||
pub fn ObjectPage() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let auth = use_context::<Auth>().expect("missing auth context");
|
||||
let feeds = use_context::<Feeds>().expect("missing feeds context");
|
||||
let object = create_local_resource(
|
||||
move || params.get().get("id").cloned().unwrap_or_default(),
|
||||
move |oid| async move {
|
||||
|
@ -36,8 +37,8 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
|||
};
|
||||
if let Ok(ctx) = obj.context().id() {
|
||||
let tl_url = format!("{}/context/page", Uri::api(U::Object, ctx, false));
|
||||
if !tl.next.get_untracked().starts_with(&tl_url) {
|
||||
tl.reset(tl_url);
|
||||
if !feeds.context.next.get_untracked().starts_with(&tl_url) {
|
||||
feeds.context.reset(Some(tl_url));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,10 +51,10 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
|||
objects::view
|
||||
<a
|
||||
class="clean ml-1" href="#"
|
||||
class:hidden=move || tl.is_empty()
|
||||
class:hidden=move || feeds.context.is_empty()
|
||||
on:click=move |_| {
|
||||
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
|
||||
tl.more(auth);
|
||||
feeds.context.reset(Some(feeds.context.next.get().split('?').next().unwrap_or_default().to_string()));
|
||||
feeds.context.more(auth);
|
||||
}><span class="emoji">
|
||||
"\u{1f5d8}"
|
||||
</span></a>
|
||||
|
@ -71,7 +72,7 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
|||
view!{
|
||||
<Object object=object />
|
||||
<div class="ml-1 mr-1 mt-2">
|
||||
<TimelineReplies tl=tl root=o.id().unwrap_or_default().to_string() />
|
||||
<TimelineReplies tl=feeds.context root=o.id().unwrap_or_default().to_string() />
|
||||
</div>
|
||||
}.into_view()
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@ pub fn TimelinePage(name: &'static str, tl: Timeline) -> impl IntoView {
|
|||
<Breadcrumb back=false>
|
||||
{name}
|
||||
<a class="clean ml-1" href="#" on:click=move |_| {
|
||||
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
|
||||
tl.reset(Some(tl.next.get().split('?').next().unwrap_or_default().to_string()));
|
||||
tl.more(auth);
|
||||
}><span class="emoji">
|
||||
"\u{1f5d8}"
|
||||
|
|
|
@ -20,8 +20,9 @@ fn send_follow_request(target: String) {
|
|||
}
|
||||
|
||||
#[component]
|
||||
pub fn UserPage(tl: Timeline) -> impl IntoView {
|
||||
pub fn UserPage() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let feeds = use_context::<Feeds>().expect("missing feeds context");
|
||||
let auth = use_context::<Auth>().expect("missing auth context");
|
||||
let id = params.get()
|
||||
.get("id")
|
||||
|
@ -33,8 +34,8 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
|
|||
move |id| {
|
||||
async move {
|
||||
let tl_url = format!("{}/outbox/page", Uri::api(U::Actor, &id, false));
|
||||
if !tl.next.get_untracked().starts_with(&tl_url) {
|
||||
tl.reset(tl_url);
|
||||
if !feeds.user.next.get_untracked().starts_with(&tl_url) {
|
||||
feeds.user.reset(Some(tl_url));
|
||||
}
|
||||
match CACHE.get(&Uri::full(U::Actor, &id)) {
|
||||
Some(x) => Some(x.clone()),
|
||||
|
@ -54,10 +55,10 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
|
|||
users::view
|
||||
<a
|
||||
class="clean ml-1" href="#"
|
||||
class:hidden=move || tl.is_empty()
|
||||
class:hidden=move || feeds.user.is_empty()
|
||||
on:click=move |_| {
|
||||
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
|
||||
tl.more(auth);
|
||||
feeds.user.reset(Some(feeds.user.next.get().split('?').next().unwrap_or_default().to_string()));
|
||||
feeds.user.more(auth);
|
||||
}><span class="emoji">
|
||||
"\u{1f5d8}"
|
||||
</span></a>
|
||||
|
@ -138,7 +139,7 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
|
|||
<p class="ml-2 mt-1 center" inner_html={mdhtml::safe_html(&summary)}></p>
|
||||
</div>
|
||||
</div>
|
||||
<TimelineFeed tl=tl />
|
||||
<TimelineFeed tl=feeds.user />
|
||||
}.into_view()
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub use crate::{
|
||||
Http, Uri,
|
||||
CACHE, URL_BASE,
|
||||
app::Feeds,
|
||||
auth::Auth,
|
||||
page::*,
|
||||
components::*,
|
||||
|
|
Loading…
Reference in a new issue