From ef8d5b73ee5b9e03345227fe597c9771cf79eb6f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 19 Dec 2024 07:31:40 -0800 Subject: [PATCH 1/4] columns: remove dead code --- crates/notedeck_columns/src/app.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs index 7456f206..cef57f99 100644 --- a/crates/notedeck_columns/src/app.rs +++ b/crates/notedeck_columns/src/app.rs @@ -57,7 +57,7 @@ pub struct Damus { pub textmode: bool, } -fn handle_key_events(input: &egui::InputState, _pixels_per_point: f32, columns: &mut Columns) { +fn handle_key_events(input: &egui::InputState, columns: &mut Columns) { for event in &input.raw.events { if let egui::Event::Key { key, pressed: true, .. @@ -87,9 +87,8 @@ fn try_process_event( app_ctx: &mut AppContext<'_>, ctx: &egui::Context, ) -> Result<()> { - let ppp = ctx.pixels_per_point(); let current_columns = get_active_columns_mut(app_ctx.accounts, &mut damus.decks_cache); - ctx.input(|i| handle_key_events(i, ppp, current_columns)); + ctx.input(|i| handle_key_events(i, current_columns)); let ctx2 = ctx.clone(); let wakeup = move || { From 5449d6ceb51e46d2e1b5f6a6933f71e4d0ada71a Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 19 Dec 2024 07:49:00 -0800 Subject: [PATCH 2/4] note: options: streamline bit macro Include has method in the bit note options macro --- .../notedeck_columns/src/ui/note/options.rs | 72 +++++-------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/crates/notedeck_columns/src/ui/note/options.rs b/crates/notedeck_columns/src/ui/note/options.rs index 1090d154..a369ca2c 100644 --- a/crates/notedeck_columns/src/ui/note/options.rs +++ b/crates/notedeck_columns/src/ui/note/options.rs @@ -24,8 +24,8 @@ impl Default for NoteOptions { } } -macro_rules! create_setter { - ($fn_name:ident, $option:ident) => { +macro_rules! create_bit_methods { + ($fn_name:ident, $has_name:ident, $option:ident) => { #[inline] pub fn $fn_name(&mut self, enable: bool) { if enable { @@ -34,19 +34,24 @@ macro_rules! create_setter { *self &= !NoteOptions::$option; } } + + #[inline] + pub fn $has_name(self) -> bool { + (self & NoteOptions::$option) == NoteOptions::$option + } }; } impl NoteOptions { - create_setter!(set_small_pfp, small_pfp); - create_setter!(set_medium_pfp, medium_pfp); - create_setter!(set_note_previews, note_previews); - create_setter!(set_selectable_text, selectable_text); - create_setter!(set_textmode, textmode); - create_setter!(set_actionbar, actionbar); - create_setter!(set_wide, wide); - create_setter!(set_options_button, options_button); - create_setter!(set_hide_media, hide_media); + create_bit_methods!(set_small_pfp, has_small_pfp, small_pfp); + create_bit_methods!(set_medium_pfp, has_medium_pfp, medium_pfp); + create_bit_methods!(set_note_previews, has_note_previews, note_previews); + create_bit_methods!(set_selectable_text, has_selectable_text, selectable_text); + create_bit_methods!(set_textmode, has_textmode, textmode); + create_bit_methods!(set_actionbar, has_actionbar, actionbar); + create_bit_methods!(set_wide, has_wide, wide); + create_bit_methods!(set_options_button, has_options_button, options_button); + create_bit_methods!(set_hide_media, has_hide_media, hide_media); pub fn new(is_universe_timeline: bool) -> Self { let mut options = NoteOptions::default(); @@ -54,51 +59,6 @@ impl NoteOptions { options } - #[inline] - pub fn has_actionbar(self) -> bool { - (self & NoteOptions::actionbar) == NoteOptions::actionbar - } - - #[inline] - pub fn has_hide_media(self) -> bool { - (self & NoteOptions::hide_media) == NoteOptions::hide_media - } - - #[inline] - pub fn has_selectable_text(self) -> bool { - (self & NoteOptions::selectable_text) == NoteOptions::selectable_text - } - - #[inline] - pub fn has_textmode(self) -> bool { - (self & NoteOptions::textmode) == NoteOptions::textmode - } - - #[inline] - pub fn has_note_previews(self) -> bool { - (self & NoteOptions::note_previews) == NoteOptions::note_previews - } - - #[inline] - pub fn has_small_pfp(self) -> bool { - (self & NoteOptions::small_pfp) == NoteOptions::small_pfp - } - - #[inline] - pub fn has_medium_pfp(self) -> bool { - (self & NoteOptions::medium_pfp) == NoteOptions::medium_pfp - } - - #[inline] - pub fn has_wide(self) -> bool { - (self & NoteOptions::wide) == NoteOptions::wide - } - - #[inline] - pub fn has_options_button(self) -> bool { - (self & NoteOptions::options_button) == NoteOptions::options_button - } - pub fn pfp_size(&self) -> f32 { if self.has_small_pfp() { ProfilePic::small_size() From cb2330abac38a8c589754a01e6c919d1fc185dbe Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 19 Dec 2024 07:58:43 -0800 Subject: [PATCH 3/4] refactor: move reply_desc into its own file it's grown up enough now to deserve that at least --- crates/notedeck_columns/src/ui/note/mod.rs | 157 +----------------- .../src/ui/note/reply_description.rs | 157 ++++++++++++++++++ 2 files changed, 160 insertions(+), 154 deletions(-) create mode 100644 crates/notedeck_columns/src/ui/note/reply_description.rs diff --git a/crates/notedeck_columns/src/ui/note/mod.rs b/crates/notedeck_columns/src/ui/note/mod.rs index 70c55442..e26e12a7 100644 --- a/crates/notedeck_columns/src/ui/note/mod.rs +++ b/crates/notedeck_columns/src/ui/note/mod.rs @@ -4,6 +4,7 @@ pub mod options; pub mod post; pub mod quote_repost; pub mod reply; +pub mod reply_description; pub use contents::NoteContents; pub use context::{NoteContextButton, NoteContextSelection}; @@ -11,6 +12,7 @@ pub use options::NoteOptions; pub use post::{PostAction, PostResponse, PostType, PostView}; pub use quote_repost::QuoteRepostView; pub use reply::PostReplyView; +pub use reply_description::reply_desc; use crate::{ actionbar::NoteAction, @@ -20,7 +22,7 @@ use crate::{ use egui::emath::{pos2, Vec2}; use egui::{Id, Label, Pos2, Rect, Response, RichText, Sense}; use enostr::{NoteId, Pubkey}; -use nostrdb::{Ndb, Note, NoteKey, NoteReply, Transaction}; +use nostrdb::{Ndb, Note, NoteKey, Transaction}; use notedeck::{CachedNote, ImageCache, NoteCache, NotedeckTextStyle}; use super::profile::preview::{get_display_name, one_line_display_name_widget}; @@ -66,159 +68,6 @@ impl View for NoteView<'_> { } } -#[must_use = "Please handle the resulting note action"] -fn reply_desc( - ui: &mut egui::Ui, - txn: &Transaction, - note_reply: &NoteReply, - ndb: &Ndb, - img_cache: &mut ImageCache, - note_cache: &mut NoteCache, -) -> Option { - #[cfg(feature = "profiling")] - puffin::profile_function!(); - - let mut note_action: Option = None; - let size = 10.0; - let selectable = false; - let visuals = ui.visuals(); - let color = visuals.noninteractive().fg_stroke.color; - let link_color = visuals.hyperlink_color; - - // note link renderer helper - let note_link = |ui: &mut egui::Ui, - note_cache: &mut NoteCache, - img_cache: &mut ImageCache, - text: &str, - note: &Note<'_>| { - let r = ui.add( - Label::new(RichText::new(text).size(size).color(link_color)) - .sense(Sense::click()) - .selectable(selectable), - ); - - if r.clicked() { - // TODO: jump to note - } - - if r.hovered() { - r.on_hover_ui_at_pointer(|ui| { - ui.set_max_width(400.0); - ui::NoteView::new(ndb, note_cache, img_cache, note) - .actionbar(false) - .wide(true) - .show(ui); - }); - } - }; - - ui.add(Label::new(RichText::new("replying to").size(size).color(color)).selectable(selectable)); - - let reply = note_reply.reply()?; - - let reply_note = if let Ok(reply_note) = ndb.get_note_by_id(txn, reply.id) { - reply_note - } else { - ui.add(Label::new(RichText::new("a note").size(size).color(color)).selectable(selectable)); - return None; - }; - - if note_reply.is_reply_to_root() { - // We're replying to the root, let's show this - let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) - .size(size) - .selectable(selectable) - .show(ui) - .inner; - - if action.is_some() { - note_action = action; - } - - ui.add(Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable)); - - note_link(ui, note_cache, img_cache, "thread", &reply_note); - } else if let Some(root) = note_reply.root() { - // replying to another post in a thread, not the root - - if let Ok(root_note) = ndb.get_note_by_id(txn, root.id) { - if root_note.pubkey() == reply_note.pubkey() { - // simply "replying to bob's note" when replying to bob in his thread - let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) - .size(size) - .selectable(selectable) - .show(ui) - .inner; - - if action.is_some() { - note_action = action; - } - - ui.add( - Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), - ); - - note_link(ui, note_cache, img_cache, "note", &reply_note); - } else { - // replying to bob in alice's thread - - let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) - .size(size) - .selectable(selectable) - .show(ui) - .inner; - - if action.is_some() { - note_action = action; - } - - ui.add( - Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), - ); - - note_link(ui, note_cache, img_cache, "note", &reply_note); - - ui.add( - Label::new(RichText::new("in").size(size).color(color)).selectable(selectable), - ); - - let action = ui::Mention::new(ndb, img_cache, txn, root_note.pubkey()) - .size(size) - .selectable(selectable) - .show(ui) - .inner; - - if action.is_some() { - note_action = action; - } - - ui.add( - Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), - ); - - note_link(ui, note_cache, img_cache, "thread", &root_note); - } - } else { - let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) - .size(size) - .selectable(selectable) - .show(ui) - .inner; - - if action.is_some() { - note_action = action; - } - - ui.add( - Label::new(RichText::new("in someone's thread").size(size).color(color)) - .selectable(selectable), - ); - } - } - - note_action -} - impl<'a> NoteView<'a> { pub fn new( ndb: &'a Ndb, diff --git a/crates/notedeck_columns/src/ui/note/reply_description.rs b/crates/notedeck_columns/src/ui/note/reply_description.rs new file mode 100644 index 00000000..a5f1c309 --- /dev/null +++ b/crates/notedeck_columns/src/ui/note/reply_description.rs @@ -0,0 +1,157 @@ +use crate::{actionbar::NoteAction, ui}; +use egui::{Label, RichText, Sense}; +use nostrdb::{Ndb, Note, NoteReply, Transaction}; +use notedeck::{ImageCache, NoteCache}; + +#[must_use = "Please handle the resulting note action"] +pub fn reply_desc( + ui: &mut egui::Ui, + txn: &Transaction, + note_reply: &NoteReply, + ndb: &Ndb, + img_cache: &mut ImageCache, + note_cache: &mut NoteCache, +) -> Option { + #[cfg(feature = "profiling")] + puffin::profile_function!(); + + let mut note_action: Option = None; + let size = 10.0; + let selectable = false; + let visuals = ui.visuals(); + let color = visuals.noninteractive().fg_stroke.color; + let link_color = visuals.hyperlink_color; + + // note link renderer helper + let note_link = |ui: &mut egui::Ui, + note_cache: &mut NoteCache, + img_cache: &mut ImageCache, + text: &str, + note: &Note<'_>| { + let r = ui.add( + Label::new(RichText::new(text).size(size).color(link_color)) + .sense(Sense::click()) + .selectable(selectable), + ); + + if r.clicked() { + // TODO: jump to note + } + + if r.hovered() { + r.on_hover_ui_at_pointer(|ui| { + ui.set_max_width(400.0); + ui::NoteView::new(ndb, note_cache, img_cache, note) + .actionbar(false) + .wide(true) + .show(ui); + }); + } + }; + + ui.add(Label::new(RichText::new("replying to").size(size).color(color)).selectable(selectable)); + + let reply = note_reply.reply()?; + + let reply_note = if let Ok(reply_note) = ndb.get_note_by_id(txn, reply.id) { + reply_note + } else { + ui.add(Label::new(RichText::new("a note").size(size).color(color)).selectable(selectable)); + return None; + }; + + if note_reply.is_reply_to_root() { + // We're replying to the root, let's show this + let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) + .size(size) + .selectable(selectable) + .show(ui) + .inner; + + if action.is_some() { + note_action = action; + } + + ui.add(Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable)); + + note_link(ui, note_cache, img_cache, "thread", &reply_note); + } else if let Some(root) = note_reply.root() { + // replying to another post in a thread, not the root + + if let Ok(root_note) = ndb.get_note_by_id(txn, root.id) { + if root_note.pubkey() == reply_note.pubkey() { + // simply "replying to bob's note" when replying to bob in his thread + let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) + .size(size) + .selectable(selectable) + .show(ui) + .inner; + + if action.is_some() { + note_action = action; + } + + ui.add( + Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), + ); + + note_link(ui, note_cache, img_cache, "note", &reply_note); + } else { + // replying to bob in alice's thread + + let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) + .size(size) + .selectable(selectable) + .show(ui) + .inner; + + if action.is_some() { + note_action = action; + } + + ui.add( + Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), + ); + + note_link(ui, note_cache, img_cache, "note", &reply_note); + + ui.add( + Label::new(RichText::new("in").size(size).color(color)).selectable(selectable), + ); + + let action = ui::Mention::new(ndb, img_cache, txn, root_note.pubkey()) + .size(size) + .selectable(selectable) + .show(ui) + .inner; + + if action.is_some() { + note_action = action; + } + + ui.add( + Label::new(RichText::new("'s").size(size).color(color)).selectable(selectable), + ); + + note_link(ui, note_cache, img_cache, "thread", &root_note); + } + } else { + let action = ui::Mention::new(ndb, img_cache, txn, reply_note.pubkey()) + .size(size) + .selectable(selectable) + .show(ui) + .inner; + + if action.is_some() { + note_action = action; + } + + ui.add( + Label::new(RichText::new("in someone's thread").size(size).color(color)) + .selectable(selectable), + ); + } + } + + note_action +} From 8025be823a8fc7099bb47096271dd7377f19cf28 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 19 Dec 2024 08:48:07 -0800 Subject: [PATCH 4/4] ui: customizable tabs per column view This reduces the number of choices the user needs to make. Some of these filters were redundant anyways. This also saves memory. Universe: Notes Notificaitons: Notes & Replies Everything else: Notes, Notes & Replies Changelog-Changed: Simplified tab selections on some columns Fixes: https://github.com/damus-io/notedeck/issues/517 --- crates/notedeck_columns/src/args.rs | 3 +- crates/notedeck_columns/src/profile.rs | 9 ++- crates/notedeck_columns/src/timeline/kind.rs | 7 +- crates/notedeck_columns/src/timeline/mod.rs | 73 +++++++++++-------- crates/notedeck_columns/src/ui/profile/mod.rs | 3 +- crates/notedeck_columns/src/ui/timeline.rs | 22 ++++-- crates/notedeck_columns/src/unknowns.rs | 4 +- 7 files changed, 75 insertions(+), 46 deletions(-) diff --git a/crates/notedeck_columns/src/args.rs b/crates/notedeck_columns/src/args.rs index 88ce805e..54d64ec3 100644 --- a/crates/notedeck_columns/src/args.rs +++ b/crates/notedeck_columns/src/args.rs @@ -1,6 +1,6 @@ use notedeck::FilterState; -use crate::timeline::{PubkeySource, Timeline, TimelineKind}; +use crate::timeline::{PubkeySource, Timeline, TimelineKind, TimelineTab}; use enostr::{Filter, Pubkey}; use nostrdb::Ndb; use tracing::{debug, error, info}; @@ -151,6 +151,7 @@ impl ArgColumn { ArgColumn::Generic(filters) => Some(Timeline::new( TimelineKind::Generic, FilterState::ready(filters), + TimelineTab::full_tabs(), )), ArgColumn::Timeline(tk) => tk.into_timeline(ndb, user), } diff --git a/crates/notedeck_columns/src/profile.rs b/crates/notedeck_columns/src/profile.rs index 6ab185fc..644ffb1e 100644 --- a/crates/notedeck_columns/src/profile.rs +++ b/crates/notedeck_columns/src/profile.rs @@ -6,7 +6,7 @@ use notedeck::{filter::default_limit, FilterState, MuteFun, NoteCache, NoteRef}; use crate::{ multi_subscriber::MultiSubscriber, notes_holder::NotesHolder, - timeline::{copy_notes_into_timeline, PubkeySource, Timeline, TimelineKind}, + timeline::{copy_notes_into_timeline, PubkeySource, Timeline, TimelineKind, TimelineTab}, }; pub enum DisplayName<'a> { @@ -62,8 +62,11 @@ impl Profile { notes: Vec, is_muted: &MuteFun, ) -> Self { - let mut timeline = - Timeline::new(TimelineKind::profile(source), FilterState::ready(filters)); + let mut timeline = Timeline::new( + TimelineKind::profile(source), + FilterState::ready(filters), + TimelineTab::full_tabs(), + ); copy_notes_into_timeline(&mut timeline, txn, ndb, note_cache, notes, is_muted); diff --git a/crates/notedeck_columns/src/timeline/kind.rs b/crates/notedeck_columns/src/timeline/kind.rs index 86ae6883..b063dd5e 100644 --- a/crates/notedeck_columns/src/timeline/kind.rs +++ b/crates/notedeck_columns/src/timeline/kind.rs @@ -1,5 +1,5 @@ use crate::error::Error; -use crate::timeline::Timeline; +use crate::timeline::{Timeline, TimelineTab}; use enostr::{Filter, Pubkey}; use nostrdb::{Ndb, Transaction}; use notedeck::{filter::default_limit, FilterError, FilterState}; @@ -119,6 +119,7 @@ impl TimelineKind { .kinds([1]) .limit(default_limit()) .build()]), + TimelineTab::no_replies(), )), TimelineKind::Generic => { @@ -141,6 +142,7 @@ impl TimelineKind { Some(Timeline::new( TimelineKind::profile(pk_src), FilterState::ready(vec![filter]), + TimelineTab::full_tabs(), )) } @@ -159,6 +161,7 @@ impl TimelineKind { Some(Timeline::new( TimelineKind::notifications(pk_src), FilterState::ready(vec![notifications_filter]), + TimelineTab::only_notes_and_replies(), )) } @@ -181,6 +184,7 @@ impl TimelineKind { return Some(Timeline::new( TimelineKind::contact_list(pk_src), FilterState::needs_remote(vec![contact_filter.clone()]), + TimelineTab::full_tabs(), )); } @@ -189,6 +193,7 @@ impl TimelineKind { Some(Timeline::new( TimelineKind::contact_list(pk_src), FilterState::needs_remote(vec![contact_filter]), + TimelineTab::full_tabs(), )) } Err(e) => { diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs index d91b95db..fa735d56 100644 --- a/crates/notedeck_columns/src/timeline/mod.rs +++ b/crates/notedeck_columns/src/timeline/mod.rs @@ -60,13 +60,6 @@ impl ViewFilter { } } - pub fn index(&self) -> usize { - match self { - ViewFilter::Notes => 0, - ViewFilter::NotesAndReplies => 1, - } - } - pub fn filter_notes(cache: &CachedNote, note: &Note) -> bool { !cache.reply.borrow(note.tags()).is_reply() } @@ -100,6 +93,21 @@ impl TimelineTab { TimelineTab::new_with_capacity(filter, 1000) } + pub fn only_notes_and_replies() -> Vec { + vec![TimelineTab::new(ViewFilter::NotesAndReplies)] + } + + pub fn no_replies() -> Vec { + vec![TimelineTab::new(ViewFilter::Notes)] + } + + pub fn full_tabs() -> Vec { + vec![ + TimelineTab::new(ViewFilter::Notes), + TimelineTab::new(ViewFilter::NotesAndReplies), + ] + } + pub fn new_with_capacity(filter: ViewFilter, cap: usize) -> Self { let selection = 0i32; let mut list = VirtualList::new(); @@ -179,7 +187,7 @@ pub struct Timeline { // that codepaths have to explicitly handle it pub filter: FilterStates, pub views: Vec, - pub selected_view: i32, + pub selected_view: usize, /// Our nostrdb subscription pub subscription: Option, @@ -198,6 +206,7 @@ impl Timeline { Ok(Timeline::new( TimelineKind::contact_list(pk_src), FilterState::ready(filter), + TimelineTab::full_tabs(), )) } @@ -211,10 +220,11 @@ impl Timeline { Timeline::new( TimelineKind::Hashtag(hashtag), FilterState::ready(vec![filter]), + TimelineTab::full_tabs(), ) } - pub fn make_view_id(id: TimelineId, selected_view: i32) -> egui::Id { + pub fn make_view_id(id: TimelineId, selected_view: usize) -> egui::Id { egui::Id::new((id, selected_view)) } @@ -222,15 +232,12 @@ impl Timeline { Timeline::make_view_id(self.id, self.selected_view) } - pub fn new(kind: TimelineKind, filter_state: FilterState) -> Self { + pub fn new(kind: TimelineKind, filter_state: FilterState, views: Vec) -> Self { // global unique id for all new timelines static UIDS: AtomicU32 = AtomicU32::new(0); let filter = FilterStates::new(filter_state); let subscription: Option = None; - let notes = TimelineTab::new(ViewFilter::Notes); - let replies = TimelineTab::new(ViewFilter::NotesAndReplies); - let views = vec![notes, replies]; let selected_view = 0; let id = TimelineId::new(UIDS.fetch_add(1, Ordering::Relaxed)); @@ -245,23 +252,32 @@ impl Timeline { } pub fn current_view(&self) -> &TimelineTab { - &self.views[self.selected_view as usize] + &self.views[self.selected_view] } pub fn current_view_mut(&mut self) -> &mut TimelineTab { - &mut self.views[self.selected_view as usize] + &mut self.views[self.selected_view] } - pub fn notes(&self, view: ViewFilter) -> &[NoteRef] { - &self.views[view.index()].notes + /// Get the note refs for NotesAndReplies. If we only have Notes, then + /// just return that instead + pub fn all_or_any_notes(&self) -> &[NoteRef] { + self.notes(ViewFilter::NotesAndReplies).unwrap_or_else(|| { + self.notes(ViewFilter::Notes) + .expect("should have at least notes") + }) } - pub fn view(&self, view: ViewFilter) -> &TimelineTab { - &self.views[view.index()] + pub fn notes(&self, view: ViewFilter) -> Option<&[NoteRef]> { + self.view(view).map(|v| &*v.notes) } - pub fn view_mut(&mut self, view: ViewFilter) -> &mut TimelineTab { - &mut self.views[view.index()] + pub fn view(&self, view: ViewFilter) -> Option<&TimelineTab> { + self.views.iter().find(|tab| tab.filter == view) + } + + pub fn view_mut(&mut self, view: ViewFilter) -> Option<&mut TimelineTab> { + self.views.iter_mut().find(|tab| tab.filter == view) } pub fn poll_notes_into_view( @@ -314,13 +330,10 @@ impl Timeline { let reversed = false; // ViewFilter::NotesAndReplies - { + if let Some(view) = timeline.view_mut(ViewFilter::NotesAndReplies) { let refs: Vec = new_refs.iter().map(|(_note, nr)| *nr).collect(); - let reversed = false; - timeline - .view_mut(ViewFilter::NotesAndReplies) - .insert(&refs, reversed); + view.insert(&refs, reversed); } // @@ -328,7 +341,7 @@ impl Timeline { // // TODO(jb55): this is mostly just copied from above, let's just use a loop // I initially tried this but ran into borrow checker issues - { + if let Some(view) = timeline.view_mut(ViewFilter::Notes) { let mut filtered_refs = Vec::with_capacity(new_refs.len()); for (note, nr) in &new_refs { let cached_note = note_cache.cached_note_or_insert(nr.key, note); @@ -338,9 +351,7 @@ impl Timeline { } } - timeline - .view_mut(ViewFilter::Notes) - .insert(&filtered_refs, reversed); + view.insert(&filtered_refs, reversed); } Ok(()) @@ -478,7 +489,7 @@ pub fn send_initial_timeline_filter( filter = filter.limit_mut(lim); } - let notes = timeline.notes(ViewFilter::NotesAndReplies); + let notes = timeline.all_or_any_notes(); // Should we since optimize? Not always. For example // if we only have a few notes locally. One way to diff --git a/crates/notedeck_columns/src/ui/profile/mod.rs b/crates/notedeck_columns/src/ui/profile/mod.rs index 488a51e8..a6ba48a6 100644 --- a/crates/notedeck_columns/src/ui/profile/mod.rs +++ b/crates/notedeck_columns/src/ui/profile/mod.rs @@ -67,7 +67,8 @@ impl<'a> ProfileView<'a> { ) .get_ptr(); - profile.timeline.selected_view = tabs_ui(ui); + profile.timeline.selected_view = + tabs_ui(ui, profile.timeline.selected_view, &profile.timeline.views); // poll for new notes and insert them into our existing notes if let Err(e) = profile.poll_notes_into_view(&txn, self.ndb, is_muted) { diff --git a/crates/notedeck_columns/src/ui/timeline.rs b/crates/notedeck_columns/src/ui/timeline.rs index 3e6554a0..0732e6f1 100644 --- a/crates/notedeck_columns/src/ui/timeline.rs +++ b/crates/notedeck_columns/src/ui/timeline.rs @@ -1,6 +1,11 @@ use crate::actionbar::NoteAction; use crate::timeline::TimelineTab; -use crate::{column::Columns, timeline::TimelineId, ui, ui::note::NoteOptions}; +use crate::{ + column::Columns, + timeline::{TimelineId, ViewFilter}, + ui, + ui::note::NoteOptions, +}; use egui::containers::scroll_area::ScrollBarVisibility; use egui::{Direction, Layout}; use egui_tabs::TabColor; @@ -86,7 +91,7 @@ fn timeline_ui( return None; }; - timeline.selected_view = tabs_ui(ui); + timeline.selected_view = tabs_ui(ui, timeline.selected_view, &timeline.views); // need this for some reason?? ui.add_space(3.0); @@ -124,11 +129,11 @@ fn timeline_ui( .inner } -pub fn tabs_ui(ui: &mut egui::Ui) -> i32 { +pub fn tabs_ui(ui: &mut egui::Ui, selected: usize, views: &[TimelineTab]) -> usize { ui.spacing_mut().item_spacing.y = 0.0; - let tab_res = egui_tabs::Tabs::new(2) - .selected(1) + let tab_res = egui_tabs::Tabs::new(views.len() as i32) + .selected(selected as i32) .hover_bg(TabColor::none()) .selected_fg(TabColor::none()) .selected_bg(TabColor::none()) @@ -141,7 +146,10 @@ pub fn tabs_ui(ui: &mut egui::Ui) -> i32 { let ind = state.index(); - let txt = if ind == 0 { "Notes" } else { "Notes & Replies" }; + let txt = match views[ind as usize].filter { + ViewFilter::Notes => "Notes", + ViewFilter::NotesAndReplies => "Notes & Replies", + }; let res = ui.add(egui::Label::new(txt).selectable(false)); @@ -189,7 +197,7 @@ pub fn tabs_ui(ui: &mut egui::Ui) -> i32 { ui.painter().hline(underline, underline_y, stroke); - sel + sel as usize } fn get_label_width(ui: &mut egui::Ui, text: &str) -> f32 { diff --git a/crates/notedeck_columns/src/unknowns.rs b/crates/notedeck_columns/src/unknowns.rs index 13dcc023..4dbfc1bd 100644 --- a/crates/notedeck_columns/src/unknowns.rs +++ b/crates/notedeck_columns/src/unknowns.rs @@ -1,4 +1,4 @@ -use crate::{column::Columns, timeline::ViewFilter, Result}; +use crate::{column::Columns, Result}; use nostrdb::{Ndb, NoteKey, Transaction}; use notedeck::{CachedNote, NoteCache, UnknownIds}; use tracing::error; @@ -37,7 +37,7 @@ pub fn get_unknown_ids( let mut new_cached_notes: Vec<(NoteKey, CachedNote)> = vec![]; for timeline in columns.timelines() { - for noteref in timeline.notes(ViewFilter::NotesAndReplies) { + for noteref in timeline.all_or_any_notes() { let note = ndb.get_note_by_key(txn, noteref.key)?; let note_key = note.key().unwrap(); let cached_note = note_cache.cached_note(noteref.key);