commit 9d98f2c9e2e9f46ca44b15f74d60d4a61bb7932d
parent 3297ad988c2c9c1505dcc3ef746a61563ec7f5e6
Author: William Casarin <jb55@jb55.com>
Date: Sun, 25 Jan 2026 15:13:52 -0800
toolbar: go to route root when clicking active toolbar button
When clicking a toolbar button for a view that's already selected,
pop all routes in that column back to the base route and scroll to top.
This provides a quick way to return to the top level of any section.
The implementation properly cleans up resources (timelines, threads,
profile state) for each popped route, following the same pattern used
in nav.rs.
Fixes: https://github.com/damus-io/notedeck/issues/980
Tested-by: William Casarin <jb55@jb55.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat:
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/crates/notedeck_columns/src/toolbar.rs b/crates/notedeck_columns/src/toolbar.rs
@@ -1,3 +1,4 @@
+use egui_nav::ReturnType;
use notedeck::AppContext;
use crate::{
@@ -55,8 +56,14 @@ impl ToolbarAction {
return;
};
- match cols.select_by_route(route) {
- crate::column::SelectionResult::AlreadySelected(_) => {} // great! no need to go to top yet
+ let selection_result = cols.select_by_route(route);
+
+ match selection_result {
+ crate::column::SelectionResult::AlreadySelected(col_index) => {
+ // We're already on this toolbar view, so pop all routes to go to top
+ go_to_top_of_column(app, ctx, col_index);
+ app.scroll_to_top();
+ }
crate::column::SelectionResult::NewSelection(_) => {
// we already selected this, so scroll to top
app.scroll_to_top();
@@ -68,3 +75,46 @@ impl ToolbarAction {
}
}
}
+
+/// Pop all routes in the column until we're back at depth 1 (the base route).
+/// This is used when clicking a toolbar button for a view we're already on
+/// to immediately return to the top level regardless of navigation depth.
+fn go_to_top_of_column(app: &mut Damus, ctx: &mut AppContext, col_index: usize) {
+ let Some(cols) = app.decks_cache.active_columns_mut(ctx.i18n, ctx.accounts) else {
+ return;
+ };
+
+ let column = cols.column_mut(col_index);
+
+ // Close any open sheets first
+ if column.sheet_router.route().is_some() {
+ column.sheet_router.go_back();
+ }
+
+ // Pop all routes except the base route
+ while column.router().routes().len() > 1 {
+ if let Some(popped) = column.router_mut().pop() {
+ // TODO(jb55): centralize this resource cleanup logic into a shared
+ // pop_and_cleanup function. This same pattern exists in nav.rs.
+ // Clean up resources for popped route
+ match popped {
+ Route::Timeline(timeline_kind) => {
+ if let Err(err) = app.timeline_cache.pop(&timeline_kind, ctx.ndb, ctx.pool) {
+ tracing::error!(
+ "popping timeline had an error: {err} for {:?}",
+ timeline_kind
+ );
+ }
+ }
+ Route::Thread(selection) => {
+ app.threads
+ .close(ctx.ndb, ctx.pool, &selection, ReturnType::Click, col_index);
+ }
+ Route::EditProfile(pk) => {
+ app.view_state.pubkey_to_profile_state.remove(&pk);
+ }
+ _ => {}
+ }
+ }
+ }
+}