1
0
Fork 0
forked from alemi/upub

fix(web): only load tl after refresh

also improved tl.more()
This commit is contained in:
əlemi 2024-05-27 16:50:42 +02:00
parent eb3c647691
commit 6397647511
Signed by: alemi
GPG key ID: A4895B84D311642C
6 changed files with 50 additions and 96 deletions

View file

@ -12,29 +12,6 @@ pub fn App() -> impl IntoView {
let (userid, set_userid) = use_cookie::<String, FromToStringCodec>("user_id"); let (userid, set_userid) = use_cookie::<String, FromToStringCodec>("user_id");
let (config, set_config, _) = use_local_storage::<crate::Config, JsonCodec>("config"); let (config, set_config, _) = use_local_storage::<crate::Config, JsonCodec>("config");
if let Some(tok) = token.get_untracked() {
spawn_local(async move {
match reqwest::Client::new()
.request(Method::PATCH, format!("{URL_BASE}/auth"))
.json(&serde_json::json!({"token": tok}))
.send()
.await
{
Err(e) => tracing::error!("could not refresh token: {e}"),
Ok(res) => match res.error_for_status() {
Err(e) => tracing::error!("server rejected refresh: {e}"),
Ok(doc) => match doc.json::<AuthResponse>().await {
Err(e) => tracing::error!("failed parsing auth response: {e}"),
Ok(auth) => {
set_token.set(Some(auth.token));
set_userid.set(Some(auth.user));
},
}
}
}
})
};
let auth = Auth { token, userid }; let auth = Auth { token, userid };
provide_context(auth); provide_context(auth);
provide_context(config); provide_context(config);
@ -55,26 +32,39 @@ pub fn App() -> impl IntoView {
let (menu, set_menu) = create_signal(screen_width <= 786); let (menu, set_menu) = create_signal(screen_width <= 786);
let (advanced, set_advanced) = create_signal(false); let (advanced, set_advanced) = create_signal(false);
spawn_local(async move {
if let Err(e) = server_tl.more(auth).await {
tracing::error!("error populating timeline: {e}");
}
});
let auth_present = auth.token.get_untracked().is_some(); // skip helper to use get_untracked let auth_present = auth.token.get_untracked().is_some(); // skip helper to use get_untracked
if auth_present { let title_target = move || if auth_present { "/web/home" } else { "/web/server" };
if let Some(tok) = token.get_untracked() {
spawn_local(async move { spawn_local(async move {
if let Err(e) = home_tl.more(auth).await { // refresh token first, or verify that we're still authed
tracing::error!("error populating timeline: {e}"); match reqwest::Client::new()
.request(Method::PATCH, format!("{URL_BASE}/auth"))
.json(&serde_json::json!({"token": tok}))
.send()
.await
{
Err(e) => tracing::error!("could not refresh token: {e}"),
Ok(res) => match res.error_for_status() {
Err(e) => tracing::error!("server rejected refresh: {e}"),
Ok(doc) => match doc.json::<AuthResponse>().await {
Err(e) => tracing::error!("failed parsing auth response: {e}"),
Ok(auth) => {
set_token.set(Some(auth.token));
set_userid.set(Some(auth.user));
},
}
} }
});
} }
let title_target = if auth_present { "/web/home" } else { "/web/server" }; server_tl.more(auth);
if auth_present { home_tl.more(auth) };
})
};
view! { view! {
<nav class="w-100 mt-1 mb-1 pb-s"> <nav class="w-100 mt-1 mb-1 pb-s">
<code class="color ml-3" ><a class="upub-title" href={title_target} >μpub</a></code> <code class="color ml-3" ><a class="upub-title" href=title_target >μpub</a></code>
<small class="ml-1 mr-1 hidden-on-tiny" ><a class="clean" href="/web/server" >micro social network, federated</a></small> <small class="ml-1 mr-1 hidden-on-tiny" ><a class="clean" href="/web/server" >micro social network, federated</a></small>
/* TODO kinda jank with the float but whatever, will do for now */ /* TODO kinda jank with the float but whatever, will do for now */
<input type="submit" class="mr-2 rev" on:click=move |_| set_menu.set(!menu.get()) value="menu" style="float: right" /> <input type="submit" class="mr-2 rev" on:click=move |_| set_menu.set(!menu.get()) value="menu" style="float: right" />

View file

@ -19,11 +19,7 @@ pub fn LoginBox(
token_tx.set(None); token_tx.set(None);
home_tl.reset(format!("{URL_BASE}/outbox/page")); home_tl.reset(format!("{URL_BASE}/outbox/page"));
server_tl.reset(format!("{URL_BASE}/inbox/page")); server_tl.reset(format!("{URL_BASE}/inbox/page"));
spawn_local(async move { server_tl.more(auth);
if let Err(e) = server_tl.more(auth).await {
logging::error!("failed refreshing server timeline: {e}");
}
});
} /> } />
</div> </div>
<div class:hidden=move || auth.present() > <div class:hidden=move || auth.present() >
@ -50,18 +46,10 @@ pub fn LoginBox(
token_tx.set(Some(auth_response.token)); token_tx.set(Some(auth_response.token));
// reset home feed and point it to our user's inbox // reset home feed and point it to our user's inbox
home_tl.reset(format!("{URL_BASE}/users/{}/inbox/page", username)); home_tl.reset(format!("{URL_BASE}/users/{}/inbox/page", username));
spawn_local(async move { home_tl.more(auth);
if let Err(e) = home_tl.more(auth).await {
tracing::error!("failed refreshing home timeline: {e}");
}
});
// reset server feed: there may be more content now that we're authed // reset server feed: there may be more content now that we're authed
server_tl.reset(format!("{URL_BASE}/inbox/page")); server_tl.reset(format!("{URL_BASE}/inbox/page"));
spawn_local(async move { server_tl.more(auth);
if let Err(e) = server_tl.more(auth).await {
tracing::error!("failed refreshing server timeline: {e}");
}
});
}); });
} > } >
<table class="w-100 align"> <table class="w-100 align">

View file

@ -36,14 +36,20 @@ impl Timeline {
self.over.set(false); self.over.set(false);
} }
pub async fn more(&self, auth: Auth) -> reqwest::Result<()> { pub fn more(&self, auth: Auth) {
self.loading.set(true); if self.loading.get() { return }
let res = self.more_inner(auth).await; let _self = *self;
self.loading.set(false); spawn_local(async move {
res _self.loading.set(true);
let res = _self.load_more(auth).await;
_self.loading.set(false);
if let Err(e) = res {
tracing::error!("failed loading posts for timeline: {e}");
}
});
} }
async fn more_inner(&self, auth: Auth) -> reqwest::Result<()> { pub async fn load_more(&self, auth: Auth) -> reqwest::Result<()> {
use apb::{Collection, CollectionPage}; use apb::{Collection, CollectionPage};
let feed_url = self.next.get_untracked(); let feed_url = self.next.get_untracked();
@ -119,13 +125,7 @@ pub fn TimelineReplies(tl: Timeline, root: String) -> impl IntoView {
<div class="center mt-1 mb-1" class:hidden=tl.over > <div class="center mt-1 mb-1" class:hidden=tl.over >
<button type="button" <button type="button"
prop:disabled=tl.loading prop:disabled=tl.loading
on:click=move |_| { on:click=move |_| tl.more(auth)
spawn_local(async move {
if let Err(e) = tl.more(auth).await {
tracing::error!("error fetching more items for timeline: {e}");
}
})
}
> >
{move || if tl.loading.get() { {move || if tl.loading.get() {
view! { "loading"<span class="dots"></span> }.into_view() view! { "loading"<span class="dots"></span> }.into_view()
@ -152,9 +152,7 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView {
move |(s, h)| async move { move |(s, h)| async move {
if !config.get().infinite_scroll { return } if !config.get().infinite_scroll { return }
if s > 0.0 && h - s < view_height && !tl.loading.get() { if s > 0.0 && h - s < view_height && !tl.loading.get() {
if let Err(e) = tl.more(auth).await { tl.more(auth);
tracing::error!("auto load failed: {e}");
}
} }
}, },
); );
@ -179,7 +177,7 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView {
<div class="center mt-1 mb-1" class:hidden=tl.over > <div class="center mt-1 mb-1" class:hidden=tl.over >
<button type="button" <button type="button"
prop:disabled=tl.loading prop:disabled=tl.loading
on:click=move |_| load_more(tl, auth) on:click=move |_| tl.more(auth)
> >
{move || if tl.loading.get() { {move || if tl.loading.get() {
view! { "loading"<span class="dots"></span> }.into_view() view! { "loading"<span class="dots"></span> }.into_view()
@ -189,16 +187,6 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView {
} }
} }
fn load_more(tl: Timeline, auth: Auth) {
if !tl.loading.get() {
spawn_local(async move {
if let Err(e) = tl.more(auth).await {
tracing::error!("error fetching more items for timeline: {e}");
}
})
}
}
async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> Vec<String> { async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> Vec<String> {
let mut sub_tasks : Vec<Pin<Box<dyn futures::Future<Output = ()>>>> = Vec::new(); let mut sub_tasks : Vec<Pin<Box<dyn futures::Future<Output = ()>>>> = Vec::new();
let mut gonna_fetch = BTreeSet::new(); let mut gonna_fetch = BTreeSet::new();

View file

@ -41,11 +41,7 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
class:hidden=move || tl.is_empty() class:hidden=move || tl.is_empty()
on:click=move |_| { on:click=move |_| {
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string()); tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
spawn_local(async move { tl.more(auth);
if let Err(e) = tl.more(auth).await {
tracing::error!("error fetching more items for timeline: {e}");
}
})
}><span class="emoji"> }><span class="emoji">
"\u{1f5d8}" "\u{1f5d8}"
</span></a> </span></a>

View file

@ -10,11 +10,7 @@ pub fn TimelinePage(name: &'static str, tl: Timeline) -> impl IntoView {
{name} {name}
<a class="clean ml-1" href="#" on:click=move |_| { <a class="clean ml-1" href="#" on:click=move |_| {
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string()); tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
spawn_local(async move { tl.more(auth);
if let Err(e) = tl.more(auth).await {
tracing::error!("error fetching more items for timeline: {e}");
}
})
}><span class="emoji"> }><span class="emoji">
"\u{1f5d8}" "\u{1f5d8}"
</span></a> </span></a>

View file

@ -51,11 +51,7 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
class:hidden=move || tl.is_empty() class:hidden=move || tl.is_empty()
on:click=move |_| { on:click=move |_| {
tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string()); tl.reset(tl.next.get().split('?').next().unwrap_or_default().to_string());
spawn_local(async move { tl.more(auth);
if let Err(e) = tl.more(auth).await {
tracing::error!("error fetching more items for timeline: {e}");
}
})
}><span class="emoji"> }><span class="emoji">
"\u{1f5d8}" "\u{1f5d8}"
</span></a> </span></a>