From 3c48076996b2351f59fac373236925f8f9034c50 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Wed, 26 Nov 2025 13:46:19 -0800 Subject: [PATCH] web: add spinner when loading next page in catchup mode --- web/src/state.rs | 26 +++++++++++++++++++++----- web/src/view/mod.rs | 17 ++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/web/src/state.rs b/web/src/state.rs index 202e432..efd2af3 100644 --- a/web/src/state.rs +++ b/web/src/state.rs @@ -291,6 +291,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { first, last, } => { + model.refreshing_state = RefreshingState::Loading; let (after, before, first, last) = match (after.as_ref(), before.as_ref(), first, last) { // If no pagination set, set reasonable defaults @@ -316,25 +317,32 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { }); } Msg::FrontPageResult(Err(e)) => { - error!("error FrontPageResult: {e:?}"); + let msg = format!("error FrontPageResult: {e:?}"); + error!("{msg}"); + model.refreshing_state = RefreshingState::Error(msg); } Msg::FrontPageResult(Ok(graphql_client::Response { data: None, errors: None, .. })) => { - error!("FrontPageResult no data or errors, should not happen"); + let msg = format!("FrontPageResult no data or errors, should not happen"); + error!("{msg}"); + model.refreshing_state = RefreshingState::Error(msg); } Msg::FrontPageResult(Ok(graphql_client::Response { data: None, errors: Some(e), .. })) => { - error!("FrontPageResult error: {e:?}"); + let msg = format!("FrontPageResult error: {e:?}"); + error!("{msg}"); + model.refreshing_state = RefreshingState::Error(msg); } Msg::FrontPageResult(Ok(graphql_client::Response { data: Some(data), .. })) => { + model.refreshing_state = RefreshingState::None; model.tags = Some( data.tags .into_iter() @@ -374,6 +382,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { } Msg::ShowThreadRequest { thread_id } => { + model.refreshing_state = RefreshingState::Loading; orders.skip().perform_cmd(async move { Msg::ShowThreadResult( send_graphql(graphql::ShowThreadQuery::build_query( @@ -386,6 +395,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { Msg::ShowThreadResult(Ok(graphql_client::Response { data: Some(data), .. })) => { + model.refreshing_state = RefreshingState::None; model.tags = Some( data.tags .into_iter() @@ -425,9 +435,12 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { orders.send_msg(Msg::WindowScrolled); } Msg::ShowThreadResult(bad) => { - error!("show_thread_query error: {bad:#?}"); + let msg = format!("show_thread_query error: {bad:#?}"); + error!("{msg}"); + model.refreshing_state = RefreshingState::Error(msg); } Msg::CatchupRequest { query } => { + model.refreshing_state = RefreshingState::Loading; orders.perform_cmd(async move { Msg::CatchupResult( send_graphql::<_, graphql::catchup_query::ResponseData>( @@ -442,6 +455,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { Msg::CatchupResult(Ok(graphql_client::Response { data: Some(data), .. })) => { + model.refreshing_state = RefreshingState::None; let items = data.catchup; if items.is_empty() { orders.send_msg(Msg::GoToSearchResults); @@ -457,7 +471,9 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { } } Msg::CatchupResult(bad) => { - error!("catchup_query error: {bad:#?}"); + let msg = format!("catchup_query error: {bad:#?}"); + error!("{msg}"); + model.refreshing_state = RefreshingState::Error(msg); } Msg::SelectionSetNone => { if let Context::SearchResult { diff --git a/web/src/view/mod.rs b/web/src/view/mod.rs index 804da78..6c665e3 100644 --- a/web/src/view/mod.rs +++ b/web/src/view/mod.rs @@ -78,6 +78,10 @@ mod tw_classes { } pub fn view(model: &Model) -> Node { + let is_loading = match model.refreshing_state { + RefreshingState::Loading => true, + _ => false, + }; match &model.context { Context::None => normal_view( div![h1!["Loading"]], @@ -96,6 +100,7 @@ pub fn view(model: &Model) -> Node { thread(thread_data, open_messages, &model.content_el, true), &catchup.items, model.read_completion_ratio, + is_loading, ) } else { normal_view( @@ -117,6 +122,7 @@ pub fn view(model: &Model) -> Node { news_post(post, &model.content_el, true), &catchup.items, model.read_completion_ratio, + is_loading, ) } else { normal_view( @@ -186,6 +192,7 @@ fn catchup_view( content: Node, items: &[CatchupItem], read_completion_ratio: f64, + is_loading: bool, ) -> Node { div![ C!["w-full", "relative", "text-white"], @@ -201,13 +208,17 @@ fn catchup_view( "bg-black/50", ], div![ - C!["absolute", "top-0", "right-4", "text-gray-500", "p-4"], - span![i![C!["fas", "fa-x"]]], - ev(Ev::Click, move |_| Msg::CatchupExit) + C!["absolute", "top-0", "left-4", "text-green-200", "p-4"], + IF!(is_loading=>span![i![C!["animate-spin", "fas", "fa-spinner"]]]) ], h1![ C!["text-center"], format!("{} left ", items.iter().filter(|i| !i.seen).count(),) + ], + div![ + C!["absolute", "top-0", "right-4", "text-gray-500", "p-4"], + span![i![C!["fas", "fa-x"]]], + ev(Ev::Click, move |_| Msg::CatchupExit) ] ], div![C!["mt-12", "mb-20"], content],