notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit d85c6043b700783d825f6e5971f976e198fde112
parent 8e0e42a1f343f1c3cf87292e6d5d18bd3eea7dda
Author: William Casarin <jb55@jb55.com>
Date:   Fri,  7 Mar 2025 16:01:55 -0800

search: auto-focus search field on navigate

I'm going to add a search changelog on this commit since I forgot
to do so previously.

Fixes: https://linear.app/damus/issue/DECK-538/auto-focus-search-field-on-search-view
Changelog-Added: Added fulltext search ui
Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mcrates/notedeck_columns/src/nav.rs | 16+++++++++++++++-
Mcrates/notedeck_columns/src/route.rs | 1-
Mcrates/notedeck_columns/src/ui/search/mod.rs | 11++++++++---
Mcrates/notedeck_columns/src/ui/search/state.rs | 18++++++++++++++++++
Mcrates/notedeck_columns/src/ui/side_panel.rs | 1-
5 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs @@ -18,7 +18,7 @@ use crate::{ edit_deck::{EditDeckResponse, EditDeckView}, note::{PostAction, PostType}, profile::EditProfileView, - search::SearchView, + search::{FocusState, SearchView}, support::SupportView, RelayView, View, }, @@ -403,9 +403,23 @@ fn render_nav_body( Route::Search => { let id = ui.id().with(("search", depth, col)); + let navigating = app + .columns_mut(ctx.accounts) + .column(col) + .router() + .navigating; let search_buffer = app.view_state.searches.entry(id).or_default(); let txn = Transaction::new(ctx.ndb).expect("txn"); + if navigating { + search_buffer.focus_state = FocusState::Navigating + } else if search_buffer.focus_state == FocusState::Navigating { + // we're not navigating but our last search buffer state + // says we were navigating. This means that navigating has + // stopped. Let's make sure to focus the input field + search_buffer.focus_state = FocusState::ShouldRequestFocus; + } + SearchView::new( ctx.ndb, &txn, diff --git a/crates/notedeck_columns/src/route.rs b/crates/notedeck_columns/src/route.rs @@ -25,7 +25,6 @@ pub enum Route { EditProfile(Pubkey), Support, NewDeck, - /// Search screen Search, EditDeck(usize), } diff --git a/crates/notedeck_columns/src/ui/search/mod.rs b/crates/notedeck_columns/src/ui/search/mod.rs @@ -12,7 +12,7 @@ use tracing::{error, info, warn}; mod state; -pub use state::{SearchQueryState, SearchState}; +pub use state::{FocusState, SearchQueryState, SearchState}; pub struct SearchView<'a> { query: &'a mut SearchQueryState, @@ -57,7 +57,7 @@ impl<'a> SearchView<'a> { } match self.query.state { - SearchState::New => None, + SearchState::New | SearchState::Navigating => None, SearchState::Searched | SearchState::Typing => { if self.query.state == SearchState::Typing { @@ -163,7 +163,7 @@ fn search_box(query: &mut SearchQueryState, ui: &mut egui::Ui) -> bool { // Search input field //let font_size = notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Body); - ui.add_sized( + let response = ui.add_sized( [ui.available_width(), search_height], TextEdit::singleline(&mut query.string) .hint_text(RichText::new("Search notes...").weak()) @@ -173,6 +173,11 @@ fn search_box(query: &mut SearchQueryState, ui: &mut egui::Ui) -> bool { .frame(false), ); + if query.focus_state == FocusState::ShouldRequestFocus { + response.request_focus(); + query.focus_state = FocusState::RequestedFocus; + } + let after_len = query.string.len(); let changed = before_len != after_len; diff --git a/crates/notedeck_columns/src/ui/search/state.rs b/crates/notedeck_columns/src/ui/search/state.rs @@ -6,9 +6,22 @@ use std::time::Duration; pub enum SearchState { Typing, Searched, + Navigating, New, } +#[derive(Debug, Eq, PartialEq)] +pub enum FocusState { + /// Get ready to focus + Navigating, + + /// We should request focus when we stop navigating + ShouldRequestFocus, + + /// We already focused, we don't need to do that again + RequestedFocus, +} + /// Search query state that exists between frames #[derive(Debug)] pub struct SearchQueryState { @@ -20,6 +33,10 @@ pub struct SearchQueryState { /// again next frames pub state: SearchState, + /// A bit of context to know if we're navigating to the view. We + /// can use this to know when to request focus on the textedit + pub focus_state: FocusState, + /// When was the input updated? We use this to debounce searches pub debouncer: Debouncer, @@ -39,6 +56,7 @@ impl SearchQueryState { string: "".to_string(), state: SearchState::New, notes: TimelineTab::default(), + focus_state: FocusState::Navigating, debouncer: Debouncer::new(Duration::from_millis(200)), } } diff --git a/crates/notedeck_columns/src/ui/side_panel.rs b/crates/notedeck_columns/src/ui/side_panel.rs @@ -324,7 +324,6 @@ impl<'a> DesktopSidePanel<'a> { } SidePanelAction::Search => { // TODO - info!("Clicked search button"); if router.top() == &Route::Search { router.go_back(); } else {