notedeck

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

commit b1a5dd6cab0cc298eb9454ef5ea01e857ef53181
parent d12e5b363cb2328650ef1c1a0bd4f513ff57a0e0
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 31 Jul 2025 16:03:13 -0700

add NotedeckOptions and feature flags, add notebook feature

This switches from bools to flags in our Args struct. We also add
notebook as an optional feature flag (--notebook) since its not ready.

Diffstat:
MCargo.lock | 1+
Mcrates/notedeck/Cargo.toml | 1+
Mcrates/notedeck/src/app.rs | 9+++++++--
Mcrates/notedeck/src/args.rs | 40++++++++++++++--------------------------
Mcrates/notedeck/src/lib.rs | 2++
Acrates/notedeck/src/options.rs | 39+++++++++++++++++++++++++++++++++++++++
Mcrates/notedeck_chrome/src/android.rs | 8+++++++-
Mcrates/notedeck_chrome/src/chrome.rs | 11++++++-----
Mcrates/notedeck_chrome/src/notedeck.rs | 12++++++++----
Mcrates/notedeck_chrome/src/setup.rs | 16++++++++++------
Mcrates/notedeck_columns/src/app.rs | 7+++++--
11 files changed, 100 insertions(+), 46 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -3483,6 +3483,7 @@ dependencies = [ "base32", "bech32", "bincode", + "bitflags 2.9.1", "blurhash", "dirs", "eframe", diff --git a/crates/notedeck/Cargo.toml b/crates/notedeck/Cargo.toml @@ -47,6 +47,7 @@ fluent-langneg = { workspace = true } unic-langid = { workspace = true } once_cell = { workspace = true } md5 = { workspace = true } +bitflags = { workspace = true } regex = "1" [dev-dependencies] diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs @@ -4,6 +4,7 @@ use crate::persist::{AppSizeHandler, SettingsHandler}; use crate::wallet::GlobalWallet; use crate::zaps::Zaps; use crate::JobPool; +use crate::NotedeckOptions; use crate::{ frame_history::FrameHistory, AccountStorage, Accounts, AppContext, Args, DataPath, DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, UnknownIds, @@ -109,7 +110,7 @@ impl eframe::App for Notedeck { }); self.app_size.try_save_app_size(ctx); - if self.args.relay_debug { + if self.args.options.contains(NotedeckOptions::RelayDebug) { if self.pool.debug.is_none() { self.pool.use_debug(); } @@ -170,7 +171,7 @@ impl Notedeck { let config = Config::new().set_ingester_threads(2).set_mapsize(map_size); - let keystore = if parsed_args.use_keystore { + let keystore = if parsed_args.options.contains(NotedeckOptions::UseKeystore) { let keys_path = path.path(DataPathType::Keys); let selected_key_path = path.path(DataPathType::SelectedKey); Some(AccountStorage::new( @@ -276,6 +277,10 @@ impl Notedeck { } } + pub fn options(&self) -> NotedeckOptions { + self.args.options + } + pub fn app<A: App + 'static>(mut self, app: A) -> Self { self.set_app(app); self diff --git a/crates/notedeck/src/args.rs b/crates/notedeck/src/args.rs @@ -1,23 +1,15 @@ use std::collections::BTreeSet; +use crate::NotedeckOptions; use enostr::{Keypair, Pubkey, SecretKey}; use tracing::error; use unic_langid::{LanguageIdentifier, LanguageIdentifierError}; pub struct Args { pub relays: Vec<String>, - pub is_mobile: Option<bool>, pub locale: Option<LanguageIdentifier>, - pub show_note_client: bool, pub keys: Vec<Keypair>, - pub light: bool, - pub debug: bool, - pub relay_debug: bool, - - /// Enable when running tests so we don't panic on app startup - pub tests: bool, - - pub use_keystore: bool, + pub options: NotedeckOptions, pub dbpath: Option<String>, pub datapath: Option<String>, } @@ -28,14 +20,8 @@ impl Args { let mut unrecognized_args = BTreeSet::new(); let mut res = Args { relays: vec![], - is_mobile: None, keys: vec![], - light: false, - show_note_client: false, - debug: false, - relay_debug: false, - tests: false, - use_keystore: true, + options: NotedeckOptions::default(), dbpath: None, datapath: None, locale: None, @@ -47,9 +33,9 @@ impl Args { let arg = &args[i]; if arg == "--mobile" { - res.is_mobile = Some(true); + res.options.set(NotedeckOptions::Mobile, true); } else if arg == "--light" { - res.light = true; + res.options.set(NotedeckOptions::LightTheme, true); } else if arg == "--locale" { i += 1; let Some(locale) = args.get(i) else { @@ -68,11 +54,11 @@ impl Args { } } } else if arg == "--dark" { - res.light = false; + res.options.set(NotedeckOptions::LightTheme, false); } else if arg == "--debug" { - res.debug = true; + res.options.set(NotedeckOptions::Debug, true); } else if arg == "--testrunner" { - res.tests = true; + res.options.set(NotedeckOptions::Tests, true); } else if arg == "--pub" || arg == "--npub" { i += 1; let pubstr = if let Some(next_arg) = args.get(i) { @@ -135,11 +121,13 @@ impl Args { }; res.relays.push(relay.clone()); } else if arg == "--no-keystore" { - res.use_keystore = false; + res.options.set(NotedeckOptions::UseKeystore, true); } else if arg == "--relay-debug" { - res.relay_debug = true; - } else if arg == "--show-note-client" { - res.show_note_client = true; + res.options.set(NotedeckOptions::RelayDebug, true); + } else if arg == "--show-client" { + res.options.set(NotedeckOptions::ShowClient, true); + } else if arg == "--notebook" { + res.options.set(NotedeckOptions::FeatureNotebook, true); } else { unrecognized_args.insert(arg.clone()); } diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs @@ -18,6 +18,7 @@ mod muted; pub mod name; pub mod note; mod notecache; +mod options; mod persist; pub mod platform; pub mod profile; @@ -68,6 +69,7 @@ pub use note::{ RootIdError, RootNoteId, RootNoteIdBuf, ScrollInfo, ZapAction, }; pub use notecache::{CachedNote, NoteCache}; +pub use options::NotedeckOptions; pub use persist::*; pub use profile::get_profile_url; pub use relay_debug::RelayDebugView; diff --git a/crates/notedeck/src/options.rs b/crates/notedeck/src/options.rs @@ -0,0 +1,39 @@ +use bitflags::bitflags; + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct NotedeckOptions: u64 { + // ===== Settings ====== + /// Are we on light theme? + const LightTheme = 1 << 0; + + /// Debug controls, fps stats + const Debug = 1 << 1; + + /// Show relay debug window? + const RelayDebug = 1 << 2; + + /// Are we running as tests? + const Tests = 1 << 3; + + /// Use keystore? + const UseKeystore = 1 << 4; + + /// Show client on notes? + const ShowClient = 1 << 5; + + /// Simulate is_compiled_as_mobile ? + const Mobile = 1 << 6; + + // ===== Feature Flags ====== + /// Is notebook enabled? + const FeatureNotebook = 1 << 32; + } +} + +impl Default for NotedeckOptions { + fn default() -> Self { + NotedeckOptions::UseKeystore + } +} diff --git a/crates/notedeck_chrome/src/android.rs b/crates/notedeck_chrome/src/android.rs @@ -97,7 +97,13 @@ pub async fn android_main(app: AndroidApp) { chrome.add_app(NotedeckApp::Columns(Box::new(columns))); chrome.add_app(NotedeckApp::Dave(Box::new(dave))); - chrome.add_app(NotedeckApp::Notebook(Box::new(notebook))); + + if notedeck + .options() + .contains(NotedeckOptions::FeaturesNotebook) + { + chrome.add_app(NotedeckApp::Notebook(Box::default())); + } // test dav chrome.set_active(0); diff --git a/crates/notedeck_chrome/src/chrome.rs b/crates/notedeck_chrome/src/chrome.rs @@ -6,7 +6,8 @@ use egui::{vec2, Button, Color32, Label, Layout, Rect, RichText, ThemePreference use egui_extras::{Size, StripBuilder}; use nostrdb::{ProfileRecord, Transaction}; use notedeck::{ - tr, App, AppAction, AppContext, Localization, NotedeckTextStyle, UserAccount, WalletType, + tr, App, AppAction, AppContext, Localization, NotedeckOptions, NotedeckTextStyle, UserAccount, + WalletType, }; use notedeck_columns::{ column::SelectionResult, timeline::kind::ListKind, timeline::TimelineKind, Damus, @@ -612,10 +613,10 @@ fn accounts_button(ui: &mut egui::Ui) -> egui::Response { fn notebook_button(ui: &mut egui::Ui) -> egui::Response { expanding_button( - "columns-button", + "notebook-button", 40.0, - app_images::new_message_image(), - app_images::new_message_image(), + app_images::algo_image(), + app_images::algo_image(), ui, ) } @@ -898,7 +899,7 @@ fn bottomup_sidebar( .add(wallet_button()) .on_hover_cursor(egui::CursorIcon::PointingHand); - if ctx.args.debug { + if ctx.args.options.contains(NotedeckOptions::Debug) { ui.weak(format!("{}", ctx.frame_history.fps() as i32)); ui.weak(format!( "{:10.1}", diff --git a/crates/notedeck_chrome/src/notedeck.rs b/crates/notedeck_chrome/src/notedeck.rs @@ -10,14 +10,13 @@ static GLOBAL: AccountingAllocator<std::alloc::System> = AccountingAllocator::new(std::alloc::System); use notedeck::enostr::Error; -use notedeck::{DataPath, DataPathType, Notedeck}; +use notedeck::{DataPath, DataPathType, Notedeck, NotedeckOptions}; use notedeck_chrome::{ setup::{generate_native_options, setup_chrome}, Chrome, NotedeckApp, }; use notedeck_columns::Damus; use notedeck_dave::Dave; -use notedeck_notebook::Notebook; use tracing::error; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::EnvFilter; @@ -98,7 +97,6 @@ async fn main() { let mut chrome = Chrome::new(); let columns = Damus::new(&mut notedeck.app_context(), &args); let dave = Dave::new(cc.wgpu_render_state.as_ref()); - let notebook = Notebook::default(); setup_chrome( ctx, @@ -121,7 +119,13 @@ async fn main() { chrome.add_app(NotedeckApp::Columns(Box::new(columns))); chrome.add_app(NotedeckApp::Dave(Box::new(dave))); - chrome.add_app(NotedeckApp::Notebook(Box::new(notebook))); + + if notedeck + .options() + .contains(NotedeckOptions::FeatureNotebook) + { + chrome.add_app(NotedeckApp::Notebook(Box::default())); + } chrome.set_active(0); diff --git a/crates/notedeck_chrome/src/setup.rs b/crates/notedeck_chrome/src/setup.rs @@ -2,7 +2,7 @@ use crate::{fonts, theme}; use eframe::NativeOptions; use egui::{FontId, ThemePreference}; -use notedeck::{AppSizeHandler, DataPath, NotedeckTextStyle}; +use notedeck::{AppSizeHandler, DataPath, NotedeckOptions, NotedeckTextStyle}; use notedeck_ui::app_images; use tracing::info; @@ -13,16 +13,20 @@ pub fn setup_chrome( note_body_font_size: f32, zoom_factor: f32, ) { - let is_mobile = args - .is_mobile - .unwrap_or(notedeck::ui::is_compiled_as_mobile()); + let is_mobile = + args.options.contains(NotedeckOptions::Mobile) || notedeck::ui::is_compiled_as_mobile(); let is_oled = notedeck::ui::is_oled(); // Some people have been running notedeck in debug, let's catch that! - if !args.tests && cfg!(debug_assertions) && !args.debug { + if !args.options.contains(NotedeckOptions::Tests) + && cfg!(debug_assertions) + && !args.options.contains(NotedeckOptions::Debug) + { println!("--- WELCOME TO DAMUS NOTEDECK! ---"); - println!("It looks like are running notedeck in debug mode, unless you are a developer, this is not likely what you want."); + println!( + "It looks like are running notedeck in debug mode, unless you are a developer, this is not likely what you want." + ); println!("If you are a developer, run `cargo run -- --debug` to skip this message."); println!("For everyone else, try again with `cargo run --release`. Enjoy!"); println!("---------------------------------"); diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -19,7 +19,7 @@ use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPo use nostrdb::Transaction; use notedeck::{ tr, ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState, - Images, JobsCache, Localization, SettingsHandler, UnknownIds, + Images, JobsCache, Localization, NotedeckOptions, SettingsHandler, UnknownIds, }; use notedeck_ui::{ media::{MediaViewer, MediaViewerFlags, MediaViewerState}, @@ -442,7 +442,10 @@ impl Damus { let mut options = AppOptions::default(); let tmp_columns = !parsed_args.columns.is_empty(); options.set(AppOptions::TmpColumns, tmp_columns); - options.set(AppOptions::Debug, app_context.args.debug); + options.set( + AppOptions::Debug, + app_context.args.options.contains(NotedeckOptions::Debug), + ); options.set( AppOptions::SinceOptimize, parsed_args.is_flag_set(ColumnsFlag::SinceOptimize),