notedeck

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

commit fcac49a0a5f23dc15d2878c3598d096c36caa0d6
parent 475314da75fada4f38dc13c1c73f8175e5be34ad
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 20 Dec 2024 15:39:26 -0800

previews: run previews as notedeck apps

This allows ./preview to be a notedeck app runner. I am currently
using it for the ProfilePic app (which will because notedeck_viz)

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
M.gitignore | 1+
Mcrates/notedeck/src/app.rs | 2+-
Mcrates/notedeck/src/context.rs | 1-
Mcrates/notedeck/src/storage/file_storage.rs | 29+++++++++++++++++++----------
Mcrates/notedeck_chrome/Cargo.toml | 10++++++----
Mcrates/notedeck_chrome/src/app.rs | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mcrates/notedeck_chrome/src/notedeck.rs | 6++----
Mcrates/notedeck_chrome/src/preview.rs | 47++++++++++++++++++-----------------------------
Mcrates/notedeck_columns/src/app.rs | 102+++++++++++++++++--------------------------------------------------------------
Mcrates/notedeck_columns/src/ui/account_login_view.rs | 7++++---
Mcrates/notedeck_columns/src/ui/configure_deck.rs | 7++++---
Mcrates/notedeck_columns/src/ui/edit_deck.rs | 7++++---
Mcrates/notedeck_columns/src/ui/note/post.rs | 25+++++++++----------------
Mcrates/notedeck_columns/src/ui/preview.rs | 23++++++-----------------
Acrates/notedeck_columns/src/ui/profile/menu.rs | 2++
Mcrates/notedeck_columns/src/ui/profile/picture.rs | 104++++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mcrates/notedeck_columns/src/ui/profile/preview.rs | 18+++++++-----------
Mcrates/notedeck_columns/src/ui/relay.rs | 5+++--
Mpreview | 3++-
19 files changed, 223 insertions(+), 239 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -3,6 +3,7 @@ build.log perf.data perf.data.old .privenv +*.swp target queries/damus-notifs.json .git diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs @@ -1,5 +1,5 @@ use crate::AppContext; pub trait App { - fn update(&mut self, ctx: &mut AppContext<'_>); + fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui); } diff --git a/crates/notedeck/src/context.rs b/crates/notedeck/src/context.rs @@ -15,5 +15,4 @@ pub struct AppContext<'a> { pub path: &'a DataPath, pub args: &'a Args, pub theme: &'a mut ThemeHandler, - pub egui: &'a egui::Context, } diff --git a/crates/notedeck/src/storage/file_storage.rs b/crates/notedeck/src/storage/file_storage.rs @@ -22,18 +22,12 @@ impl DataPath { pub fn default_base() -> Option<PathBuf> { dirs::data_local_dir().map(|pb| pb.join("notedeck")) } -} -pub enum DataPathType { - Log, - Setting, - Keys, - SelectedKey, - Db, - Cache, -} + pub fn default_base_or_cwd() -> PathBuf { + use std::str::FromStr; + Self::default_base().unwrap_or_else(|| PathBuf::from_str(".").unwrap()) + } -impl DataPath { pub fn rel_path(&self, typ: DataPathType) -> PathBuf { match typ { DataPathType::Log => PathBuf::from("logs"), @@ -50,6 +44,21 @@ impl DataPath { } } +impl Default for DataPath { + fn default() -> Self { + Self::new(Self::default_base_or_cwd()) + } +} + +pub enum DataPathType { + Log, + Setting, + Keys, + SelectedKey, + Db, + Cache, +} + #[derive(Debug, PartialEq)] pub struct Directory { pub file_path: PathBuf, diff --git a/crates/notedeck_chrome/Cargo.toml b/crates/notedeck_chrome/Cargo.toml @@ -10,18 +10,20 @@ description = "The nostr browser" [dependencies] eframe = { workspace = true } -egui = { workspace = true } egui_extras = { workspace = true } +egui = { workspace = true } enostr = { workspace = true } nostrdb = { workspace = true } -notedeck = { workspace = true } notedeck_columns = { workspace = true } +notedeck = { workspace = true } +puffin = { workspace = true, optional = true } +puffin_egui = { workspace = true, optional = true } serde_json = { workspace = true } strum = { workspace = true } tokio = { workspace = true } -tracing = { workspace = true } tracing-appender = { workspace = true } tracing-subscriber = { workspace = true } +tracing = { workspace = true } [dev-dependencies] tempfile = { workspace = true } @@ -39,7 +41,7 @@ path = "src/preview.rs" [features] default = [] -profiling = ["notedeck_columns/puffin"] +profiling = ["notedeck_columns/puffin", "puffin", "puffin_egui"] [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11.1" diff --git a/crates/notedeck_chrome/src/app.rs b/crates/notedeck_chrome/src/app.rs @@ -25,7 +25,38 @@ pub struct Notedeck { theme: ThemeHandler, tabs: Tabs, app_rect_handler: AppSizeHandler, - egui: egui::Context, +} + +fn margin_top(narrow: bool) -> f32 { + #[cfg(target_os = "android")] + { + // FIXME - query the system bar height and adjust more precisely + let _ = narrow; // suppress compiler warning on android + 40.0 + } + #[cfg(not(target_os = "android"))] + { + if narrow { + 50.0 + } else { + 0.0 + } + } +} + +/// Our chrome, which is basically nothing +fn main_panel(style: &egui::Style, narrow: bool) -> egui::CentralPanel { + let inner_margin = egui::Margin { + top: margin_top(narrow), + left: 0.0, + right: 0.0, + bottom: 0.0, + }; + egui::CentralPanel::default().frame(egui::Frame { + inner_margin, + fill: style.visuals.panel_fill, + ..Default::default() + }) } impl eframe::App for Notedeck { @@ -36,19 +67,34 @@ impl eframe::App for Notedeck { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { // TODO: render chrome - - // render app - if let Some(app) = &self.tabs.app { - let app = app.clone(); - app.borrow_mut().update(&mut self.app_context()); - } + #[cfg(feature = "profiling")] + puffin::GlobalProfiler::lock().new_frame(); + + main_panel(&ctx.style(), notedeck::ui::is_narrow(ctx)).show(ctx, |ui| { + // render app + if let Some(app) = &self.tabs.app { + let app = app.clone(); + app.borrow_mut().update(&mut self.app_context(), ui); + } + }); self.app_rect_handler.try_save_app_size(ctx); + + #[cfg(feature = "profiling")] + puffin_egui::profiler_window(ctx); } } +#[cfg(feature = "profiling")] +fn setup_profiling() { + puffin::set_scopes_on(true); // tell puffin to collect data +} + impl Notedeck { pub fn new<P: AsRef<Path>>(ctx: &egui::Context, data_path: P, args: &[String]) -> Self { + #[cfg(feature = "profiling")] + setup_profiling(); + let parsed_args = Args::parse(args); let is_mobile = parsed_args .is_mobile @@ -139,7 +185,6 @@ impl Notedeck { let img_cache = ImageCache::new(imgcache_dir); let note_cache = NoteCache::default(); let unknown_ids = UnknownIds::default(); - let egui = ctx.clone(); let tabs = Tabs::new(None); let parsed_args = Args::parse(args); let app_rect_handler = AppSizeHandler::new(&path); @@ -155,7 +200,6 @@ impl Notedeck { path: path.clone(), args: parsed_args, theme, - egui, tabs, } } @@ -171,7 +215,6 @@ impl Notedeck { path: &self.path, args: &self.args, theme: &mut self.theme, - egui: &self.egui, } } diff --git a/crates/notedeck_chrome/src/notedeck.rs b/crates/notedeck_chrome/src/notedeck.rs @@ -3,8 +3,6 @@ use notedeck_chrome::{setup::generate_native_options, Notedeck}; use notedeck::{DataPath, DataPathType}; use notedeck_columns::Damus; -use std::path::PathBuf; -use std::str::FromStr; use tracing_subscriber::EnvFilter; // Entry point for wasm @@ -64,8 +62,8 @@ fn setup_logging(path: &DataPath) { #[cfg(not(target_arch = "wasm32"))] #[tokio::main] async fn main() { - let base_path = DataPath::default_base().unwrap_or(PathBuf::from_str(".").unwrap()); - let path = DataPath::new(&base_path); + let base_path = DataPath::default_base_or_cwd(); + let path = DataPath::new(base_path.clone()); setup_logging(&path); diff --git a/crates/notedeck_chrome/src/preview.rs b/crates/notedeck_chrome/src/preview.rs @@ -1,7 +1,6 @@ use notedeck::DataPath; -use notedeck_chrome::setup::{ - generate_mobile_emulator_native_options, generate_native_options, setup_cc, -}; +use notedeck_chrome::setup::generate_native_options; +use notedeck_chrome::Notedeck; use notedeck_columns::ui::configure_deck::ConfigureDeckView; use notedeck_columns::ui::edit_deck::EditDeckView; use notedeck_columns::ui::{ @@ -10,42 +9,32 @@ use notedeck_columns::ui::{ }; use std::env; -struct PreviewRunner { - force_mobile: bool, - light_mode: bool, -} +struct PreviewRunner {} impl PreviewRunner { - fn new(force_mobile: bool, light_mode: bool) -> Self { - PreviewRunner { - force_mobile, - light_mode, - } + fn new() -> Self { + PreviewRunner {} } async fn run<P>(self, preview: P) where - P: Into<PreviewApp> + 'static, + P: notedeck::App + 'static, { tracing_subscriber::fmt::init(); - let native_options = if self.force_mobile { - generate_mobile_emulator_native_options() - } else { - // TODO: tmp preview pathbuf? - generate_native_options(DataPath::new("previews")) - }; + let base_path = DataPath::default_base_or_cwd(); + let path = DataPath::new(&base_path); + + let _res = eframe::run_native( + "Notedeck Preview", + generate_native_options(path), + Box::new(|cc| { + let args: Vec<String> = std::env::args().collect(); + let mut notedeck = Notedeck::new(&cc.egui_ctx, &base_path, &args); - let is_mobile = self.force_mobile; - let light_mode = self.light_mode; + notedeck.add_app(PreviewApp::new(preview)); - let _ = eframe::run_native( - "UI Preview Runner", - native_options, - Box::new(move |cc| { - let app = Into::<PreviewApp>::into(preview); - setup_cc(&cc.egui_ctx, is_mobile, light_mode); - Ok(Box::new(app)) + Ok(Box::new(notedeck)) }), ); } @@ -93,7 +82,7 @@ async fn main() { if light_mode { "enabled" } else { "disabled" } ); let is_mobile = is_mobile.unwrap_or(notedeck::ui::is_compiled_as_mobile()); - let runner = PreviewRunner::new(is_mobile, light_mode); + let runner = PreviewRunner::new(); previews!( runner, diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -22,7 +22,6 @@ use notedeck::{Accounts, AppContext, DataPath, DataPathType, FilterState, ImageC use enostr::{ClientMessage, Keypair, Pubkey, RelayEvent, RelayMessage, RelayPool}; use uuid::Uuid; -use egui::{Frame, Style}; use egui_extras::{Size, StripBuilder}; use nostrdb::{Ndb, Transaction}; @@ -185,22 +184,11 @@ fn unknown_id_send(unknown_ids: &mut UnknownIds, pool: &mut RelayPool) { pool.send(&msg); } -#[cfg(feature = "profiling")] -fn setup_profiling() { - puffin::set_scopes_on(true); // tell puffin to collect data -} - -fn update_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>) { - let _ctx = app_ctx.egui.clone(); - let ctx = &_ctx; - +fn update_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>, ctx: &egui::Context) { app_ctx.accounts.update(app_ctx.ndb, app_ctx.pool, ctx); // update user relay and mute lists match damus.state { DamusState::Initializing => { - #[cfg(feature = "profiling")] - setup_profiling(); - damus.state = DamusState::Initialized; // this lets our eose handler know to close unknownids right away damus @@ -337,18 +325,15 @@ fn process_message(damus: &mut Damus, ctx: &mut AppContext<'_>, relay: &str, msg } } -fn render_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>) { - if notedeck::ui::is_narrow(app_ctx.egui) { - render_damus_mobile(damus, app_ctx); +fn render_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { + if notedeck::ui::is_narrow(ui.ctx()) { + render_damus_mobile(damus, app_ctx, ui); } else { - render_damus_desktop(damus, app_ctx); + render_damus_desktop(damus, app_ctx, ui); } // We use this for keeping timestamps and things up to date - app_ctx.egui.request_repaint_after(Duration::from_secs(1)); - - #[cfg(feature = "profiling")] - puffin_egui::profiler_window(ctx); + ui.ctx().request_repaint_after(Duration::from_secs(1)); } /* @@ -498,63 +483,24 @@ fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) { } */ -fn render_damus_mobile(app: &mut Damus, app_ctx: &mut AppContext<'_>) { - let _ctx = app_ctx.egui.clone(); - let ctx = &_ctx; - +fn render_damus_mobile(app: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { #[cfg(feature = "profiling")] puffin::profile_function!(); //let routes = app.timelines[0].routes.clone(); - main_panel(&ctx.style(), notedeck::ui::is_narrow(ctx)).show(ctx, |ui| { - if !app.columns(app_ctx.accounts).columns().is_empty() - && nav::render_nav(0, app, app_ctx, ui).process_render_nav_response(app, app_ctx) - { - storage::save_decks_cache(app_ctx.path, &app.decks_cache); - } - }); -} - -fn margin_top(narrow: bool) -> f32 { - #[cfg(target_os = "android")] + if !app.columns(app_ctx.accounts).columns().is_empty() + && nav::render_nav(0, app, app_ctx, ui).process_render_nav_response(app, app_ctx) { - // FIXME - query the system bar height and adjust more precisely - let _ = narrow; // suppress compiler warning on android - 40.0 + storage::save_decks_cache(app_ctx.path, &app.decks_cache); } - #[cfg(not(target_os = "android"))] - { - if narrow { - 50.0 - } else { - 0.0 - } - } -} - -fn main_panel(style: &Style, narrow: bool) -> egui::CentralPanel { - let inner_margin = egui::Margin { - top: margin_top(narrow), - left: 0.0, - right: 0.0, - bottom: 0.0, - }; - egui::CentralPanel::default().frame(Frame { - inner_margin, - fill: style.visuals.panel_fill, - ..Default::default() - }) } -fn render_damus_desktop(app: &mut Damus, app_ctx: &mut AppContext<'_>) { - let _ctx = app_ctx.egui.clone(); - let ctx = &_ctx; - +fn render_damus_desktop(app: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { #[cfg(feature = "profiling")] puffin::profile_function!(); - let screen_size = ctx.screen_rect().width(); + let screen_size = ui.ctx().screen_rect().width(); let calc_panel_width = (screen_size / get_active_columns(app_ctx.accounts, &app.decks_cache).num_columns() as f32) - 30.0; @@ -566,16 +512,14 @@ fn render_damus_desktop(app: &mut Damus, app_ctx: &mut AppContext<'_>) { Size::remainder() }; - main_panel(&ctx.style(), notedeck::ui::is_narrow(ctx)).show(ctx, |ui| { - ui.spacing_mut().item_spacing.x = 0.0; - if need_scroll { - egui::ScrollArea::horizontal().show(ui, |ui| { - timelines_view(ui, panel_sizes, app, app_ctx); - }); - } else { + ui.spacing_mut().item_spacing.x = 0.0; + if need_scroll { + egui::ScrollArea::horizontal().show(ui, |ui| { timelines_view(ui, panel_sizes, app, app_ctx); - } - }); + }); + } else { + timelines_view(ui, panel_sizes, app, app_ctx); + } } fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, ctx: &mut AppContext<'_>) { @@ -653,17 +597,15 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, ctx: &mut App } impl notedeck::App for Damus { - fn update(&mut self, ctx: &mut AppContext<'_>) { + fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { /* self.app .frame_history .on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage); */ - #[cfg(feature = "profiling")] - puffin::GlobalProfiler::lock().new_frame(); - update_damus(self, ctx); - render_damus(self, ctx); + update_damus(self, ctx, ui.ctx()); + render_damus(self, ctx, ui); } } diff --git a/crates/notedeck_columns/src/ui/account_login_view.rs b/crates/notedeck_columns/src/ui/account_login_view.rs @@ -1,5 +1,5 @@ use crate::login_manager::AcquireKeyState; -use crate::ui::{Preview, PreviewConfig, View}; +use crate::ui::{Preview, PreviewConfig}; use egui::TextEdit; use egui::{Align, Button, Color32, Frame, InnerResponse, Margin, RichText, Vec2}; use enostr::Keypair; @@ -110,13 +110,14 @@ fn login_textedit(manager: &mut AcquireKeyState) -> TextEdit { mod preview { use super::*; + use notedeck::{App, AppContext}; pub struct AccountLoginPreview { manager: AcquireKeyState, } - impl View for AccountLoginPreview { - fn ui(&mut self, ui: &mut egui::Ui) { + impl App for AccountLoginPreview { + fn update(&mut self, _app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { AccountLoginView::new(&mut self.manager).ui(ui); } } diff --git a/crates/notedeck_columns/src/ui/configure_deck.rs b/crates/notedeck_columns/src/ui/configure_deck.rs @@ -297,10 +297,11 @@ fn paint_row(ui: &mut egui::Ui, row_glyphs: &[char], font_size: f32) -> Option<c mod preview { use crate::{ deck_state::DeckState, - ui::{Preview, PreviewConfig, View}, + ui::{Preview, PreviewConfig}, }; use super::ConfigureDeckView; + use notedeck::{App, AppContext}; pub struct ConfigureDeckPreview { state: DeckState, @@ -314,8 +315,8 @@ mod preview { } } - impl View for ConfigureDeckPreview { - fn ui(&mut self, ui: &mut egui::Ui) { + impl App for ConfigureDeckPreview { + fn update(&mut self, _app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { ConfigureDeckView::new(&mut self.state).ui(ui); } } diff --git a/crates/notedeck_columns/src/ui/edit_deck.rs b/crates/notedeck_columns/src/ui/edit_deck.rs @@ -58,10 +58,11 @@ fn delete_button() -> impl Widget { mod preview { use crate::{ deck_state::DeckState, - ui::{Preview, PreviewConfig, View}, + ui::{Preview, PreviewConfig}, }; use super::EditDeckView; + use notedeck::{App, AppContext}; pub struct EditDeckPreview { state: DeckState, @@ -75,8 +76,8 @@ mod preview { } } - impl View for EditDeckPreview { - fn ui(&mut self, ui: &mut egui::Ui) { + impl App for EditDeckPreview { + fn update(&mut self, _app_ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { EditDeckView::new(&mut self.state).ui(ui); } } diff --git a/crates/notedeck_columns/src/ui/note/post.rs b/crates/notedeck_columns/src/ui/note/post.rs @@ -1,11 +1,11 @@ use crate::draft::{Draft, Drafts}; use crate::post::NewPost; -use crate::ui::{self, Preview, PreviewConfig, View}; +use crate::ui::{self, Preview, PreviewConfig}; use crate::Result; use egui::widgets::text_edit::TextEdit; use egui::{Frame, Layout}; use enostr::{FilledKeypair, FullKeypair, NoteId, RelayPool}; -use nostrdb::{Config, Ndb, Transaction}; +use nostrdb::{Ndb, Transaction}; use tracing::info; use notedeck::{ImageCache, NoteCache}; @@ -257,38 +257,31 @@ fn post_button(interactive: bool) -> impl egui::Widget { mod preview { use super::*; + use notedeck::{App, AppContext}; pub struct PostPreview { - ndb: Ndb, - img_cache: ImageCache, - note_cache: NoteCache, draft: Draft, poster: FullKeypair, } impl PostPreview { fn new() -> Self { - let ndb = Ndb::new(".", &Config::new()).expect("ndb"); - PostPreview { - ndb, - img_cache: ImageCache::new(".".into()), - note_cache: NoteCache::default(), draft: Draft::new(), poster: FullKeypair::generate(), } } } - impl View for PostPreview { - fn ui(&mut self, ui: &mut egui::Ui) { - let txn = Transaction::new(&self.ndb).expect("txn"); + impl App for PostPreview { + fn update(&mut self, app: &mut AppContext<'_>, ui: &mut egui::Ui) { + let txn = Transaction::new(app.ndb).expect("txn"); PostView::new( - &self.ndb, + app.ndb, &mut self.draft, PostType::New, - &mut self.img_cache, - &mut self.note_cache, + app.img_cache, + app.note_cache, self.poster.to_filled(), ) .ui(&txn, ui); diff --git a/crates/notedeck_columns/src/ui/preview.rs b/crates/notedeck_columns/src/ui/preview.rs @@ -1,37 +1,26 @@ -use crate::ui::View; - pub struct PreviewConfig { pub is_mobile: bool, } pub trait Preview { - type Prev: View; + type Prev: notedeck::App; fn preview(cfg: PreviewConfig) -> Self::Prev; } pub struct PreviewApp { - view: Box<dyn View>, -} - -impl<V> From<V> for PreviewApp -where - V: View + 'static, -{ - fn from(v: V) -> Self { - PreviewApp::new(v) - } + view: Box<dyn notedeck::App>, } impl PreviewApp { - pub fn new(view: impl View + 'static) -> PreviewApp { + pub fn new(view: impl notedeck::App + 'static) -> PreviewApp { let view = Box::new(view); Self { view } } } -impl eframe::App for PreviewApp { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - egui::CentralPanel::default().show(ctx, |ui| self.view.ui(ui)); +impl notedeck::App for PreviewApp { + fn update(&mut self, app_ctx: &mut notedeck::AppContext<'_>, ui: &mut egui::Ui) { + self.view.update(app_ctx, ui); } } diff --git a/crates/notedeck_columns/src/ui/profile/menu.rs b/crates/notedeck_columns/src/ui/profile/menu.rs @@ -0,0 +1,2 @@ + +// profile menu diff --git a/crates/notedeck_columns/src/ui/profile/picture.rs b/crates/notedeck_columns/src/ui/profile/picture.rs @@ -1,9 +1,10 @@ use crate::images::ImageType; -use crate::ui::{Preview, PreviewConfig, View}; +use crate::ui::{Preview, PreviewConfig}; use egui::{vec2, Sense, TextureHandle}; use nostrdb::{Ndb, Transaction}; +use tracing::info; -use notedeck::ImageCache; +use notedeck::{AppContext, ImageCache}; pub struct ProfilePic<'cache, 'url> { cache: &'cache mut ImageCache, @@ -132,22 +133,62 @@ mod preview { use std::collections::HashSet; pub struct ProfilePicPreview { - cache: ImageCache, - ndb: Ndb, - keys: Vec<ProfileKey>, + keys: Option<Vec<ProfileKey>>, } impl ProfilePicPreview { fn new() -> Self { - let config = Config::new(); - let ndb = Ndb::new(".", &config).expect("ndb"); - let txn = Transaction::new(&ndb).unwrap(); + ProfilePicPreview { keys: None } + } + + fn show(&mut self, app: &mut AppContext<'_>, ui: &mut egui::Ui) { + egui::ScrollArea::both().show(ui, |ui| { + ui.horizontal_wrapped(|ui| { + let txn = Transaction::new(app.ndb).unwrap(); + + let keys = if let Some(keys) = &self.keys { + keys + } else { + return; + }; + + for key in keys { + let profile = app.ndb.get_profile_by_key(&txn, *key).unwrap(); + let url = profile + .record() + .profile() + .expect("should have profile") + .picture() + .expect("should have picture"); + + let expand_size = 10.0; + let anim_speed = 0.05; + + let (rect, size, _resp) = ui::anim::hover_expand( + ui, + egui::Id::new(profile.key().unwrap()), + ui::ProfilePic::default_size(), + expand_size, + anim_speed, + ); + + ui.put(rect, ui::ProfilePic::new(app.img_cache, url).size(size)) + .on_hover_ui_at_pointer(|ui| { + ui.set_max_width(300.0); + ui.add(ui::ProfilePreview::new(&profile, app.img_cache)); + }); + } + }); + }); + } + + fn setup(&mut self, ndb: &Ndb) { + let txn = Transaction::new(ndb).unwrap(); let filters = vec![Filter::new().kinds(vec![0]).build()]; - let cache = ImageCache::new("cache/img".into()); let mut pks = HashSet::new(); let mut keys = HashSet::new(); - for query_result in ndb.query(&txn, &filters, 2000).unwrap() { + for query_result in ndb.query(&txn, &filters, 20000).unwrap() { pks.insert(query_result.note.pubkey()); } @@ -170,44 +211,19 @@ mod preview { keys.insert(profile.key().expect("should not be owned")); } - let keys = keys.into_iter().collect(); - ProfilePicPreview { cache, ndb, keys } + let keys: Vec<ProfileKey> = keys.into_iter().collect(); + info!("Loaded {} profiles", keys.len()); + self.keys = Some(keys); } } - impl View for ProfilePicPreview { - fn ui(&mut self, ui: &mut egui::Ui) { - egui::ScrollArea::both().show(ui, |ui| { - ui.horizontal_wrapped(|ui| { - let txn = Transaction::new(&self.ndb).unwrap(); - for key in &self.keys { - let profile = self.ndb.get_profile_by_key(&txn, *key).unwrap(); - let url = profile - .record() - .profile() - .expect("should have profile") - .picture() - .expect("should have picture"); - - let expand_size = 10.0; - let anim_speed = 0.05; - - let (rect, size, _resp) = ui::anim::hover_expand( - ui, - egui::Id::new(profile.key().unwrap()), - ui::ProfilePic::default_size(), - expand_size, - anim_speed, - ); + impl notedeck::App for ProfilePicPreview { + fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { + if self.keys.is_none() { + self.setup(ctx.ndb); + } - ui.put(rect, ui::ProfilePic::new(&mut self.cache, url).size(size)) - .on_hover_ui_at_pointer(|ui| { - ui.set_max_width(300.0); - ui.add(ui::ProfilePreview::new(&profile, &mut self.cache)); - }); - } - }); - }); + self.show(ctx, ui) } } diff --git a/crates/notedeck_columns/src/ui/profile/preview.rs b/crates/notedeck_columns/src/ui/profile/preview.rs @@ -6,7 +6,7 @@ use egui_extras::Size; use enostr::{NoteId, Pubkey}; use nostrdb::{Ndb, ProfileRecord, Transaction}; -use notedeck::{DataPath, DataPathType, ImageCache, NotedeckTextStyle, UserAccount}; +use notedeck::{ImageCache, NotedeckTextStyle, UserAccount}; pub struct ProfilePreview<'a, 'cache> { profile: &'a ProfileRecord<'a>, @@ -147,21 +147,17 @@ impl egui::Widget for SimpleProfilePreview<'_, '_> { mod previews { use super::*; use crate::test_data::test_profile_record; - use crate::ui::{Preview, PreviewConfig, View}; + use crate::ui::{Preview, PreviewConfig}; + use notedeck::{App, AppContext}; pub struct ProfilePreviewPreview<'a> { profile: ProfileRecord<'a>, - cache: ImageCache, } impl ProfilePreviewPreview<'_> { pub fn new() -> Self { let profile = test_profile_record(); - let path = DataPath::new("previews") - .path(DataPathType::Cache) - .join(ImageCache::rel_dir()); - let cache = ImageCache::new(path); - ProfilePreviewPreview { profile, cache } + ProfilePreviewPreview { profile } } } @@ -171,9 +167,9 @@ mod previews { } } - impl View for ProfilePreviewPreview<'_> { - fn ui(&mut self, ui: &mut egui::Ui) { - ProfilePreview::new(&self.profile, &mut self.cache).ui(ui); + impl App for ProfilePreviewPreview<'_> { + fn update(&mut self, app: &mut AppContext<'_>, ui: &mut egui::Ui) { + ProfilePreview::new(&self.profile, app.img_cache).ui(ui); } } diff --git a/crates/notedeck_columns/src/ui/relay.rs b/crates/notedeck_columns/src/ui/relay.rs @@ -184,6 +184,7 @@ fn get_connection_icon(status: &RelayStatus) -> egui::Image<'static> { mod preview { use super::*; use crate::test_data::sample_pool; + use notedeck::{App, AppContext}; pub struct RelayViewPreview { pool: RelayPool, @@ -197,8 +198,8 @@ mod preview { } } - impl View for RelayViewPreview { - fn ui(&mut self, ui: &mut egui::Ui) { + impl App for RelayViewPreview { + fn update(&mut self, _app: &mut AppContext<'_>, ui: &mut egui::Ui) { self.pool.try_recv(); RelayView::new(RelayPoolManager::new(&mut self.pool)).ui(ui); } diff --git a/preview b/preview @@ -1,4 +1,5 @@ #!/usr/bin/env bash # pass --mobile for mobile previews -RUST_LOG=info cargo run --bin ui_preview --release -- "$@" +#RUST_LOG=info cargo run --bin ui_preview --features profiling --release -- "$@" +cargo run --bin ui_preview --release -- "$@"