notedeck

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

commit 22cfaaf64a9a940cd0a07b0d3069387b4bae5174
parent e4e8d7fcf363c982c6dba14407381112f5f64afb
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 10 Jul 2025 09:09:49 -0700

Merge remote-tracking branch 'github/pr/928'

Diffstat:
Mcrates/notedeck_chrome/src/chrome.rs | 62+++++++++++++++++++++++++++++++++++++++++++-------------------
Mcrates/notedeck_columns/src/app.rs | 18++++++++++++++----
Mcrates/notedeck_columns/src/ui/add_column.rs | 14+++++++-------
Mcrates/notedeck_columns/src/ui/timeline.rs | 5++++-
Mcrates/notedeck_ui/src/app_images.rs | 22++++++++++++++++------
Mcrates/notedeck_ui/src/note/mod.rs | 9+++++++--
6 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/crates/notedeck_chrome/src/chrome.rs b/crates/notedeck_chrome/src/chrome.rs @@ -2,7 +2,7 @@ //#[cfg(target_arch = "wasm32")] //use wasm_bindgen::prelude::*; use crate::app::NotedeckApp; -use egui::{vec2, Button, Label, Layout, Rect, RichText, ThemePreference, Widget}; +use egui::{vec2, Button, Color32, Label, Layout, Rect, RichText, ThemePreference, Widget}; use egui_extras::{Size, StripBuilder}; use nostrdb::{ProfileRecord, Transaction}; use notedeck::{App, AppAction, AppContext, NotedeckTextStyle, UserAccount, WalletType}; @@ -207,8 +207,8 @@ impl Chrome { .size(Size::exact(amt_open)) // collapsible sidebar .size(Size::remainder()) // the main app contents .clip(true) - .horizontal(|mut strip| { - strip.cell(|ui| { + .horizontal(|mut hstrip| { + hstrip.cell(|ui| { let rect = ui.available_rect_before_wrap(); if !ui.visuals().dark_mode { let rect = ui.available_rect_before_wrap(); @@ -216,20 +216,28 @@ impl Chrome { rect, 0, notedeck_ui::colors::ALMOST_WHITE, - egui::Stroke::new(0.0, egui::Color32::TRANSPARENT), + egui::Stroke::new(0.0, Color32::TRANSPARENT), egui::StrokeKind::Inside, ); } - ui.with_layout(Layout::top_down(egui::Align::Center), |ui| { - self.topdown_sidebar(ui); - }); - - ui.with_layout(Layout::bottom_up(egui::Align::Center), |ui| { - if let Some(action) = bottomup_sidebar(self, app_ctx, ui) { - got_action = Some(action); - } - }); + StripBuilder::new(ui) + .size(Size::remainder()) + .size(Size::remainder()) + .vertical(|mut vstrip| { + vstrip.cell(|ui| { + _ = ui.vertical_centered(|ui| { + self.topdown_sidebar(ui); + }) + }); + vstrip.cell(|ui| { + ui.with_layout(Layout::bottom_up(egui::Align::Center), |ui| { + if let Some(action) = bottomup_sidebar(self, app_ctx, ui) { + got_action = Some(action); + } + }); + }); + }); // vertical sidebar line ui.painter().vline( @@ -239,7 +247,7 @@ impl Chrome { ); }); - strip.cell(|ui| { + hstrip.cell(|ui| { /* let rect = ui.available_rect_before_wrap(); ui.painter().rect( @@ -286,7 +294,6 @@ impl Chrome { .vertical(|mut strip| { strip.strip(|builder| { // the chrome panel is nested above the toolbar - got_action = self.panel(ctx, builder, amt_open); }); @@ -303,6 +310,23 @@ impl Chrome { fn toolbar(&mut self, ui: &mut egui::Ui) -> Option<ToolbarAction> { use egui_tabs::{TabColor, Tabs}; + let rect = ui.available_rect_before_wrap(); + ui.painter().hline( + rect.x_range(), + rect.top(), + ui.visuals().widgets.noninteractive.bg_stroke, + ); + + if !ui.visuals().dark_mode { + ui.painter().rect( + rect, + 0, + notedeck_ui::colors::ALMOST_WHITE, + egui::Stroke::new(0.0, Color32::TRANSPARENT), + egui::StrokeKind::Inside, + ); + } + let rs = Tabs::new(3) .selected(self.tab_selected) .hover_bg(TabColor::none()) @@ -496,8 +520,8 @@ fn notifications_button(ui: &mut egui::Ui) -> egui::Response { expanding_button( "notifications-button", 24.0, - app_images::notifications_button_image(), - app_images::notifications_button_image(), + app_images::notifications_light_image(), + app_images::notifications_dark_image(), ui, ) } @@ -506,8 +530,8 @@ fn home_button(ui: &mut egui::Ui) -> egui::Response { expanding_button( "home-button", 24.0, - app_images::home_button_image(), - app_images::home_button_image(), + app_images::home_light_image(), + app_images::home_dark_image(), ui, ) } diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -14,7 +14,9 @@ use crate::{ Result, }; -use notedeck::{Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState, UnknownIds}; +use notedeck::{ + ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState, UnknownIds, +}; use notedeck_ui::{jobs::JobsCache, NoteOptions}; use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPool}; @@ -561,14 +563,22 @@ fn render_damus_mobile( } } - rect.min.x = rect.max.x - 100.0; + rect.min.x = rect.max.x - if is_narrow(ui.ctx()) { 60.0 } else { 100.0 }; rect.min.y = rect.max.y - 100.0; - let interactive = true; + let is_interactive = app_ctx + .accounts + .get_selected_account() + .key + .secret_key + .is_some(); let darkmode = ui.ctx().style().visuals.dark_mode; if ui - .put(rect, ui::post::compose_note_button(interactive, darkmode)) + .put( + rect, + ui::post::compose_note_button(is_interactive, darkmode), + ) .clicked() && !app.columns(app_ctx.accounts).columns().is_empty() { diff --git a/crates/notedeck_columns/src/ui/add_column.rs b/crates/notedeck_columns/src/ui/add_column.rs @@ -187,7 +187,7 @@ impl<'a> AddColumnView<'a> { ScrollArea::vertical() .show(ui, |ui| { let mut selected_option: Option<AddColumnResponse> = None; - for column_option_data in self.get_base_options() { + for column_option_data in self.get_base_options(ui) { let option = column_option_data.option.clone(); if self.column_option_ui(ui, column_option_data).clicked() { selected_option = Some(option.take_as_response(self.cur_account)); @@ -203,7 +203,7 @@ impl<'a> AddColumnView<'a> { fn notifications_ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> { let mut selected_option: Option<AddColumnResponse> = None; - for column_option_data in self.get_notifications_options() { + for column_option_data in self.get_notifications_options(ui) { let option = column_option_data.option.clone(); if self.column_option_ui(ui, column_option_data).clicked() { selected_option = Some(option.take_as_response(self.cur_account)); @@ -441,7 +441,7 @@ impl<'a> AddColumnView<'a> { helper.take_animation_response() } - fn get_base_options(&self) -> Vec<ColumnOptionData> { + fn get_base_options(&self, ui: &mut Ui) -> Vec<ColumnOptionData> { let mut vec = Vec::new(); vec.push(ColumnOptionData { title: "Universe", @@ -465,7 +465,7 @@ impl<'a> AddColumnView<'a> { vec.push(ColumnOptionData { title: "Notifications", description: "Stay up to date with notifications and mentions", - icon: app_images::notifications_image(), + icon: app_images::notifications_image(ui.visuals().dark_mode), option: AddColumnOption::UndecidedNotification, }); vec.push(ColumnOptionData { @@ -490,7 +490,7 @@ impl<'a> AddColumnView<'a> { vec } - fn get_notifications_options(&self) -> Vec<ColumnOptionData> { + fn get_notifications_options(&self, ui: &mut Ui) -> Vec<ColumnOptionData> { let mut vec = Vec::new(); let source = if self.cur_account.key.secret_key.is_some() { @@ -502,14 +502,14 @@ impl<'a> AddColumnView<'a> { vec.push(ColumnOptionData { title: "Your Notifications", description: "Stay up to date with your notifications and mentions", - icon: app_images::notifications_image(), + icon: app_images::notifications_image(ui.visuals().dark_mode), option: AddColumnOption::Notification(source), }); vec.push(ColumnOptionData { title: "Someone else's Notifications", description: "Stay up to date with someone else's notifications and mentions", - icon: app_images::notifications_image(), + icon: app_images::notifications_image(ui.visuals().dark_mode), option: AddColumnOption::ExternalNotification, }); diff --git a/crates/notedeck_columns/src/ui/timeline.rs b/crates/notedeck_columns/src/ui/timeline.rs @@ -3,6 +3,7 @@ use egui::{vec2, Direction, Layout, Pos2, Stroke}; use egui_tabs::TabColor; use enostr::KeypairUnowned; use nostrdb::Transaction; +use notedeck::ui::is_narrow; use notedeck_ui::jobs::JobsCache; use std::f32::consts::PI; use tracing::{error, warn}; @@ -114,7 +115,9 @@ fn timeline_ui( .unwrap_or(false); let goto_top_resp = if show_top_button { - let top_button_pos = ui.available_rect_before_wrap().right_top() - vec2(48.0, -24.0); + let top_button_pos_x = if is_narrow(ui.ctx()) { 28.0 } else { 48.0 }; + let top_button_pos = + ui.available_rect_before_wrap().right_top() - vec2(top_button_pos_x, -24.0); egui::Area::new(ui.id().with("foreground_area")) .order(egui::Order::Middle) .fixed_pos(top_button_pos) diff --git a/crates/notedeck_ui/src/app_images.rs b/crates/notedeck_ui/src/app_images.rs @@ -113,10 +113,14 @@ pub fn help_light_image() -> Image<'static> { )) } -pub fn home_button_image() -> Image<'static> { +pub fn home_dark_image() -> Image<'static> { Image::new(include_image!("../../../assets/icons/home-toolbar.png")) } +pub fn home_light_image() -> Image<'static> { + home_dark_image().tint(egui::Color32::BLACK) +} + pub fn home_image() -> Image<'static> { Image::new(include_image!( "../../../assets/icons/home_icon_dark_4x.png" @@ -141,16 +145,22 @@ pub fn new_deck_image() -> Image<'static> { )) } -pub fn notifications_button_image() -> Image<'static> { +pub fn notifications_image(dark_mode: bool) -> Image<'static> { + if dark_mode { + crate::app_images::notifications_dark_image() + } else { + crate::app_images::notifications_light_image() + } +} + +pub fn notifications_dark_image() -> Image<'static> { Image::new(include_image!( "../../../assets/icons/notifications_dark_4x.png" )) } -pub fn notifications_image() -> Image<'static> { - Image::new(include_image!( - "../../../assets/icons/notifications_icon_dark_4x.png" - )) +pub fn notifications_light_image() -> Image<'static> { + notifications_dark_image().tint(egui::Color32::BLACK) } pub fn repost_dark_image() -> Image<'static> { Image::new(include_image!("../../../assets/icons/repost_icon_4x.png")) diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs @@ -15,6 +15,7 @@ pub use contents::{render_note_contents, render_note_preview, NoteContents}; pub use context::NoteContextButton; use notedeck::note::MediaAction; use notedeck::note::ZapTargetAmount; +use notedeck::ui::is_narrow; use notedeck::Images; pub use options::NoteOptions; pub use reply_description::reply_desc; @@ -257,6 +258,10 @@ impl<'a, 'd> NoteView<'a, 'd> { ui.spacing_mut().item_spacing.x = 4.0; } + if is_narrow(ui.ctx()) { + ui.spacing_mut().item_spacing.x = 1.0 + } + let pfp_size = self.options().pfp_size(); match profile @@ -367,7 +372,7 @@ impl<'a, 'd> NoteView<'a, 'd> { let horiz_resp = ui .horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 2.0; + ui.spacing_mut().item_spacing.x = if is_narrow(ui.ctx()) { 1.0 } else { 2.0 }; ui.add(Username::new(profile.as_ref().ok(), note.pubkey()).abbreviated(20)); let cached_note = note_cache.cached_note_or_insert_mut(note_key, note); @@ -511,7 +516,7 @@ impl<'a, 'd> NoteView<'a, 'd> { self.show_unread_indicator, ); ui.horizontal(|ui| 's: { - ui.spacing_mut().item_spacing.x = 2.0; + ui.spacing_mut().item_spacing.x = if is_narrow(ui.ctx()) { 1.0 } else { 2.0 }; let note_reply = self .note_context