From ce3f24abcd07d74f236d9e8d7e1df8d08f62290c Mon Sep 17 00:00:00 2001 From: kernelkind Date: Fri, 11 Oct 2024 16:36:34 -0400 Subject: [PATCH] add profile preview and implement scrolling Signed-off-by: kernelkind --- src/column.rs | 5 +- src/nav.rs | 6 +- src/route.rs | 8 +-- src/timeline/route.rs | 3 +- src/ui/profile/mod.rs | 43 +++++++---- src/ui/timeline.rs | 164 ++++++++++++++++++++++++++---------------- 6 files changed, 143 insertions(+), 86 deletions(-) diff --git a/src/column.rs b/src/column.rs index 3a7fc99..7415920 100644 --- a/src/column.rs +++ b/src/column.rs @@ -1,5 +1,6 @@ use crate::route::{Route, Router}; use crate::timeline::{Timeline, TimelineId}; +use enostr::Pubkey; use indexmap::IndexMap; use std::iter::Iterator; use std::sync::atomic::{AtomicU32, Ordering}; @@ -60,10 +61,10 @@ impl Columns { self.timelines.insert(col_id, timeline); } - pub fn route_profile_timeline(&mut self, col: usize, timeline: Timeline) { + pub fn route_profile_timeline(&mut self, col: usize, pubkey: Pubkey, timeline: Timeline) { self.column_mut(col) .router_mut() - .route_to(Route::Profile(timeline.id)); + .route_to(Route::Profile(pubkey, timeline.id)); self.timelines.insert(Self::get_new_id(), timeline); } diff --git a/src/nav.rs b/src/nav.rs index f8c0fa4..2b05d51 100644 --- a/src/nav.rs +++ b/src/nav.rs @@ -113,8 +113,9 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) { None } - Route::Profile(id) => render_profile_route( + Route::Profile(pubkey, id) => render_profile_route( *id, + *pubkey, &app.ndb, &mut app.columns, &mut app.pool, @@ -150,7 +151,8 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) { TimelineKind::profile(pubkey_source).into_timeline(&app.ndb, None) { let timeline_id = timeline.id; - app.columns_mut().route_profile_timeline(col, timeline); + app.columns_mut() + .route_profile_timeline(col, pubkey, timeline); app.subscribe_new_timeline(timeline_id); } } diff --git a/src/route.rs b/src/route.rs index 3ee6be4..b8339ac 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,4 +1,4 @@ -use enostr::NoteId; +use enostr::{NoteId, Pubkey}; use nostrdb::Ndb; use std::fmt::{self}; @@ -17,7 +17,7 @@ pub enum Route { Relays, ComposeNote, AddColumn, - Profile(TimelineId), + Profile(Pubkey, TimelineId), } #[derive(Clone)] @@ -97,7 +97,7 @@ impl Route { }, Route::ComposeNote => "Compose Note".to_owned(), Route::AddColumn => "Add Column".to_owned(), - Route::Profile(id) => { + Route::Profile(_, id) => { let timeline = columns .find_timeline(*id) .expect("expected to find timeline"); @@ -210,7 +210,7 @@ impl fmt::Display for Route { Route::ComposeNote => write!(f, "Compose Note"), Route::AddColumn => write!(f, "Add Column"), - Route::Profile(_) => write!(f, "Profile"), + Route::Profile(_, _) => write!(f, "Profile"), } } } diff --git a/src/timeline/route.rs b/src/timeline/route.rs index fa9b898..85fae01 100644 --- a/src/timeline/route.rs +++ b/src/timeline/route.rs @@ -154,6 +154,7 @@ pub fn render_timeline_route( #[allow(clippy::too_many_arguments)] pub fn render_profile_route( id: TimelineId, + pubkey: Pubkey, ndb: &Ndb, columns: &mut Columns, pool: &mut RelayPool, @@ -163,7 +164,7 @@ pub fn render_profile_route( col: usize, ui: &mut egui::Ui, ) -> Option { - let timeline_response = ProfileView::new(id, columns, ndb, note_cache, img_cache).ui(ui); + let timeline_response = ProfileView::new(pubkey, id, columns, ndb, note_cache, img_cache).ui(ui); if let Some(bar_action) = timeline_response.bar_action { let txn = nostrdb::Transaction::new(ndb).expect("txn"); let mut cur_column = columns.columns_mut(); diff --git a/src/ui/profile/mod.rs b/src/ui/profile/mod.rs index 51f1284..fbba30b 100644 --- a/src/ui/profile/mod.rs +++ b/src/ui/profile/mod.rs @@ -1,11 +1,11 @@ pub mod picture; pub mod preview; -use egui::{Label, RichText}; -use nostrdb::Ndb; +use egui::{ScrollArea, Widget}; +use enostr::Pubkey; +use nostrdb::{Ndb, Transaction}; pub use picture::ProfilePic; pub use preview::ProfilePreview; -use tracing::info; use crate::{ actionbar::TimelineResponse, column::Columns, imgcache::ImageCache, notecache::NoteCache, @@ -15,6 +15,7 @@ use crate::{ use super::TimelineView; pub struct ProfileView<'a> { + pubkey: Pubkey, timeline_id: TimelineId, columns: &'a mut Columns, ndb: &'a Ndb, @@ -24,6 +25,7 @@ pub struct ProfileView<'a> { impl<'a> ProfileView<'a> { pub fn new( + pubkey: Pubkey, timeline_id: TimelineId, columns: &'a mut Columns, ndb: &'a Ndb, @@ -31,6 +33,7 @@ impl<'a> ProfileView<'a> { img_cache: &'a mut ImageCache, ) -> Self { ProfileView { + pubkey, timeline_id, columns, ndb, @@ -40,18 +43,28 @@ impl<'a> ProfileView<'a> { } pub fn ui(&mut self, ui: &mut egui::Ui) -> TimelineResponse { - ui.add(Label::new( - RichText::new("PROFILE VIEW").text_style(egui::TextStyle::Heading), - )); + let scroll_id = egui::Id::new(("profile_scroll", self.timeline_id, self.pubkey)); - TimelineView::new( - self.timeline_id, - self.columns, - self.ndb, - self.note_cache, - self.img_cache, - false, - ) - .ui(ui) + ScrollArea::vertical() + .id_source(scroll_id) + .show(ui, |ui| { + { + let txn = Transaction::new(self.ndb).expect("txn"); + if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, self.pubkey.bytes()) { + ProfilePreview::new(&profile, self.img_cache).ui(ui); + } + } + + TimelineView::new( + self.timeline_id, + self.columns, + self.ndb, + self.note_cache, + self.img_cache, + false, + ) + .ui_no_scroll(ui) + }) + .inner } } diff --git a/src/ui/timeline.rs b/src/ui/timeline.rs index 2378ef2..cc8c972 100644 --- a/src/ui/timeline.rs +++ b/src/ui/timeline.rs @@ -54,6 +54,23 @@ impl<'a> TimelineView<'a> { ) } + pub fn ui_no_scroll(&mut self, ui: &mut egui::Ui) -> TimelineResponse { + if let Some(timeline) = self.columns.find_timeline_mut(self.timeline_id) { + timeline.selected_view = tabs_ui(ui); + }; + + timeline_ui_no_scroll( + ui, + self.ndb, + self.timeline_id, + self.columns, + self.note_cache, + self.img_cache, + self.reverse, + self.textmode, + ) + } + pub fn reversed(mut self) -> Self { self.reverse = true; self @@ -96,82 +113,105 @@ fn timeline_ui( egui::Id::new(("tlscroll", timeline.view_id())) }; - let mut open_profile: Option = None; - let mut bar_action: Option = None; egui::ScrollArea::vertical() .id_source(scroll_id) .animated(false) .auto_shrink([false, false]) .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) .show(ui, |ui| { - let timeline = if let Some(timeline) = columns.find_timeline_mut(timeline_id) { - timeline + timeline_ui_no_scroll( + ui, + ndb, + timeline_id, + columns, + note_cache, + img_cache, + reversed, + textmode, + ) + }) + .inner +} + +#[allow(clippy::too_many_arguments)] +fn timeline_ui_no_scroll( + ui: &mut egui::Ui, + ndb: &Ndb, + timeline_id: TimelineId, + columns: &mut Columns, + note_cache: &mut NoteCache, + img_cache: &mut ImageCache, + reversed: bool, + textmode: bool, +) -> TimelineResponse { + let mut open_profile: Option = None; + let mut bar_action: Option = None; + + let timeline = if let Some(timeline) = columns.find_timeline_mut(timeline_id) { + timeline + } else { + error!("tried to render timeline in column, but timeline was missing"); + // TODO (jb55): render error when timeline is missing? + // this shouldn't happen... + return TimelineResponse::default(); + }; + + let view = timeline.current_view(); + let len = view.notes.len(); + let txn = if let Ok(txn) = Transaction::new(ndb) { + txn + } else { + warn!("failed to create transaction"); + return TimelineResponse::default(); + }; + + view.list + .clone() + .borrow_mut() + .ui_custom_layout(ui, len, |ui, start_index| { + ui.spacing_mut().item_spacing.y = 0.0; + ui.spacing_mut().item_spacing.x = 4.0; + + let ind = if reversed { + len - start_index - 1 } else { - error!("tried to render timeline in column, but timeline was missing"); - // TODO (jb55): render error when timeline is missing? - // this shouldn't happen... + start_index + }; + + let note_key = timeline.current_view().notes[ind].key; + + let note = if let Ok(note) = ndb.get_note_by_key(&txn, note_key) { + note + } else { + warn!("failed to query note {:?}", note_key); return 0; }; - let view = timeline.current_view(); - let len = view.notes.len(); - let txn = if let Ok(txn) = Transaction::new(ndb) { - txn - } else { - warn!("failed to create transaction"); - return 0; - }; + ui::padding(8.0, ui, |ui| { + let resp = ui::NoteView::new(ndb, note_cache, img_cache, ¬e) + .note_previews(!textmode) + .selectable_text(false) + .options_button(true) + .show(ui); - view.list - .clone() - .borrow_mut() - .ui_custom_layout(ui, len, |ui, start_index| { - ui.spacing_mut().item_spacing.y = 0.0; - ui.spacing_mut().item_spacing.x = 4.0; + if let Some(ba) = resp.action { + bar_action = Some(ba); + } else if resp.response.clicked() { + debug!("clicked note"); + } - let ind = if reversed { - len - start_index - 1 - } else { - start_index - }; + if let Some(context) = resp.context_selection { + context.process(ui, ¬e); + } - let note_key = timeline.current_view().notes[ind].key; + if resp.clicked_profile { + info!("clicked profile"); + open_profile = Some(Pubkey::new(*note.pubkey())) + } + }); - let note = if let Ok(note) = ndb.get_note_by_key(&txn, note_key) { - note - } else { - warn!("failed to query note {:?}", note_key); - return 0; - }; - - ui::padding(8.0, ui, |ui| { - let resp = ui::NoteView::new(ndb, note_cache, img_cache, ¬e) - .note_previews(!textmode) - .selectable_text(false) - .options_button(true) - .show(ui); - - if let Some(ba) = resp.action { - bar_action = Some(ba); - } else if resp.response.clicked() { - debug!("clicked note"); - } - - if let Some(context) = resp.context_selection { - context.process(ui, ¬e); - } - - if resp.clicked_profile { - info!("clicked profile"); - open_profile = Some(Pubkey::new(*note.pubkey())) - } - }); - - ui::hline(ui); - //ui.add(egui::Separator::default().spacing(0.0)); - - 1 - }); + ui::hline(ui); + //ui.add(egui::Separator::default().spacing(0.0)); 1 });