From 6645d4880f912e5d925a7ad43fd8642c8505db39 Mon Sep 17 00:00:00 2001 From: kernelkind Date: Fri, 3 Jan 2025 17:50:48 -0500 Subject: [PATCH] integrate EditProfileView Signed-off-by: kernelkind --- crates/notedeck_columns/src/nav.rs | 43 ++++++++++++++++++- crates/notedeck_columns/src/profile.rs | 30 +++++++++++-- crates/notedeck_columns/src/route.rs | 3 ++ crates/notedeck_columns/src/storage/decks.rs | 14 ++++++ crates/notedeck_columns/src/timeline/route.rs | 20 +++++++-- .../notedeck_columns/src/ui/column/header.rs | 3 ++ crates/notedeck_columns/src/ui/profile/mod.rs | 39 +++++++++++++---- 7 files changed, 137 insertions(+), 15 deletions(-) diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs index a9746c7c..d96702ef 100644 --- a/crates/notedeck_columns/src/nav.rs +++ b/crates/notedeck_columns/src/nav.rs @@ -6,7 +6,8 @@ use crate::{ deck_state::DeckState, decks::{Deck, DecksAction, DecksCache}, notes_holder::NotesHolder, - profile::Profile, + profile::{Profile, ProfileAction, SaveProfileChanges}, + profile_state::ProfileState, relay_pool_manager::RelayPoolManager, route::Route, thread::Thread, @@ -21,6 +22,7 @@ use crate::{ configure_deck::ConfigureDeckView, edit_deck::{EditDeckResponse, EditDeckView}, note::{PostAction, PostType}, + profile::EditProfileView, support::SupportView, RelayView, View, }, @@ -39,6 +41,7 @@ pub enum RenderNavAction { RemoveColumn, PostAction(PostAction), NoteAction(NoteAction), + ProfileAction(ProfileAction), SwitchingAction(SwitchingAction), } @@ -168,6 +171,15 @@ impl RenderNavResponse { RenderNavAction::SwitchingAction(switching_action) => { switching_occured = switching_action.process(&mut app.decks_cache, ctx); } + RenderNavAction::ProfileAction(profile_action) => { + profile_action.process( + ctx.ndb, + ctx.pool, + get_active_columns_mut(ctx.accounts, &mut app.decks_cache) + .column_mut(col) + .router_mut(), + ); + } } } @@ -368,6 +380,35 @@ fn render_nav_body( action } + Route::EditProfile(pubkey) => { + let mut action = None; + if let Some(kp) = ctx.accounts.get_full(pubkey.bytes()) { + let state = app + .view_state + .pubkey_to_profile_state + .entry(*kp.pubkey) + .or_insert_with(|| { + let txn = Transaction::new(ctx.ndb).expect("txn"); + if let Ok(record) = ctx.ndb.get_profile_by_pubkey(&txn, kp.pubkey.bytes()) { + ProfileState::from_profile(&record) + } else { + ProfileState::default() + } + }); + if EditProfileView::new(state, ctx.img_cache).ui(ui) { + if let Some(taken_state) = + app.view_state.pubkey_to_profile_state.remove(kp.pubkey) + { + action = Some(RenderNavAction::ProfileAction(ProfileAction::SaveChanges( + SaveProfileChanges::new(kp.to_full(), taken_state), + ))) + } + } + } else { + error!("Pubkey in EditProfile route did not have an nsec attached in Accounts"); + } + action + } } } diff --git a/crates/notedeck_columns/src/profile.rs b/crates/notedeck_columns/src/profile.rs index abcb7d3b..03fbc0be 100644 --- a/crates/notedeck_columns/src/profile.rs +++ b/crates/notedeck_columns/src/profile.rs @@ -1,12 +1,16 @@ -use enostr::{Filter, FullKeypair, Pubkey}; -use nostrdb::{FilterBuilder, Ndb, Note, NoteBuilder, ProfileRecord, Transaction}; +use enostr::{Filter, FullKeypair, Pubkey, RelayPool}; +use nostrdb::{ + FilterBuilder, Ndb, Note, NoteBuildOptions, NoteBuilder, ProfileRecord, Transaction, +}; use notedeck::{filter::default_limit, FilterState, MuteFun, NoteCache, NoteRef}; +use tracing::info; use crate::{ multi_subscriber::MultiSubscriber, notes_holder::NotesHolder, profile_state::ProfileState, + route::{Route, Router}, timeline::{copy_notes_into_timeline, PubkeySource, Timeline, TimelineKind, TimelineTab}, }; @@ -171,7 +175,7 @@ impl SaveProfileChanges { add_client_tag(NoteBuilder::new()) .kind(0) .content(&self.state.to_json()) - .sign(sec) + .options(NoteBuildOptions::default().created_at(true).sign(sec)) .build() .expect("should build") } @@ -188,3 +192,23 @@ pub enum ProfileAction { Edit(FullKeypair), SaveChanges(SaveProfileChanges), } + +impl ProfileAction { + pub fn process(&self, ndb: &Ndb, pool: &mut RelayPool, router: &mut Router) { + match self { + ProfileAction::Edit(kp) => { + router.route_to(Route::EditProfile(kp.pubkey)); + } + ProfileAction::SaveChanges(changes) => { + let raw_msg = format!("[\"EVENT\",{}]", changes.to_note().json().unwrap()); + + let _ = ndb.process_client_event(raw_msg.as_str()); + + info!("sending {}", raw_msg); + pool.send(&enostr::ClientMessage::raw(raw_msg)); + + router.go_back(); + } + } + } +} diff --git a/crates/notedeck_columns/src/route.rs b/crates/notedeck_columns/src/route.rs index 2fad3c27..647aa66b 100644 --- a/crates/notedeck_columns/src/route.rs +++ b/crates/notedeck_columns/src/route.rs @@ -16,6 +16,7 @@ pub enum Route { Relays, ComposeNote, AddColumn(AddColumnRoute), + EditProfile(Pubkey), Support, NewDeck, EditDeck(usize), @@ -104,6 +105,7 @@ impl Route { Route::Support => ColumnTitle::simple("Damus Support"), Route::NewDeck => ColumnTitle::simple("Add Deck"), Route::EditDeck(_) => ColumnTitle::simple("Edit Deck"), + Route::EditProfile(_) => ColumnTitle::simple("Edit Profile"), } } } @@ -215,6 +217,7 @@ impl fmt::Display for Route { Route::Support => write!(f, "Support"), Route::NewDeck => write!(f, "Add Deck"), Route::EditDeck(_) => write!(f, "Edit Deck"), + Route::EditProfile(_) => write!(f, "Edit Profile"), } } } diff --git a/crates/notedeck_columns/src/storage/decks.rs b/crates/notedeck_columns/src/storage/decks.rs index b928692b..5bd79e5b 100644 --- a/crates/notedeck_columns/src/storage/decks.rs +++ b/crates/notedeck_columns/src/storage/decks.rs @@ -541,6 +541,11 @@ fn serialize_route(route: &Route, columns: &Columns) -> Option { selections.push(Selection::Keyword(Keyword::Edit)); selections.push(Selection::Payload(index.to_string())); } + Route::EditProfile(pubkey) => { + selections.push(Selection::Keyword(Keyword::Profile)); + selections.push(Selection::Keyword(Keyword::Edit)); + selections.push(Selection::Payload(pubkey.hex())); + } } if selections.is_empty() { @@ -649,6 +654,15 @@ fn selections_to_route(selections: Vec) -> Option Some(CleanIntermediaryRoute::ToTimeline( TimelineKind::profile(PubkeySource::DeckAuthor), )), + Selection::Keyword(Keyword::Edit) => { + if let Selection::Payload(hex) = selections.get(2)? { + Some(CleanIntermediaryRoute::ToRoute(Route::EditProfile( + Pubkey::from_hex(hex.as_str()).ok()?, + ))) + } else { + None + } + } _ => None, }, Selection::Keyword(Keyword::Universe) => { diff --git a/crates/notedeck_columns/src/timeline/route.rs b/crates/notedeck_columns/src/timeline/route.rs index 50485892..851d19d5 100644 --- a/crates/notedeck_columns/src/timeline/route.rs +++ b/crates/notedeck_columns/src/timeline/route.rs @@ -3,7 +3,7 @@ use crate::{ draft::Drafts, nav::RenderNavAction, notes_holder::NotesHolderStorage, - profile::Profile, + profile::{Profile, ProfileAction}, thread::Thread, timeline::{TimelineId, TimelineKind}, ui::{ @@ -117,6 +117,7 @@ pub fn render_timeline_route( TimelineRoute::Profile(pubkey) => render_profile_route( &pubkey, + accounts, ndb, profiles, img_cache, @@ -155,6 +156,7 @@ pub fn render_timeline_route( #[allow(clippy::too_many_arguments)] pub fn render_profile_route( pubkey: &Pubkey, + accounts: &Accounts, ndb: &Ndb, profiles: &mut NotesHolderStorage, img_cache: &mut ImageCache, @@ -163,8 +165,9 @@ pub fn render_profile_route( ui: &mut egui::Ui, is_muted: &MuteFun, ) -> Option { - let note_action = ProfileView::new( + let action = ProfileView::new( pubkey, + accounts, col, profiles, ndb, @@ -174,5 +177,16 @@ pub fn render_profile_route( ) .ui(ui, is_muted); - note_action.map(RenderNavAction::NoteAction) + if let Some(action) = action { + match action { + ui::profile::ProfileViewAction::EditProfile => accounts + .get_full(pubkey.bytes()) + .map(|kp| RenderNavAction::ProfileAction(ProfileAction::Edit(kp.to_full()))), + ui::profile::ProfileViewAction::Note(note_action) => { + Some(RenderNavAction::NoteAction(note_action)) + } + } + } else { + None + } } diff --git a/crates/notedeck_columns/src/ui/column/header.rs b/crates/notedeck_columns/src/ui/column/header.rs index 69b0bda4..8b61419f 100644 --- a/crates/notedeck_columns/src/ui/column/header.rs +++ b/crates/notedeck_columns/src/ui/column/header.rs @@ -256,6 +256,9 @@ impl<'a> NavTitle<'a> { Route::Relays => {} Route::NewDeck => {} Route::EditDeck(_) => {} + Route::EditProfile(pubkey) => { + self.show_profile(ui, pubkey, pfp_size); + } } } diff --git a/crates/notedeck_columns/src/ui/profile/mod.rs b/crates/notedeck_columns/src/ui/profile/mod.rs index bb52f732..001f66ed 100644 --- a/crates/notedeck_columns/src/ui/profile/mod.rs +++ b/crates/notedeck_columns/src/ui/profile/mod.rs @@ -18,10 +18,11 @@ use tracing::error; use crate::{actionbar::NoteAction, notes_holder::NotesHolderStorage, profile::Profile}; use super::timeline::{tabs_ui, TimelineTabView}; -use notedeck::{ImageCache, MuteFun, NoteCache, NotedeckTextStyle}; +use notedeck::{Accounts, ImageCache, MuteFun, NoteCache, NotedeckTextStyle}; pub struct ProfileView<'a> { pubkey: &'a Pubkey, + accounts: &'a Accounts, col_id: usize, profiles: &'a mut NotesHolderStorage, note_options: NoteOptions, @@ -30,9 +31,16 @@ pub struct ProfileView<'a> { img_cache: &'a mut ImageCache, } +pub enum ProfileViewAction { + EditProfile, + Note(NoteAction), +} + impl<'a> ProfileView<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( pubkey: &'a Pubkey, + accounts: &'a Accounts, col_id: usize, profiles: &'a mut NotesHolderStorage, ndb: &'a Ndb, @@ -42,6 +50,7 @@ impl<'a> ProfileView<'a> { ) -> Self { ProfileView { pubkey, + accounts, col_id, profiles, ndb, @@ -51,15 +60,18 @@ impl<'a> ProfileView<'a> { } } - pub fn ui(&mut self, ui: &mut egui::Ui, is_muted: &MuteFun) -> Option { + pub fn ui(&mut self, ui: &mut egui::Ui, is_muted: &MuteFun) -> Option { let scroll_id = egui::Id::new(("profile_scroll", self.col_id, self.pubkey)); ScrollArea::vertical() .id_salt(scroll_id) .show(ui, |ui| { + let mut action = None; let txn = Transaction::new(self.ndb).expect("txn"); if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, self.pubkey.bytes()) { - self.profile_body(ui, profile); + if self.profile_body(ui, profile) { + action = Some(ProfileViewAction::EditProfile); + } } let profile = self .profiles @@ -82,7 +94,7 @@ impl<'a> ProfileView<'a> { let reversed = false; - TimelineTabView::new( + if let Some(note_action) = TimelineTabView::new( profile.timeline.current_view(), reversed, self.note_options, @@ -92,11 +104,16 @@ impl<'a> ProfileView<'a> { self.img_cache, ) .show(ui) + { + action = Some(ProfileViewAction::Note(note_action)); + } + action }) .inner } - fn profile_body(&mut self, ui: &mut egui::Ui, profile: ProfileRecord<'_>) { + fn profile_body(&mut self, ui: &mut egui::Ui, profile: ProfileRecord<'_>) -> bool { + let mut action = false; ui.vertical(|ui| { banner( ui, @@ -129,9 +146,13 @@ impl<'a> ProfileView<'a> { }); } - ui.with_layout(Layout::right_to_left(egui::Align::Max), |ui| { - ui.add(edit_profile_button()) - }); + if self.accounts.contains_full_kp(self.pubkey) { + ui.with_layout(Layout::right_to_left(egui::Align::Max), |ui| { + if ui.add(edit_profile_button()).clicked() { + action = true; + } + }); + } }); ui.add_space(18.0); @@ -163,6 +184,8 @@ impl<'a> ProfileView<'a> { }); }); }); + + action } }