notedeck

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

commit 571435cf85bcd2d399aa3052936b46429ee7c44a
parent 8f8ff42156c7c046ca0ae9a255a9a8c51d07f0b0
Author: kernelkind <kernelkind@gmail.com>
Date:   Thu,  4 Sep 2025 15:01:46 -0400

ui: modularize composite entry rendering & fix tr

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/notedeck_columns/src/ui/timeline.rs | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 116 insertions(+), 16 deletions(-)

diff --git a/crates/notedeck_columns/src/ui/timeline.rs b/crates/notedeck_columns/src/ui/timeline.rs @@ -5,14 +5,15 @@ use enostr::Pubkey; use nostrdb::{ProfileRecord, Transaction}; use notedeck::name::get_display_name; use notedeck::ui::is_narrow; -use notedeck::{JobsCache, Muted, NoteRef}; +use notedeck::{tr_plural, JobsCache, Muted, NoteRef}; use notedeck_ui::app_images::like_image; use notedeck_ui::ProfilePic; use std::f32::consts::PI; use tracing::{error, warn}; use crate::timeline::{ - CompositeUnit, NoteUnit, ReactionUnit, TimelineCache, TimelineKind, TimelineTab, ViewFilter, + CompositeType, CompositeUnit, NoteUnit, ReactionUnit, TimelineCache, TimelineKind, TimelineTab, + ViewFilter, }; use notedeck::{ note::root_note_id_from_selected_id, tr, Localization, NoteAction, NoteContext, ScrollInfo, @@ -464,6 +465,83 @@ impl<'a, 'd> TimelineTabView<'a, 'd> { } } +enum ReferencedNoteType { + Tagged, + Yours, +} + +impl CompositeType { + fn image(&self, darkmode: bool) -> egui::Image<'static> { + match self { + CompositeType::Reaction => like_image(), + } + } + + fn description( + &self, + loc: &mut Localization, + first_name: &str, + total_count: usize, + referenced_type: ReferencedNoteType, + ) -> String { + let count = total_count - 1; + + match self { + CompositeType::Reaction => { + reaction_description(loc, first_name, count, referenced_type) + } + } + } +} + +fn reaction_description( + loc: &mut Localization, + first_name: &str, + count: usize, + referenced_type: ReferencedNoteType, +) -> String { + match referenced_type { + ReferencedNoteType::Tagged => { + if count == 0 { + tr!( + loc, + "{name} reacted to a note you were tagged in", + "reaction from user to a note you were tagged in", + name = first_name + ) + } else { + tr_plural!( + loc, + "{name} and {count} other reacted to a note you were tagged in", + "{name} and {count} others reacted to a note you were tagged in", + "amount of reactions a note you were tagged in received", + count, + name = first_name + ) + } + } + ReferencedNoteType::Yours => { + if count == 0 { + tr!( + loc, + "{name} reacted to your note", + "reaction from user to your note", + name = first_name + ) + } else { + tr_plural!( + loc, + "{name} and {count} other reacted to your note", + "{name} and {count} others reacted to your note", + "describing the amount of reactions your note received", + count, + name = first_name + ) + } + } + } +} + fn render_note( ui: &mut egui::Ui, note_context: &mut NoteContext, @@ -534,10 +612,30 @@ fn render_reaction_cluster( }) .collect(); + render_composite_entry( + ui, + note_context, + note_options, + jobs, + reacted_to_note, + profiles_to_show, + CompositeType::Reaction, + ) +} + +fn render_composite_entry( + ui: &mut egui::Ui, + note_context: &mut NoteContext, + note_options: NoteOptions, + jobs: &mut JobsCache, + underlying_note: nostrdb::Note<'_>, + profiles_to_show: Vec<ProfileEntry>, + composite_type: CompositeType, +) -> RenderEntryResponse { let first_name = get_display_name(profiles_to_show.iter().find_map(|opt| opt.record.as_ref())) .name() .to_string(); - let num_profiles_other = profiles_to_show.len() - 1; + let num_profiles = profiles_to_show.len(); let mut action = None; egui::Frame::new() @@ -549,7 +647,10 @@ fn render_reaction_cluster( |ui| { ui.vertical(|ui| { ui.add_space(4.0); - ui.add_sized(vec2(28.0, 28.0), like_image()); + ui.add_sized( + vec2(28.0, 28.0), + composite_type.image(ui.visuals().dark_mode), + ); }); ui.add_space(16.0); @@ -577,17 +678,17 @@ fn render_reaction_cluster( }, ); - let note_type_desc = if note_context + let referenced_type = if note_context .accounts .get_selected_account() .key .pubkey .bytes() - != reacted_to_note.pubkey() + != underlying_note.pubkey() { - "note you were tagged in" + ReferencedNoteType::Tagged } else { - "your note" + ReferencedNoteType::Yours }; ui.add_space(2.0); @@ -595,13 +696,12 @@ fn render_reaction_cluster( ui.add_space(52.0); ui.horizontal_wrapped(|ui| { - if num_profiles_other > 0 { - ui.label(format!( - "{first_name} and {num_profiles_other} others reacted to {note_type_desc}", - )); - } else { - ui.label(format!("{first_name} reacted to {note_type_desc}")); - } + ui.label(composite_type.description( + note_context.i18n, + &first_name, + num_profiles, + referenced_type, + )) }); }); @@ -612,7 +712,7 @@ fn render_reaction_cluster( let options = note_options .difference(NoteOptions::ActionBar | NoteOptions::OptionsButton) .union(NoteOptions::NotificationPreview); - let resp = NoteView::new(note_context, &reacted_to_note, options, jobs).show(ui); + let resp = NoteView::new(note_context, &underlying_note, options, jobs).show(ui); if let Some(note_action) = resp.action { action = Some(note_action);