From 3834fd3d0f77a809cf3e394fca31fd036d3173dd Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Sun, 13 Sep 2026 21:14:00 +0400 Subject: fix: auto-expire worktree action error banners (#4063) * fix: auto-expire worktree action error banners refs #1827 * fix: restart endpoint error timeout on repeated errors Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline. refs #1827 * test: derive endpoint error expiry assertions from stored deadline refs #1827 --- src/client/shell/state.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src/client/shell/state.rs') diff --git a/src/client/shell/state.rs b/src/client/shell/state.rs index e2e406a2..eede0f29 100644 --- a/src/client/shell/state.rs +++ b/src/client/shell/state.rs @@ -3,6 +3,7 @@ use super::*; pub(super) const MIN_TAB_WIDTH: u16 = 8; pub(super) const NEW_TAB_WIDTH: u16 = 3; pub(super) const WORKSPACE_HEADER_ROWS: u16 = 2; +const ENDPOINT_ERROR_TIMEOUT_SECS: u64 = 5; fn pane_surface_row<'a>( surface: &'a PaneSurfaceFrame, @@ -974,6 +975,7 @@ pub(crate) struct ClientShellState { pub(super) local_config_diagnostic: Option, pub(super) config_diagnostic: Option, pub(super) endpoint_error: Option, + pub(super) endpoint_error_deadline: Option, pub(super) dismissed_product_announcement: Option<(String, String)>, } @@ -1132,6 +1134,7 @@ impl ClientShellState { config_diagnostic: local_config_diagnostic.clone(), local_config_diagnostic, endpoint_error: None, + endpoint_error_deadline: None, dismissed_product_announcement: None, } } @@ -1273,6 +1276,7 @@ impl ClientShellState { self.endpoint_notice_seen.clear(); self.visible_endpoint_notice = None; self.endpoint_error = None; + self.endpoint_error_deadline = None; self.navigate_workspace_id = None; self.overlay = self .config @@ -1389,7 +1393,7 @@ impl ClientShellState { snapshot.server_keybindings_toml.as_deref(), &snapshot.commands, ) { - self.endpoint_error = Some(err); + self.set_endpoint_error(err); } else if active_keymap_changed && matches!( self.mode, @@ -1693,6 +1697,7 @@ impl ClientShellState { } self.hits.popup = None; self.endpoint_error = None; + self.endpoint_error_deadline = None; } if next_popup.is_some() { self.popup_pending = false; @@ -1852,6 +1857,33 @@ impl ClientShellState { repaint } + /// Show a transient client-side action error, restarting its lifetime. + /// + /// Every assignment must go through this setter so a repeated identical + /// message gets a fresh deadline instead of inheriting the previous one. + pub(super) fn set_endpoint_error(&mut self, message: impl Into) { + self.endpoint_error = Some(message.into()); + self.endpoint_error_deadline = Some( + std::time::Instant::now() + std::time::Duration::from_secs(ENDPOINT_ERROR_TIMEOUT_SECS), + ); + } + + pub(crate) fn tick_endpoint_error(&mut self, now: std::time::Instant) -> bool { + if self.endpoint_error.is_none() { + self.endpoint_error_deadline = None; + return false; + } + if self + .endpoint_error_deadline + .is_some_and(|deadline| now >= deadline) + { + self.endpoint_error = None; + self.endpoint_error_deadline = None; + return true; + } + false + } + pub(crate) fn timer_delay(&self, now: std::time::Instant) -> std::time::Duration { let default = std::time::Duration::from_millis(100); self.selection_autoscroll_deadline -- cgit v1.2.3