notedeck

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

commit 5e45e4a2e572c0d273eaff313d54a6cffed45344
parent 4e7d168d223bd3b02363468b80c030dc02cc62dc
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 12 Apr 2024 16:46:31 -0700

ui: simplify note widget

pass just a note pointer to the note ui widget. We currently don't
support non-db notes but we can add support for that later.

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

Diffstat:
Msrc/app.rs | 19+++++++++++++++----
Msrc/ui/note/mod.rs | 179++++++++++++++++++++++++++++++++++++++-----------------------------------------
2 files changed, 100 insertions(+), 98 deletions(-)

diff --git a/src/app.rs b/src/app.rs @@ -515,17 +515,28 @@ struct NoteTimelineKey { note_key: NoteKey, } -fn render_notes(ui: &mut egui::Ui, damus: &mut Damus, timeline: usize) { +fn render_notes(ui: &mut egui::Ui, damus: &mut Damus, timeline: usize) -> Result<()> { #[cfg(feature = "profiling")] puffin::profile_function!(); let num_notes = damus.timelines[timeline].notes.len(); + let txn = Transaction::new(&damus.ndb)?; for i in 0..num_notes { - let note = ui::Note::new(damus, damus.timelines[timeline].notes[i].key, timeline); - ui.add(note); + let note_key = damus.timelines[timeline].notes[i].key; + let note = if let Ok(note) = damus.ndb.get_note_by_key(&txn, note_key) { + note + } else { + warn!("failed to query note {:?}", note_key); + continue; + }; + + let note_ui = ui::Note::new(damus, &note, timeline); + ui.add(note_ui); ui.add(egui::Separator::default().spacing(0.0)); } + + Ok(()) } fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) { @@ -545,7 +556,7 @@ fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) { */ .show(ui, |ui| { ui.spacing_mut().item_spacing.y = 0.0; - render_notes(ui, app, timeline); + let _ = render_notes(ui, app, timeline); }); } diff --git a/src/ui/note/mod.rs b/src/ui/note/mod.rs @@ -3,11 +3,11 @@ pub use contents::NoteContents; use crate::{ui, Damus}; use egui::{Color32, Label, RichText, Sense, TextureHandle, Vec2}; -use nostrdb::{NoteKey, Transaction}; +use nostrdb::NoteKey; pub struct Note<'a> { app: &'a mut Damus, - note_key: NoteKey, + note: &'a nostrdb::Note<'a>, timeline: usize, } @@ -19,128 +19,119 @@ struct NoteTimelineKey { impl<'a> egui::Widget for Note<'a> { fn ui(self, ui: &mut egui::Ui) -> egui::Response { - let res = if self.app.textmode { + if self.app.textmode { self.textmode_ui(ui) } else { self.standard_ui(ui) - }; - - if let Ok(resp) = res { - resp - } else { - ui.label("Could not render note") } } } impl<'a> Note<'a> { - pub fn new(app: &'a mut Damus, note_key: NoteKey, timeline: usize) -> Self { + pub fn new(app: &'a mut Damus, note: &'a nostrdb::Note<'a>, timeline: usize) -> Self { Note { app, - note_key, + note, timeline, } } - fn textmode_ui(self, ui: &mut egui::Ui) -> Result<egui::Response, nostrdb::Error> { - let txn = Transaction::new(&self.app.ndb)?; - let note = self.app.ndb.get_note_by_key(&txn, self.note_key)?; + fn textmode_ui(self, ui: &mut egui::Ui) -> egui::Response { + let note_key = self.note.key().expect("todo: implement non-db notes"); + let txn = self.note.txn().expect("todo: implement non-db notes"); - Ok(ui - .with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { - let profile = self.app.ndb.get_profile_by_pubkey(&txn, note.pubkey()); + ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { + let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey()); - ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 2.0; + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 2.0; - let note_cache = self - .app - .get_note_cache_mut(self.note_key, note.created_at()); - let (_id, rect) = ui.allocate_space(egui::vec2(50.0, 20.0)); - ui.allocate_rect(rect, Sense::hover()); - ui.put(rect, |ui: &mut egui::Ui| { - render_reltime(ui, note_cache, false).response - }); - let (_id, rect) = ui.allocate_space(egui::vec2(150.0, 20.0)); - ui.allocate_rect(rect, Sense::hover()); - ui.put(rect, |ui: &mut egui::Ui| { - ui.add( - ui::Username::new(profile.as_ref().ok(), note.pubkey()) - .abbreviated(8) - .pk_colored(true), - ) - }); + let note_cache = self + .app + .get_note_cache_mut(note_key, self.note.created_at()); - ui.add(NoteContents::new(self.app, &txn, &note, self.note_key)); + let (_id, rect) = ui.allocate_space(egui::vec2(50.0, 20.0)); + ui.allocate_rect(rect, Sense::hover()); + ui.put(rect, |ui: &mut egui::Ui| { + render_reltime(ui, note_cache, false).response }); - }) - .response) + let (_id, rect) = ui.allocate_space(egui::vec2(150.0, 20.0)); + ui.allocate_rect(rect, Sense::hover()); + ui.put(rect, |ui: &mut egui::Ui| { + ui.add( + ui::Username::new(profile.as_ref().ok(), self.note.pubkey()) + .abbreviated(8) + .pk_colored(true), + ) + }); + + ui.add(NoteContents::new(self.app, txn, self.note, note_key)); + }); + }) + .response } - pub fn standard_ui(self, ui: &mut egui::Ui) -> Result<egui::Response, nostrdb::Error> { + pub fn standard_ui(self, ui: &mut egui::Ui) -> egui::Response { #[cfg(feature = "profiling")] puffin::profile_function!(); - - let txn = Transaction::new(&self.app.ndb)?; - let note = self.app.ndb.get_note_by_key(&txn, self.note_key)?; - let note_key = self.note_key; + let note_key = self.note.key().expect("todo: support non-db notes"); + let txn = self.note.txn().expect("todo: support non-db notes"); let timeline = self.timeline; let id = egui::Id::new(NoteTimelineKey { note_key, timeline }); - Ok(ui - .with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { - let profile = self.app.ndb.get_profile_by_pubkey(&txn, note.pubkey()); - - let mut collapse_state = - egui::collapsing_header::CollapsingState::load_with_default_open( - ui.ctx(), - id, - false, - ); - - let inner_resp = crate::ui::padding(6.0, ui, |ui| { - match profile - .as_ref() - .ok() - .and_then(|p| p.record.profile()?.picture()) - { - // these have different lifetimes and types, - // so the calls must be separate - Some(pic) => render_pfp(ui, self.app, pic), - None => render_pfp(ui, self.app, no_pfp_url()), - } - - ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { - ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 2.0; - ui.add( - ui::Username::new(profile.as_ref().ok(), note.pubkey()) - .abbreviated(20), - ); - - let note_cache = self - .app - .get_note_cache_mut(self.note_key, note.created_at()); - render_reltime(ui, note_cache, true); - }); - - ui.add(NoteContents::new(self.app, &txn, &note, self.note_key)); - - render_note_actionbar(ui); - - //let header_res = ui.horizontal(|ui| {}); + ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { + let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey()); + + let mut collapse_state = + egui::collapsing_header::CollapsingState::load_with_default_open( + ui.ctx(), + id, + false, + ); + + let inner_resp = crate::ui::padding(6.0, ui, |ui| { + match profile + .as_ref() + .ok() + .and_then(|p| p.record.profile()?.picture()) + { + // these have different lifetimes and types, + // so the calls must be separate + Some(pic) => render_pfp(ui, self.app, pic), + None => render_pfp(ui, self.app, no_pfp_url()), + } + + ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 2.0; + ui.add( + ui::Username::new(profile.as_ref().ok(), self.note.pubkey()) + .abbreviated(20), + ); + + let note_cache = self + .app + .get_note_cache_mut(note_key, self.note.created_at()); + render_reltime(ui, note_cache, true); }); + + ui.add(NoteContents::new(self.app, txn, self.note, note_key)); + + render_note_actionbar(ui); + + //let header_res = ui.horizontal(|ui| {}); }); + }); - let resp = ui.interact(inner_resp.response.rect, id, Sense::hover()); + let resp = ui.interact(inner_resp.response.rect, id, Sense::hover()); - if resp.hovered() ^ collapse_state.is_open() { - //info!("clicked {:?}, {}", self.note_key, collapse_state.is_open()); - collapse_state.toggle(ui); - collapse_state.store(ui.ctx()); - } - }) - .response) + if resp.hovered() ^ collapse_state.is_open() { + //info!("clicked {:?}, {}", self.note_key, collapse_state.is_open()); + collapse_state.toggle(ui); + collapse_state.store(ui.ctx()); + } + }) + .response } }