From 7dfe846535ba861facdd4b744c7e81d7e12fc65a Mon Sep 17 00:00:00 2001 From: kernelkind Date: Wed, 2 Oct 2024 19:39:05 -0400 Subject: [PATCH] title bar add title bar to columns with title specific to the column type. also add column deletion button Signed-off-by: kernelkind --- assets/icons/column_delete_icon_4x.png | Bin 0 -> 806 bytes src/app.rs | 19 +- src/column.rs | 124 +++++++++---- src/fonts.rs | 15 +- src/nav.rs | 241 ++++++++++++++++++------- src/route.rs | 51 ++++++ src/timeline/kind.rs | 30 +++ src/timeline/mod.rs | 6 +- src/timeline/route.rs | 6 +- src/ui/anim.rs | 21 +++ src/ui/profile/preview.rs | 26 +++ src/ui/side_panel.rs | 4 +- 12 files changed, 426 insertions(+), 117 deletions(-) create mode 100644 assets/icons/column_delete_icon_4x.png diff --git a/assets/icons/column_delete_icon_4x.png b/assets/icons/column_delete_icon_4x.png new file mode 100644 index 0000000000000000000000000000000000000000..ae8860bf6977bb308e8378d73bc9488e971957df GIT binary patch literal 806 zcmV+>1KIqEP)@~0drDELIAGL9O(c600d`2O+f$vv5yPK~#7F?VGW3 z)G!c+e>2oKp}&Iq8Yn5DrJ_hdohN`70FxIWX(=*8M?pcAf`%(7uB1vEF>^haynxu9 zC^L+sJ+!i9*|ENvots(9mi%iat!y7qsZ=VJ^jK@(ll`&%;cI-vl;8&2^t8x8gA8!5 z(a!w ztmCEe<`Fdy5d1mT@zUt>2%BylFU{UOB6u%A7kNWJzS;DQ>=*-Nr(_%S{w2@rf= z+96;BztmIDgky(+K0bE^U7ZFBa?b&p338#lM(^>@5Qh=6NkBt`%u_zD7z5Ohr(~CQIDU{lM*@=mWyUq?71=+Nwf1=zu<0bAl k**6;RYg8(gN~J%40Ttn9aZu6DwEzGB07*qoM6N<$f?J$o*8l(j literal 0 HcmV?d00001 diff --git a/src/app.rs b/src/app.rs index 0753a96..ce3c234 100644 --- a/src/app.rs +++ b/src/app.rs @@ -247,7 +247,7 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> { if let Err(err) = Timeline::poll_notes_into_view( timeline_ind, - &mut damus.columns.timelines, + damus.columns.timelines_mut(), &damus.ndb, &txn, &mut damus.unknown_ids, @@ -490,6 +490,8 @@ fn update_damus(damus: &mut Damus, ctx: &egui::Context) { if let Err(err) = try_process_event(damus, ctx) { error!("error processing event: {}", err); } + + damus.columns.attempt_perform_deletion_request(); } fn process_event(damus: &mut Damus, _subid: &str, event: &str) { @@ -952,7 +954,7 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) { puffin::profile_function!(); let screen_size = ctx.screen_rect().width(); - let calc_panel_width = (screen_size / app.columns.columns().len() as f32) - 30.0; + let calc_panel_width = (screen_size / app.columns.num_columns() as f32) - 30.0; let min_width = 320.0; let need_scroll = calc_panel_width < min_width; let panel_sizes = if need_scroll { @@ -965,18 +967,18 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) { ui.spacing_mut().item_spacing.x = 0.0; if need_scroll { egui::ScrollArea::horizontal().show(ui, |ui| { - timelines_view(ui, panel_sizes, app, app.columns.columns().len()); + timelines_view(ui, panel_sizes, app); }); } else { - timelines_view(ui, panel_sizes, app, app.columns.columns().len()); + timelines_view(ui, panel_sizes, app); } }); } -fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, columns: usize) { +fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) { StripBuilder::new(ui) .size(Size::exact(ui::side_panel::SIDE_PANEL_WIDTH)) - .sizes(sizes, columns) + .sizes(sizes, app.columns.num_columns()) .clip(true) .horizontal(|mut strip| { strip.cell(|ui| { @@ -1000,11 +1002,10 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, columns: usiz ); }); - let n_cols = app.columns.columns().len(); - for column_ind in 0..n_cols { + for col_index in 0..app.columns.num_columns() { strip.cell(|ui| { let rect = ui.available_rect_before_wrap(); - nav::render_nav(column_ind, app, ui); + nav::render_nav(col_index, app, ui); // vertical line ui.painter().vline( diff --git a/src/column.rs b/src/column.rs index b5f971c..1011758 100644 --- a/src/column.rs +++ b/src/column.rs @@ -1,7 +1,9 @@ use crate::route::{Route, Router}; use crate::timeline::{Timeline, TimelineId}; +use indexmap::IndexMap; use std::iter::Iterator; -use tracing::warn; +use std::sync::atomic::{AtomicU32, Ordering}; +use tracing::{info, warn}; pub struct Column { router: Router, @@ -25,16 +27,18 @@ impl Column { #[derive(Default)] pub struct Columns { /// Columns are simply routers into settings, timelines, etc - columns: Vec, + columns: IndexMap, /// Timeline state is not tied to routing logic separately, so that /// different columns can navigate to and from settings to timelines, /// etc. - pub timelines: Vec, + pub timelines: IndexMap, /// The selected column for key navigation selected: i32, + should_delete_column_at_index: Option, } +static UIDS: AtomicU32 = AtomicU32::new(0); impl Columns { pub fn new() -> Self { @@ -42,25 +46,38 @@ impl Columns { } pub fn add_timeline(&mut self, timeline: Timeline) { + let id = Self::get_new_id(); let routes = vec![Route::timeline(timeline.id)]; - self.timelines.push(timeline); - self.columns.push(Column::new(routes)) - } - - pub fn add_timeline_to_column(&mut self, col: usize, timeline: Timeline) -> bool { - if let Some(column) = self.columns.get_mut(col) { - column - .router_mut() - .route_to_replaced(Route::timeline(timeline.id)); - self.timelines.push(timeline); - true - } else { - false - } + self.timelines.insert(id, timeline); + self.columns.insert(id, Column::new(routes)); + } + + pub fn add_timeline_to_column(&mut self, col: usize, timeline: Timeline) { + let col_id = self.get_column_id_at_index(col); + self.column_mut(col) + .router_mut() + .route_to_replaced(Route::timeline(timeline.id)); + self.timelines.insert(col_id, timeline); } pub fn new_column_picker(&mut self) { - self.columns.push(Column::new(vec![Route::AddColumn])); + self.add_column(Column::new(vec![Route::AddColumn])); + } + + fn get_new_id() -> u32 { + UIDS.fetch_add(1, Ordering::Relaxed) + } + + pub fn add_column(&mut self, column: Column) { + self.columns.insert(Self::get_new_id(), column); + } + + pub fn columns_mut(&mut self) -> Vec<&mut Column> { + self.columns.values_mut().collect() + } + + pub fn num_columns(&self) -> usize { + self.columns.len() } // Get the first router in the columns if there are columns present. @@ -70,49 +87,66 @@ impl Columns { self.new_column_picker(); } self.columns - .get_mut(0) + .get_index_mut(0) .expect("There should be at least one column") + .1 .router_mut() } - pub fn columns_mut(&mut self) -> &mut Vec { - &mut self.columns - } - pub fn timeline_mut(&mut self, timeline_ind: usize) -> &mut Timeline { - &mut self.timelines[timeline_ind] + self.timelines + .get_index_mut(timeline_ind) + .expect("expected index to be in bounds") + .1 } pub fn column(&self, ind: usize) -> &Column { - &self.columns()[ind] + self.columns + .get_index(ind) + .expect("Expected index to be in bounds") + .1 + } + + pub fn columns(&self) -> Vec<&Column> { + self.columns.values().collect() } - pub fn columns(&self) -> &Vec { - &self.columns + pub fn get_column_id_at_index(&self, ind: usize) -> u32 { + *self + .columns + .get_index(ind) + .expect("expected index to be within bounds") + .0 } pub fn selected(&mut self) -> &mut Column { - &mut self.columns[self.selected as usize] + self.columns + .get_index_mut(self.selected as usize) + .expect("Expected selected index to be in bounds") + .1 } - pub fn timelines_mut(&mut self) -> &mut Vec { - &mut self.timelines + pub fn timelines_mut(&mut self) -> Vec<&mut Timeline> { + self.timelines.values_mut().collect() } - pub fn timelines(&self) -> &Vec { - &self.timelines + pub fn timelines(&self) -> Vec<&Timeline> { + self.timelines.values().collect() } pub fn find_timeline_mut(&mut self, id: TimelineId) -> Option<&mut Timeline> { - self.timelines_mut().iter_mut().find(|tl| tl.id == id) + self.timelines_mut().into_iter().find(|tl| tl.id == id) } pub fn find_timeline(&self, id: TimelineId) -> Option<&Timeline> { - self.timelines().iter().find(|tl| tl.id == id) + self.timelines().into_iter().find(|tl| tl.id == id) } pub fn column_mut(&mut self, ind: usize) -> &mut Column { - &mut self.columns[ind] + self.columns + .get_index_mut(ind) + .expect("Expected index to be in bounds") + .1 } pub fn select_down(&mut self) { @@ -136,4 +170,24 @@ impl Columns { } self.selected += 1; } + + pub fn request_deletion_at_index(&mut self, index: usize) { + info!("sent deletion request"); + self.should_delete_column_at_index = Some(index); + } + + pub fn attempt_perform_deletion_request(&mut self) { + if let Some(index) = self.should_delete_column_at_index { + if let Some((key, _)) = self.columns.get_index_mut(index) { + self.timelines.shift_remove(key); + } + + self.columns.shift_remove_index(index); + self.should_delete_column_at_index = None; + + if self.columns.is_empty() { + self.new_column_picker(); + } + } + } } diff --git a/src/fonts.rs b/src/fonts.rs index 6ec5174..220e01d 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -4,12 +4,13 @@ use tracing::debug; pub enum NamedFontFamily { Medium, + Bold, } impl NamedFontFamily { pub fn as_str(&mut self) -> &'static str { match self { - //Self::Bold => "bold", + Self::Bold => "bold", Self::Medium => "medium", } } @@ -43,7 +44,7 @@ pub fn setup_fonts(ctx: &egui::Context) { "DejaVuSans".to_owned(), FontData::from_static(include_bytes!("../assets/fonts/DejaVuSansSansEmoji.ttf")), ); - /* + font_data.insert( "OnestBold".to_owned(), FontData::from_static(include_bytes!( @@ -51,6 +52,7 @@ pub fn setup_fonts(ctx: &egui::Context) { )), ); + /* font_data.insert( "DejaVuSansBold".to_owned(), FontData::from_static(include_bytes!( @@ -119,7 +121,10 @@ pub fn setup_fonts(ctx: &egui::Context) { medium.extend(base_fonts.clone()); let mut mono = vec!["Inconsolata".to_owned()]; - mono.extend(base_fonts); + mono.extend(base_fonts.clone()); + + let mut bold = vec!["OnestBold".to_owned()]; + bold.extend(base_fonts); families.insert(egui::FontFamily::Proportional, proportional); families.insert(egui::FontFamily::Monospace, mono); @@ -127,6 +132,10 @@ pub fn setup_fonts(ctx: &egui::Context) { egui::FontFamily::Name(NamedFontFamily::Medium.as_str().into()), medium, ); + families.insert( + egui::FontFamily::Name(NamedFontFamily::Bold.as_str().into()), + bold, + ); debug!("fonts: {:?}", families); diff --git a/src/nav.rs b/src/nav.rs index 0702efb..1c5629a 100644 --- a/src/nav.rs +++ b/src/nav.rs @@ -1,5 +1,7 @@ use crate::{ account_manager::render_accounts_route, + app_style::{desktop_font_size, NotedeckTextStyle}, + fonts::NamedFontFamily, relay_pool_manager::RelayPoolManager, route::Route, thread::thread_unsubscribe, @@ -7,91 +9,105 @@ use crate::{ ui::{ self, add_column::{AddColumnResponse, AddColumnView}, + anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE}, note::PostAction, RelayView, View, }, Damus, }; +use egui::{pos2, Color32, InnerResponse}; use egui_nav::{Nav, NavAction}; pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) { + let col_id = app.columns.get_column_id_at_index(col); // TODO(jb55): clean up this router_mut mess by using Router in egui-nav directly - let nav_response = Nav::new(app.columns().column(col).router().routes().clone()) + let routes = app + .columns() + .column(col) + .router() + .routes() + .iter() + .map(|r| r.get_titled_route(&app.columns, &app.ndb)) + .collect(); + let nav_response = Nav::new(routes) .navigating(app.columns_mut().column_mut(col).router_mut().navigating) .returning(app.columns_mut().column_mut(col).router_mut().returning) - .title(false) - .show_mut(ui, |ui, nav| match nav.top() { - Route::Timeline(tlr) => render_timeline_route( - &app.ndb, - &mut app.columns, - &mut app.pool, - &mut app.drafts, - &mut app.img_cache, - &mut app.note_cache, - &mut app.threads, - &mut app.accounts, - *tlr, - col, - app.textmode, - ui, - ), - Route::Accounts(amr) => { - render_accounts_route( - ui, + .title(48.0, title_bar) + .show_mut(col_id, ui, |ui, nav| { + let column = app.columns.column_mut(col); + match &nav.top().route { + Route::Timeline(tlr) => render_timeline_route( &app.ndb, - col, &mut app.columns, + &mut app.pool, + &mut app.drafts, &mut app.img_cache, + &mut app.note_cache, + &mut app.threads, &mut app.accounts, - &mut app.view_state.login, - *amr, - ); - None - } - Route::Relays => { - let manager = RelayPoolManager::new(app.pool_mut()); - RelayView::new(manager).ui(ui); - None - } - Route::ComposeNote => { - let kp = app.accounts.selected_or_first_nsec()?; - let draft = app.drafts.compose_mut(); + *tlr, + col, + app.textmode, + ui, + ), + Route::Accounts(amr) => { + render_accounts_route( + ui, + &app.ndb, + col, + &mut app.columns, + &mut app.img_cache, + &mut app.accounts, + &mut app.view_state.login, + *amr, + ); + None + } + Route::Relays => { + let manager = RelayPoolManager::new(app.pool_mut()); + RelayView::new(manager).ui(ui); + None + } + Route::ComposeNote => { + let kp = app.accounts.selected_or_first_nsec()?; + let draft = app.drafts.compose_mut(); - let txn = nostrdb::Transaction::new(&app.ndb).expect("txn"); - let post_response = ui::PostView::new( - &app.ndb, - draft, - crate::draft::DraftSource::Compose, - &mut app.img_cache, - &mut app.note_cache, - kp, - ) - .ui(&txn, ui); - - if let Some(action) = post_response.action { - PostAction::execute(kp, &action, &mut app.pool, draft, |np, seckey| { - np.to_note(seckey) - }); - app.columns_mut().column_mut(col).router_mut().go_back(); + let txn = nostrdb::Transaction::new(&app.ndb).expect("txn"); + let post_response = ui::PostView::new( + &app.ndb, + draft, + crate::draft::DraftSource::Compose, + &mut app.img_cache, + &mut app.note_cache, + kp, + ) + .ui(&txn, ui); + + if let Some(action) = post_response.action { + PostAction::execute(kp, &action, &mut app.pool, draft, |np, seckey| { + np.to_note(seckey) + }); + column.router_mut().go_back(); + } + + None } + Route::AddColumn => { + let resp = + AddColumnView::new(&app.ndb, app.accounts.get_selected_account()).ui(ui); - None - } - Route::AddColumn => { - let resp = AddColumnView::new(&app.ndb, app.accounts.get_selected_account()).ui(ui); - - if let Some(resp) = resp { - match resp { - AddColumnResponse::Timeline(timeline) => { - let id = timeline.id; - if app.columns_mut().add_timeline_to_column(col, timeline) { + if let Some(resp) = resp { + match resp { + AddColumnResponse::Timeline(timeline) => { + let id = timeline.id; + app.columns_mut().add_timeline_to_column(col, timeline); app.subscribe_new_timeline(id); } - } - }; + }; + } + None } - None } }); @@ -128,4 +144,103 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) { cur_router.remove_previous_route(); } } + + if let Some(title_response) = nav_response.title_response { + match title_response { + TitleResponse::RemoveColumn => { + app.columns_mut().request_deletion_at_index(col); + } + } + } +} + +fn title_bar( + ui: &mut egui::Ui, + title_name: String, + allocated_response: egui::Response, +) -> egui::InnerResponse> { + let icon_width = 32.0; + let padding = 16.0; + title(ui, title_name, allocated_response.rect, icon_width, padding); + let button_resp = delete_column_button(ui, allocated_response, icon_width, padding); + let title_response = if button_resp.clicked() { + Some(TitleResponse::RemoveColumn) + } else { + None + }; + + InnerResponse::new(title_response, button_resp) +} + +fn delete_column_button( + ui: &mut egui::Ui, + title_bar_resp: egui::Response, + icon_width: f32, + padding: f32, +) -> egui::Response { + let img_size = 16.0; + let max_size = icon_width * ICON_EXPANSION_MULTIPLE; + + let img_data = egui::include_image!("../assets/icons/column_delete_icon_4x.png"); + let img = egui::Image::new(img_data).max_width(img_size); + + let button_rect = { + let titlebar_rect = title_bar_resp.rect; + let titlebar_width = titlebar_rect.width(); + let titlebar_center = titlebar_rect.center(); + let button_center_y = titlebar_center.y; + let button_center_x = + titlebar_center.x + (titlebar_width / 2.0) - (max_size / 2.0) - padding; + egui::Rect::from_center_size( + pos2(button_center_x, button_center_y), + egui::vec2(max_size, max_size), + ) + }; + + let helper = AnimationHelper::new_from_rect(ui, "delete-column-button", button_rect); + + let cur_img_size = helper.scale_1d_pos(img_size); + + let animation_rect = helper.get_animation_rect(); + let animation_resp = helper.take_animation_response(); + if title_bar_resp.union(animation_resp.clone()).hovered() { + img.paint_at(ui, animation_rect.shrink((max_size - cur_img_size) / 2.0)); + } + + animation_resp +} + +fn title( + ui: &mut egui::Ui, + title_name: String, + titlebar_rect: egui::Rect, + icon_width: f32, + padding: f32, +) { + let painter = ui.painter_at(titlebar_rect); + + let font = egui::FontId::new( + desktop_font_size(&NotedeckTextStyle::Body), + egui::FontFamily::Name(NamedFontFamily::Bold.as_str().into()), + ); + + let max_title_width = titlebar_rect.width() - icon_width - padding * 2.; + let title_galley = + ui.fonts(|f| f.layout(title_name, font, ui.visuals().text_color(), max_title_width)); + + let pos = { + let titlebar_center = titlebar_rect.center(); + let titlebar_width = titlebar_rect.width(); + let text_height = title_galley.rect.height(); + + let galley_pos_x = titlebar_center.x - (titlebar_width / 2.) + padding; + let galley_pos_y = titlebar_center.y - (text_height / 2.); + pos2(galley_pos_x, galley_pos_y) + }; + + painter.galley(pos, title_galley, Color32::WHITE); +} + +enum TitleResponse { + RemoveColumn, } diff --git a/src/route.rs b/src/route.rs index 22828ab..9b622c9 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,9 +1,12 @@ use enostr::NoteId; +use nostrdb::Ndb; use std::fmt::{self}; use crate::{ account_manager::AccountsRoute, + column::Columns, timeline::{TimelineId, TimelineRoute}, + ui::profile::preview::get_note_users_displayname_string, }; /// App routing. These describe different places you can go inside Notedeck. @@ -16,6 +19,18 @@ pub enum Route { AddColumn, } +#[derive(Clone)] +pub struct TitledRoute { + pub route: Route, + pub title: String, +} + +impl fmt::Display for TitledRoute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.title) + } +} + impl Route { pub fn timeline(timeline_id: TimelineId) -> Self { Route::Timeline(TimelineRoute::Timeline(timeline_id)) @@ -52,6 +67,42 @@ impl Route { pub fn add_account() -> Self { Route::Accounts(AccountsRoute::AddAccount) } + + pub fn get_titled_route(&self, columns: &Columns, ndb: &Ndb) -> TitledRoute { + let title = match self { + Route::Timeline(tlr) => match tlr { + TimelineRoute::Timeline(id) => { + let timeline = columns + .find_timeline(*id) + .expect("expected to find timeline"); + timeline.kind.to_title(ndb) + } + TimelineRoute::Thread(id) => { + format!("{}'s Thread", get_note_users_displayname_string(ndb, id)) + } + TimelineRoute::Reply(id) => { + format!("{}'s Reply", get_note_users_displayname_string(ndb, id)) + } + TimelineRoute::Quote(id) => { + format!("{}'s Quote", get_note_users_displayname_string(ndb, id)) + } + }, + + Route::Relays => "Relays".to_owned(), + + Route::Accounts(amr) => match amr { + AccountsRoute::Accounts => "Accounts".to_owned(), + AccountsRoute::AddAccount => "Add Account".to_owned(), + }, + Route::ComposeNote => "Compose Note".to_owned(), + Route::AddColumn => "Add Column".to_owned(), + }; + + TitledRoute { + title, + route: *self, + } + } } // TODO: add this to egui-nav so we don't have to deal with returning diff --git a/src/timeline/kind.rs b/src/timeline/kind.rs index 016706e..e39afa1 100644 --- a/src/timeline/kind.rs +++ b/src/timeline/kind.rs @@ -2,6 +2,7 @@ use crate::error::{Error, FilterError}; use crate::filter; use crate::filter::FilterState; use crate::timeline::Timeline; +use crate::ui::profile::preview::get_profile_displayname_string; use enostr::{Filter, Pubkey}; use nostrdb::{Ndb, Transaction}; use std::fmt::Display; @@ -150,4 +151,33 @@ impl TimelineKind { } } } + + pub fn to_title(&self, ndb: &Ndb) -> String { + match self { + TimelineKind::List(list_kind) => match list_kind { + ListKind::Contact(pubkey_source) => match pubkey_source { + PubkeySource::Explicit(pubkey) => format!( + "{}'s Home Timeline", + get_profile_displayname_string(ndb, pubkey) + ), + PubkeySource::DeckAuthor => "Your Home Timeline".to_owned(), + }, + }, + TimelineKind::Notifications(pubkey_source) => match pubkey_source { + PubkeySource::DeckAuthor => "Your Notifications".to_owned(), + PubkeySource::Explicit(pk) => format!( + "{}'s Notifications", + get_profile_displayname_string(ndb, pk) + ), + }, + TimelineKind::Profile(pubkey_source) => match pubkey_source { + PubkeySource::DeckAuthor => "Your Profile".to_owned(), + PubkeySource::Explicit(pk) => { + format!("{}'s Profile", get_profile_displayname_string(ndb, pk)) + } + }, + TimelineKind::Universe => "Universe".to_owned(), + TimelineKind::Generic => "Custom Filter".to_owned(), + } + } } diff --git a/src/timeline/mod.rs b/src/timeline/mod.rs index f5365c8..e271fee 100644 --- a/src/timeline/mod.rs +++ b/src/timeline/mod.rs @@ -241,13 +241,15 @@ impl Timeline { pub fn poll_notes_into_view( timeline_idx: usize, - timelines: &mut [Timeline], + mut timelines: Vec<&mut Timeline>, ndb: &Ndb, txn: &Transaction, unknown_ids: &mut UnknownIds, note_cache: &mut NoteCache, ) -> Result<()> { - let timeline = &mut timelines[timeline_idx]; + let timeline = timelines + .get_mut(timeline_idx) + .ok_or(Error::TimelineNotFound)?; let sub = timeline.subscription.ok_or(Error::no_active_sub())?; let new_note_ids = ndb.poll_for_notes(sub, 500); diff --git a/src/timeline/route.rs b/src/timeline/route.rs index 71b9774..7c1367c 100644 --- a/src/timeline/route.rs +++ b/src/timeline/route.rs @@ -58,7 +58,8 @@ pub fn render_timeline_route( .ui(ui) { let txn = Transaction::new(ndb).expect("txn"); - let router = columns.columns_mut()[col].router_mut(); + let mut cur_column = columns.columns_mut(); + let router = cur_column[col].router_mut(); bar_action.execute_and_process_result(ndb, router, threads, note_cache, pool, &txn); } @@ -73,7 +74,8 @@ pub fn render_timeline_route( .ui(ui) { let txn = Transaction::new(ndb).expect("txn"); - let router = columns.columns_mut()[col].router_mut(); + let mut cur_column = columns.columns_mut(); + let router = cur_column[col].router_mut(); bar_action.execute_and_process_result(ndb, router, threads, note_cache, pool, &txn); } diff --git a/src/ui/anim.rs b/src/ui/anim.rs index 339e0dc..ff5bd52 100644 --- a/src/ui/anim.rs +++ b/src/ui/anim.rs @@ -60,6 +60,27 @@ impl AnimationHelper { } } + pub fn new_from_rect( + ui: &mut egui::Ui, + animation_name: impl std::hash::Hash, + animation_rect: egui::Rect, + ) -> Self { + let id = ui.id().with(animation_name); + let response = ui.allocate_rect(animation_rect, Sense::click()); + + let animation_progress = + ui.ctx() + .animate_bool_with_time(id, response.hovered(), ANIM_SPEED); + + Self { + rect: animation_rect, + center: animation_rect.center(), + response, + animation_progress, + expansion_multiple: ICON_EXPANSION_MULTIPLE, + } + } + pub fn scale_1d_pos(&self, min_object_size: f32) -> f32 { let max_object_size = min_object_size * self.expansion_multiple; diff --git a/src/ui/profile/preview.rs b/src/ui/profile/preview.rs index 25ef49e..223ae17 100644 --- a/src/ui/profile/preview.rs +++ b/src/ui/profile/preview.rs @@ -6,6 +6,7 @@ use crate::{colors, images, DisplayName}; use egui::load::TexturePoll; use egui::{Frame, RichText, Sense, Widget}; use egui_extras::Size; +use enostr::NoteId; use nostrdb::ProfileRecord; pub struct ProfilePreview<'a, 'cache> { @@ -256,3 +257,28 @@ fn about_section_widget<'a>(profile: &'a ProfileRecord<'a>) -> impl egui::Widget } } } + +fn get_display_name_as_string(profile: Option<&'_ ProfileRecord<'_>>) -> String { + let display_name = get_display_name(profile); + match display_name { + DisplayName::One(n) => n.to_string(), + DisplayName::Both { display_name, .. } => display_name.to_string(), + } +} + +pub fn get_profile_displayname_string(ndb: &nostrdb::Ndb, pk: &enostr::Pubkey) -> String { + let txn = nostrdb::Transaction::new(ndb).expect("Transaction should have worked"); + let profile = ndb.get_profile_by_pubkey(&txn, pk.bytes()).ok(); + get_display_name_as_string(profile.as_ref()) +} + +pub fn get_note_users_displayname_string(ndb: &nostrdb::Ndb, id: &NoteId) -> String { + let txn = nostrdb::Transaction::new(ndb).expect("Transaction should have worked"); + let note = ndb.get_note_by_id(&txn, id.bytes()); + let profile = if let Ok(note) = note { + ndb.get_profile_by_pubkey(&txn, note.pubkey()).ok() + } else { + None + }; + get_display_name_as_string(profile.as_ref()) +} diff --git a/src/ui/side_panel.rs b/src/ui/side_panel.rs index e676222..bb779ca 100644 --- a/src/ui/side_panel.rs +++ b/src/ui/side_panel.rs @@ -370,9 +370,7 @@ mod preview { impl DesktopSidePanelPreview { fn new() -> Self { let mut app = test_data::test_app(); - app.columns - .columns_mut() - .push(Column::new(vec![Route::accounts()])); + app.columns.add_column(Column::new(vec![Route::accounts()])); DesktopSidePanelPreview { app } } }