From 482a3cb818950ad37ca540b44dea37246a8c7635 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Tue, 17 Dec 2024 10:18:19 -0800 Subject: [PATCH] columns: move from Cow<'static, str> to ColumnTitle<'a> This further deliminates our column titles to those that are simple, and to those that require additional information from the database. This allows us to avoid creating many transactions pointlessly if we don't need to. Changelog-Changed: Show usernames in user columns --- crates/notedeck_columns/src/route.rs | 47 ++++++------ crates/notedeck_columns/src/timeline/kind.rs | 73 +++++++++++++++++-- crates/notedeck_columns/src/timeline/mod.rs | 2 +- .../notedeck_columns/src/ui/column/header.rs | 59 ++++++++++----- 4 files changed, 131 insertions(+), 50 deletions(-) diff --git a/crates/notedeck_columns/src/route.rs b/crates/notedeck_columns/src/route.rs index 159426cd..274a0aba 100644 --- a/crates/notedeck_columns/src/route.rs +++ b/crates/notedeck_columns/src/route.rs @@ -1,13 +1,10 @@ use enostr::{NoteId, Pubkey}; -use std::{ - borrow::Cow, - fmt::{self}, -}; +use std::fmt::{self}; use crate::{ accounts::AccountsRoute, column::Columns, - timeline::{TimelineId, TimelineRoute}, + timeline::{kind::ColumnTitle, TimelineId, TimelineRoute}, ui::add_column::AddColumnRoute, }; @@ -65,7 +62,7 @@ impl Route { Route::Accounts(AccountsRoute::AddAccount) } - pub fn title(&self, columns: &Columns) -> Cow<'static, str> { + pub fn title<'a>(&self, columns: &'a Columns) -> ColumnTitle<'a> { match self { Route::Timeline(tlr) => match tlr { TimelineRoute::Timeline(id) => { @@ -74,36 +71,38 @@ impl Route { .expect("expected to find timeline"); timeline.kind.to_title() } - TimelineRoute::Thread(_id) => Cow::Borrowed("Thread"), - TimelineRoute::Reply(_id) => Cow::Borrowed("Reply"), - TimelineRoute::Quote(_id) => Cow::Borrowed("Quote"), - TimelineRoute::Profile(_pubkey) => Cow::Borrowed("Profile"), + TimelineRoute::Thread(_id) => ColumnTitle::simple("Thread"), + TimelineRoute::Reply(_id) => ColumnTitle::simple("Reply"), + TimelineRoute::Quote(_id) => ColumnTitle::simple("Quote"), + TimelineRoute::Profile(_pubkey) => ColumnTitle::simple("Profile"), }, - Route::Relays => Cow::Borrowed("Relays"), + Route::Relays => ColumnTitle::simple("Relays"), Route::Accounts(amr) => match amr { - AccountsRoute::Accounts => Cow::Borrowed("Accounts"), - AccountsRoute::AddAccount => Cow::Borrowed("Add Account"), + AccountsRoute::Accounts => ColumnTitle::simple("Accounts"), + AccountsRoute::AddAccount => ColumnTitle::simple("Add Account"), }, - Route::ComposeNote => Cow::Borrowed("Compose Note"), + Route::ComposeNote => ColumnTitle::simple("Compose Note"), Route::AddColumn(c) => match c { - AddColumnRoute::Base => Cow::Borrowed("Add Column"), - AddColumnRoute::UndecidedNotification => Cow::Borrowed("Add Notifications Column"), - AddColumnRoute::ExternalNotification => { - Cow::Borrowed("Add External Notifications Column") + AddColumnRoute::Base => ColumnTitle::simple("Add Column"), + AddColumnRoute::UndecidedNotification => { + ColumnTitle::simple("Add Notifications Column") } - AddColumnRoute::Hashtag => Cow::Borrowed("Add Hashtag Column"), + AddColumnRoute::ExternalNotification => { + ColumnTitle::simple("Add External Notifications Column") + } + AddColumnRoute::Hashtag => ColumnTitle::simple("Add Hashtag Column"), AddColumnRoute::UndecidedIndividual => { - Cow::Borrowed("Subscribe to someone's notes") + ColumnTitle::simple("Subscribe to someone's notes") } AddColumnRoute::ExternalIndividual => { - Cow::Borrowed("Subscribe to someone else's notes") + ColumnTitle::simple("Subscribe to someone else's notes") } }, - Route::Support => Cow::Borrowed("Damus Support"), - Route::NewDeck => Cow::Borrowed("Add Deck"), - Route::EditDeck(_) => Cow::Borrowed("Edit Deck"), + Route::Support => ColumnTitle::simple("Damus Support"), + Route::NewDeck => ColumnTitle::simple("Add Deck"), + Route::EditDeck(_) => ColumnTitle::simple("Edit Deck"), } } } diff --git a/crates/notedeck_columns/src/timeline/kind.rs b/crates/notedeck_columns/src/timeline/kind.rs index 49178d3f..30acec4c 100644 --- a/crates/notedeck_columns/src/timeline/kind.rs +++ b/crates/notedeck_columns/src/timeline/kind.rs @@ -194,16 +194,75 @@ impl TimelineKind { } } - pub fn to_title(&self) -> Cow<'static, str> { + pub fn to_title(&self) -> ColumnTitle<'_> { match self { TimelineKind::List(list_kind) => match list_kind { - ListKind::Contact(_pubkey_source) => Cow::Borrowed("Contacts"), + ListKind::Contact(_pubkey_source) => ColumnTitle::simple("Contacts"), }, - TimelineKind::Notifications(_pubkey_source) => Cow::Borrowed("Notifications"), - TimelineKind::Profile(_pubkey_source) => Cow::Borrowed("Notes"), - TimelineKind::Universe => Cow::Borrowed("Universe"), - TimelineKind::Generic => Cow::Borrowed("Custom"), - TimelineKind::Hashtag(hashtag) => Cow::Owned(format!("#{}", hashtag)), + TimelineKind::Notifications(_pubkey_source) => ColumnTitle::simple("Notifications"), + TimelineKind::Profile(_pubkey_source) => ColumnTitle::needs_db(self), + TimelineKind::Universe => ColumnTitle::simple("Universe"), + TimelineKind::Generic => ColumnTitle::simple("Custom"), + TimelineKind::Hashtag(hashtag) => ColumnTitle::formatted(format!("#{}", hashtag)), } } } + +#[derive(Debug)] +pub struct TitleNeedsDb<'a> { + kind: &'a TimelineKind, +} + +impl<'a> TitleNeedsDb<'a> { + pub fn new(kind: &'a TimelineKind) -> Self { + TitleNeedsDb { kind } + } + + pub fn title<'txn>( + &self, + txn: &'txn Transaction, + ndb: &Ndb, + deck_author: Option<&Pubkey>, + ) -> &'txn str { + if let TimelineKind::Profile(pubkey_source) = self.kind { + if let Some(deck_author) = deck_author { + let pubkey = pubkey_source.to_pubkey(deck_author); + let profile = ndb.get_profile_by_pubkey(txn, pubkey); + let m_name = profile + .ok() + .as_ref() + .and_then(|p| crate::profile::get_profile_name(p)) + .map(|display_name| display_name.username()); + + m_name.unwrap_or("Profile") + } else { + // why would be there be no deck author? weird + "nostrich" + } + } else { + "Unknown" + } + } +} + +/// This saves us from having to construct a transaction if we don't need to +/// for a particular column when rendering the title +#[derive(Debug)] +pub enum ColumnTitle<'a> { + Simple(Cow<'static, str>), + NeedsDb(TitleNeedsDb<'a>), +} + +impl<'a> ColumnTitle<'a> { + pub fn simple(title: &'static str) -> Self { + Self::Simple(Cow::Borrowed(title)) + } + + pub fn formatted(title: String) -> Self { + Self::Simple(Cow::Owned(title)) + } + + pub fn needs_db(kind: &'a TimelineKind) -> ColumnTitle<'a> { + Self::NeedsDb(TitleNeedsDb::new(kind)) + } +} diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs index 23c27aea..e60d02c9 100644 --- a/crates/notedeck_columns/src/timeline/mod.rs +++ b/crates/notedeck_columns/src/timeline/mod.rs @@ -26,7 +26,7 @@ use tracing::{debug, error, info, warn}; pub mod kind; pub mod route; -pub use kind::{PubkeySource, TimelineKind}; +pub use kind::{ColumnTitle, PubkeySource, TimelineKind}; pub use route::TimelineRoute; #[derive(Debug, Hash, Copy, Clone, Eq, PartialEq)] diff --git a/crates/notedeck_columns/src/ui/column/header.rs b/crates/notedeck_columns/src/ui/column/header.rs index 9ca66c98..f05987e5 100644 --- a/crates/notedeck_columns/src/ui/column/header.rs +++ b/crates/notedeck_columns/src/ui/column/header.rs @@ -2,7 +2,7 @@ use crate::{ column::Columns, nav::RenderNavAction, route::Route, - timeline::{TimelineId, TimelineRoute}, + timeline::{ColumnTitle, TimelineId, TimelineRoute}, ui::{ self, anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE}, @@ -105,17 +105,29 @@ impl<'a> NavTitle<'a> { // not it looks cool self.title_pfp(ui, prev, 32.0); - let back_label = ui.add( - egui::Label::new( - RichText::new(prev.title(self.columns).to_string()) - .color(color) - .text_style(NotedeckTextStyle::Body.text_style()), - ) - .selectable(false) - .sense(egui::Sense::click()), - ); + let column_title = prev.title(self.columns); - back_label.union(chev_resp) + let back_resp = match &column_title { + ColumnTitle::Simple(title) => ui.add(Self::back_label(title, color)), + + ColumnTitle::NeedsDb(need_db) => { + let txn = Transaction::new(self.ndb).unwrap(); + let title = need_db.title(&txn, self.ndb, self.deck_author); + ui.add(Self::back_label(title, color)) + } + }; + + back_resp.union(chev_resp) + } + + fn back_label(title: &str, color: egui::Color32) -> egui::Label { + egui::Label::new( + RichText::new(title.to_string()) + .color(color) + .text_style(NotedeckTextStyle::Body.text_style()), + ) + .selectable(false) + .sense(egui::Sense::click()) } fn delete_column_button(&self, ui: &mut egui::Ui, icon_width: f32) -> egui::Response { @@ -209,14 +221,25 @@ impl<'a> NavTitle<'a> { } } + fn title_label_value(title: &str) -> egui::Label { + egui::Label::new(RichText::new(title).text_style(NotedeckTextStyle::Body.text_style())) + .selectable(false) + } + fn title_label(&self, ui: &mut egui::Ui, top: &Route) { - ui.add( - egui::Label::new( - RichText::new(top.title(self.columns)) - .text_style(NotedeckTextStyle::Body.text_style()), - ) - .selectable(false), - ); + let column_title = top.title(self.columns); + + match &column_title { + ColumnTitle::Simple(title) => { + ui.add(Self::title_label_value(title)); + } + + ColumnTitle::NeedsDb(need_db) => { + let txn = Transaction::new(self.ndb).unwrap(); + let title = need_db.title(&txn, self.ndb, self.deck_author); + ui.add(Self::title_label_value(title)); + } + }; } fn title(