notedeck

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

commit b6348b15072449cca45ca141f7e4dcdc0dbd42d3
parent c5093a718093701f0f068ba8cddaa1ea5820623c
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 10 Jul 2025 14:29:56 -0700

note/options: simplify flag logic

simpler, less macro magic

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mcrates/notedeck_columns/src/app.rs | 15++++++++++++---
Mcrates/notedeck_columns/src/timeline/route.rs | 2+-
Mcrates/notedeck_columns/src/ui/thread.rs | 4++--
Mcrates/notedeck_ui/src/note/contents.rs | 16++++++++--------
Mcrates/notedeck_ui/src/note/mod.rs | 38+++++++++++++++++++-------------------
Mcrates/notedeck_ui/src/note/options.rs | 76+++++++++++++++++++++++-----------------------------------------------------
6 files changed, 65 insertions(+), 86 deletions(-)

diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -437,9 +437,18 @@ impl Damus { let debug = ctx.args.debug; let support = Support::new(ctx.path); let mut note_options = NoteOptions::default(); - note_options.set_textmode(parsed_args.is_flag_set(ColumnsFlag::Textmode)); - note_options.set_scramble_text(parsed_args.is_flag_set(ColumnsFlag::Scramble)); - note_options.set_hide_media(parsed_args.is_flag_set(ColumnsFlag::NoMedia)); + note_options.set( + NoteOptions::Textmode, + parsed_args.is_flag_set(ColumnsFlag::Textmode), + ); + note_options.set( + NoteOptions::ScrambleText, + parsed_args.is_flag_set(ColumnsFlag::Scramble), + ); + note_options.set( + NoteOptions::HideMedia, + parsed_args.is_flag_set(ColumnsFlag::NoMedia), + ); let jobs = JobsCache::default(); diff --git a/crates/notedeck_columns/src/timeline/route.rs b/crates/notedeck_columns/src/timeline/route.rs @@ -88,7 +88,7 @@ pub fn render_thread_route( ) -> Option<RenderNavAction> { // don't truncate thread notes for now, since they are // default truncated everywher eelse - note_options.set_truncate(false); + note_options.set(NoteOptions::Truncate, false); ui::ThreadView::new( threads, diff --git a/crates/notedeck_columns/src/ui/thread.rs b/crates/notedeck_columns/src/ui/thread.rs @@ -302,8 +302,8 @@ impl<'a> ThreadNote<'a> { match self.note_type { ThreadNoteType::Chain { root: _ } => cur_options, ThreadNoteType::Selected { root: _ } => { - cur_options.set_wide(true); - cur_options.set_selectable_text(true); + cur_options.set(NoteOptions::Wide, true); + cur_options.set(NoteOptions::SelectableText, true); cur_options } ThreadNoteType::Reply => cur_options, diff --git a/crates/notedeck_ui/src/note/contents.rs b/crates/notedeck_ui/src/note/contents.rs @@ -123,17 +123,17 @@ pub fn render_note_contents( jobs: &mut JobsCache, ) -> NoteResponse { let note_key = note.key().expect("todo: implement non-db notes"); - let selectable = options.has_selectable_text(); + let selectable = options.has(NoteOptions::SelectableText); let mut note_action: Option<NoteAction> = None; let mut inline_note: Option<(&[u8; 32], &str)> = None; - let hide_media = options.has_hide_media(); + let hide_media = options.has(NoteOptions::HideMedia); let link_color = ui.visuals().hyperlink_color; // The current length of the rendered blocks. Used in trucation logic let mut current_len: usize = 0; let truncate_len = 280; - if !options.has_is_preview() { + if !options.has(NoteOptions::Preview) { // need this for the rect to take the full width of the column let _ = ui.allocate_at_least(egui::vec2(ui.available_width(), 0.0), egui::Sense::click()); } @@ -183,11 +183,11 @@ pub fn render_note_contents( } } - Mention::Note(note) if options.has_note_previews() => { + Mention::Note(note) if options.has(NoteOptions::Preview) => { inline_note = Some((note.id(), block.as_str())); } - Mention::Event(note) if options.has_note_previews() => { + Mention::Event(note) if options.has(NoteOptions::Preview) => { inline_note = Some((note.id(), block.as_str())); } @@ -233,7 +233,7 @@ pub fn render_note_contents( BlockType::Text => { // truncate logic let mut truncate = false; - let block_str = if options.has_truncate() + let block_str = if options.has(NoteOptions::Truncate) && (current_len + block.as_str().len() > truncate_len) { truncate = true; @@ -251,7 +251,7 @@ pub fn render_note_contents( block_str }; - if options.has_scramble_text() { + if options.has(NoteOptions::ScrambleText) { ui.add( egui::Label::new(rot13(block_str)) .wrap() @@ -287,7 +287,7 @@ pub fn render_note_contents( }); let mut media_action = None; - if !supported_medias.is_empty() && !options.has_textmode() { + if !supported_medias.is_empty() && !options.has(NoteOptions::Textmode) { ui.add_space(2.0); let carousel_id = egui::Id::new(("carousel", note.key().expect("expected tx note"))); diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs @@ -90,8 +90,8 @@ impl<'a, 'd> NoteView<'a, 'd> { mut flags: NoteOptions, jobs: &'a mut JobsCache, ) -> Self { - flags.set_actionbar(true); - flags.set_note_previews(true); + flags.set(NoteOptions::ActionBar, true); + flags.set(NoteOptions::NotePreviews, true); let framed = false; let parent: Option<NoteKey> = None; @@ -119,17 +119,17 @@ impl<'a, 'd> NoteView<'a, 'd> { } pub fn textmode(mut self, enable: bool) -> Self { - self.options_mut().set_textmode(enable); + self.options_mut().set(NoteOptions::Textmode, enable); self } pub fn actionbar(mut self, enable: bool) -> Self { - self.options_mut().set_actionbar(enable); + self.options_mut().set(NoteOptions::ActionBar, enable); self } pub fn hide_media(mut self, enable: bool) -> Self { - self.options_mut().set_hide_media(enable); + self.options_mut().set(NoteOptions::HideMedia, enable); self } @@ -139,37 +139,37 @@ impl<'a, 'd> NoteView<'a, 'd> { } pub fn truncate(mut self, enable: bool) -> Self { - self.options_mut().set_truncate(enable); + self.options_mut().set(NoteOptions::Truncate, enable); self } pub fn small_pfp(mut self, enable: bool) -> Self { - self.options_mut().set_small_pfp(enable); + self.options_mut().set(NoteOptions::SmallPfp, enable); self } pub fn medium_pfp(mut self, enable: bool) -> Self { - self.options_mut().set_medium_pfp(enable); + self.options_mut().set(NoteOptions::MediumPfp, enable); self } pub fn note_previews(mut self, enable: bool) -> Self { - self.options_mut().set_note_previews(enable); + self.options_mut().set(NoteOptions::NotePreviews, enable); self } pub fn selectable_text(mut self, enable: bool) -> Self { - self.options_mut().set_selectable_text(enable); + self.options_mut().set(NoteOptions::SelectableText, enable); self } pub fn wide(mut self, enable: bool) -> Self { - self.options_mut().set_wide(enable); + self.options_mut().set(NoteOptions::Wide, enable); self } pub fn options_button(mut self, enable: bool) -> Self { - self.options_mut().set_options_button(enable); + self.options_mut().set(NoteOptions::OptionsButton, enable); self } @@ -187,7 +187,7 @@ impl<'a, 'd> NoteView<'a, 'd> { } pub fn is_preview(mut self, is_preview: bool) -> Self { - self.options_mut().set_is_preview(is_preview); + self.options_mut().set(NoteOptions::Preview, is_preview); self } @@ -252,7 +252,7 @@ impl<'a, 'd> NoteView<'a, 'd> { profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>, ui: &mut egui::Ui, ) -> PfpResponse { - if !self.options().has_wide() { + if !self.options().has(NoteOptions::Wide) { ui.spacing_mut().item_spacing.x = 16.0; } else { ui.spacing_mut().item_spacing.x = 4.0; @@ -337,7 +337,7 @@ impl<'a, 'd> NoteView<'a, 'd> { } pub fn show(&mut self, ui: &mut egui::Ui) -> NoteResponse { - if self.options().has_textmode() { + if self.options().has(NoteOptions::Textmode) { NoteResponse::new(self.textmode_ui(ui)) } else if self.framed { egui::Frame::new() @@ -468,7 +468,7 @@ impl<'a, 'd> NoteView<'a, 'd> { note_action = contents.action.or(note_action); - if self.options().has_actionbar() { + if self.options().has(NoteOptions::ActionBar) { note_action = render_note_actionbar( ui, self.zapping_acc.as_ref().map(|c| Zapper { @@ -549,7 +549,7 @@ impl<'a, 'd> NoteView<'a, 'd> { note_action = contents.action.or(note_action); - if self.options().has_actionbar() { + if self.options().has(NoteOptions::ActionBar) { note_action = render_note_actionbar( ui, self.zapping_acc.as_ref().map(|c| Zapper { @@ -587,7 +587,7 @@ impl<'a, 'd> NoteView<'a, 'd> { let maybe_hitbox = maybe_note_hitbox(ui, hitbox_id); // wide design - let response = if self.options().has_wide() { + let response = if self.options().has(NoteOptions::Wide) { self.wide_ui(ui, txn, note_key, &profile) } else { self.standard_ui(ui, txn, note_key, &profile) @@ -596,7 +596,7 @@ impl<'a, 'd> NoteView<'a, 'd> { let note_ui_resp = response.inner; let mut note_action = note_ui_resp.action; - if self.options().has_options_button() { + if self.options().has(NoteOptions::OptionsButton) { let context_pos = { let size = NoteContextButton::max_width(); let top_right = response.response.rect.right_top(); diff --git a/crates/notedeck_ui/src/note/options.rs b/crates/notedeck_ui/src/note/options.rs @@ -6,79 +6,49 @@ bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct NoteOptions: u64 { - const actionbar = 0b0000000000000001; - const note_previews = 0b0000000000000010; - const small_pfp = 0b0000000000000100; - const medium_pfp = 0b0000000000001000; - const wide = 0b0000000000010000; - const selectable_text = 0b0000000000100000; - const textmode = 0b0000000001000000; - const options_button = 0b0000000010000000; - const hide_media = 0b0000000100000000; - + const ActionBar = 1 << 0; + const NotePreviews = 1 << 1; + const SmallPfp = 1 << 2; + const MediumPfp = 1 << 3; + const Wide = 1 << 4; + const SelectableText = 1 << 5; + const Textmode = 1 << 6; + const OptionsButton = 1 << 7; + const HideMedia = 1 << 8; /// Scramble text so that its not distracting during development - const scramble_text = 0b0000001000000000; - - /// Whether the current note is a preview - const is_preview = 0b0000010000000000; - + const ScrambleText = 1 << 9; + /// Is this a note preview? + const Preview = 1 << 10; /// Is the content truncated? If the length is over a certain size it /// will end with a ... and a "Show more" button. - const truncate = 0b0000100000000000; + const Truncate = 1 << 11; } } impl Default for NoteOptions { fn default() -> NoteOptions { - NoteOptions::options_button - | NoteOptions::note_previews - | NoteOptions::actionbar - | NoteOptions::truncate + NoteOptions::OptionsButton + | NoteOptions::NotePreviews + | NoteOptions::ActionBar + | NoteOptions::Truncate } } -macro_rules! create_bit_methods { - ($fn_name:ident, $has_name:ident, $option:ident) => { - #[inline] - pub fn $fn_name(&mut self, enable: bool) { - if enable { - *self |= NoteOptions::$option; - } else { - *self &= !NoteOptions::$option; - } - } - - #[inline] - pub fn $has_name(self) -> bool { - (self & NoteOptions::$option) == NoteOptions::$option - } - }; -} - impl NoteOptions { - 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); - create_bit_methods!(set_scramble_text, has_scramble_text, scramble_text); - create_bit_methods!(set_is_preview, has_is_preview, is_preview); - create_bit_methods!(set_truncate, has_truncate, truncate); + pub fn has(self, flag: NoteOptions) -> bool { + (self & flag) == flag + } pub fn new(is_universe_timeline: bool) -> Self { let mut options = NoteOptions::default(); - options.set_hide_media(is_universe_timeline); + options.set(NoteOptions::HideMedia, is_universe_timeline); options } pub fn pfp_size(&self) -> i8 { - if self.has_small_pfp() { + if self.has(NoteOptions::SmallPfp) { ProfilePic::small_size() - } else if self.has_medium_pfp() { + } else if self.has(NoteOptions::MediumPfp) { ProfilePic::medium_size() } else { ProfilePic::default_size()