move Notedeck to notedeck crate

This commit is contained in:
kieran
2025-01-20 22:32:00 +00:00
parent 43637f52bb
commit 86e68b1c29
17 changed files with 309 additions and 312 deletions

View File

@@ -1,11 +1,16 @@
//#[cfg(target_os = "android")]
//use egui_android::run_android;
use crate::app::Notedeck;
use notedeck_columns::Damus;
use winit::platform::android::activity::AndroidApp;
use winit::platform::android::EventLoopBuilderExtAndroid;
use crate::setup::setup_chrome;
use notedeck::Notedeck;
use serde_json::Value;
use std::fs;
use std::path::PathBuf;
#[no_mangle]
#[tokio::main]
pub async fn android_main(app: AndroidApp) {
@@ -50,18 +55,17 @@ pub async fn android_main(app: AndroidApp) {
"Damus Notedeck",
options,
Box::new(move |cc| {
let mut notedeck = Notedeck::new(&cc.egui_ctx, path, &app_args);
let ctx = &cc.egui_ctx;
let mut notedeck = Notedeck::new(ctx, path, &app_args);
setup_chrome(ctx, &notedeck.args(), notedeck.theme());
let damus = Damus::new(&mut notedeck.app_context(), &app_args);
notedeck.add_app(damus);
notedeck.set_app(damus);
Ok(Box::new(notedeck))
}),
);
}
use serde_json::Value;
use std::fs;
use std::path::PathBuf;
/*
Read args from a config file:
- allows use of more interesting args w/o risk of checking them in by mistake

View File

@@ -1,265 +0,0 @@
use crate::{app_size::AppSizeHandler, persist_zoom::ZoomHandler, setup::setup_cc, theme};
use notedeck::{
Accounts, AppContext, Args, DataPath, DataPathType, Directory, FileKeyStorage, ImageCache,
KeyStorageType, NoteCache, ThemeHandler, UnknownIds,
};
use enostr::RelayPool;
use nostrdb::{Config, Ndb, Transaction};
use notedeck_columns::ui::relay_debug::RelayDebugView;
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use tracing::{error, info};
/// Our browser app state
pub struct Notedeck {
ndb: Ndb,
img_cache: ImageCache,
unknown_ids: UnknownIds,
pool: RelayPool,
note_cache: NoteCache,
accounts: Accounts,
path: DataPath,
args: Args,
theme: ThemeHandler,
tabs: Tabs,
app_rect_handler: AppSizeHandler,
zoom_handler: ZoomHandler,
}
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 {
/// Called by the frame work to save state before shutdown.
fn save(&mut self, _storage: &mut dyn eframe::Storage) {
//eframe::set_value(storage, eframe::APP_KEY, self);
}
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// TODO: render chrome
#[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);
self.zoom_handler.try_save_zoom_factor(ctx);
if self.args.relay_debug {
if self.pool.debug.is_none() {
self.pool.use_debug();
}
if let Some(debug) = &mut self.pool.debug {
RelayDebugView::window(ctx, debug);
}
}
#[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
.unwrap_or(notedeck::ui::is_compiled_as_mobile());
// Some people have been running notedeck in debug, let's catch that!
if !parsed_args.tests && cfg!(debug_assertions) && !parsed_args.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!("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!("---------------------------------");
panic!();
}
setup_cc(ctx, is_mobile, parsed_args.light);
let data_path = parsed_args
.datapath
.clone()
.unwrap_or(data_path.as_ref().to_str().expect("db path ok").to_string());
let path = DataPath::new(&data_path);
let dbpath_str = parsed_args
.dbpath
.clone()
.unwrap_or_else(|| path.path(DataPathType::Db).to_str().unwrap().to_string());
let _ = std::fs::create_dir_all(&dbpath_str);
let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir());
let _ = std::fs::create_dir_all(imgcache_dir.clone());
let mapsize = if cfg!(target_os = "windows") {
// 16 Gib on windows because it actually creates the file
1024usize * 1024usize * 1024usize * 16usize
} else {
// 1 TiB for everything else since its just virtually mapped
1024usize * 1024usize * 1024usize * 1024usize
};
let theme = ThemeHandler::new(&path);
ctx.options_mut(|o| {
let cur_theme = theme.load();
info!("Loaded theme {:?} from disk", cur_theme);
o.theme_preference = cur_theme;
});
ctx.set_visuals_of(
egui::Theme::Dark,
theme::dark_mode(notedeck::ui::is_compiled_as_mobile()),
);
ctx.set_visuals_of(egui::Theme::Light, theme::light_mode());
let config = Config::new().set_ingester_threads(4).set_mapsize(mapsize);
let keystore = if parsed_args.use_keystore {
let keys_path = path.path(DataPathType::Keys);
let selected_key_path = path.path(DataPathType::SelectedKey);
KeyStorageType::FileSystem(FileKeyStorage::new(
Directory::new(keys_path),
Directory::new(selected_key_path),
))
} else {
KeyStorageType::None
};
let mut accounts = Accounts::new(keystore, parsed_args.relays.clone());
let num_keys = parsed_args.keys.len();
let mut unknown_ids = UnknownIds::default();
let ndb = Ndb::new(&dbpath_str, &config).expect("ndb");
{
let txn = Transaction::new(&ndb).expect("txn");
for key in &parsed_args.keys {
info!("adding account: {}", &key.pubkey);
accounts
.add_account(key.clone())
.process_action(&mut unknown_ids, &ndb, &txn);
}
}
if num_keys != 0 {
accounts.select_account(0);
}
// AccountManager will setup the pool on first update
let mut pool = RelayPool::new();
{
let ctx = ctx.clone();
if let Err(err) = pool.add_multicast_relay(move || ctx.request_repaint()) {
error!("error setting up multicast relay: {err}");
}
}
let img_cache = ImageCache::new(imgcache_dir);
let note_cache = NoteCache::default();
let unknown_ids = UnknownIds::default();
let tabs = Tabs::new(None);
let app_rect_handler = AppSizeHandler::new(&path);
let zoom_handler = ZoomHandler::new(&path);
if let Some(zoom_factor) = zoom_handler.get_zoom_factor() {
ctx.set_zoom_factor(zoom_factor);
}
// migrate
if let Err(e) = img_cache.migrate_v0() {
error!("error migrating image cache: {e}");
}
Self {
ndb,
img_cache,
app_rect_handler,
unknown_ids,
pool,
note_cache,
accounts,
path: path.clone(),
args: parsed_args,
theme,
tabs,
zoom_handler,
}
}
pub fn app_context(&mut self) -> AppContext<'_> {
AppContext {
ndb: &mut self.ndb,
img_cache: &mut self.img_cache,
unknown_ids: &mut self.unknown_ids,
pool: &mut self.pool,
note_cache: &mut self.note_cache,
accounts: &mut self.accounts,
path: &self.path,
args: &self.args,
theme: &mut self.theme,
}
}
pub fn add_app<T: notedeck::App + 'static>(&mut self, app: T) {
self.tabs.app = Some(Rc::new(RefCell::new(app)));
}
}
struct Tabs {
app: Option<Rc<RefCell<dyn notedeck::App>>>,
}
impl Tabs {
pub fn new(app: Option<Rc<RefCell<dyn notedeck::App>>>) -> Self {
Self { app }
}
}

View File

@@ -1,31 +0,0 @@
use std::time::Duration;
use egui::Context;
use notedeck::{DataPath, DataPathType};
use crate::timed_serializer::TimedSerializer;
pub struct AppSizeHandler {
serializer: TimedSerializer<egui::Vec2>,
}
impl AppSizeHandler {
pub fn new(path: &DataPath) -> Self {
let serializer =
TimedSerializer::new(path, DataPathType::Setting, "app_size.json".to_owned())
.with_delay(Duration::from_millis(500));
Self { serializer }
}
pub fn try_save_app_size(&mut self, ctx: &Context) {
// There doesn't seem to be a way to check if user is resizing window, so if the rect is different than last saved, we'll wait DELAY before saving again to avoid spamming io
let cur_size = ctx.input(|i| i.screen_rect.size());
self.serializer.try_save(cur_size);
}
pub fn get_app_size(&self) -> Option<egui::Vec2> {
self.serializer.get_item()
}
}

View File

@@ -1,13 +1,6 @@
pub mod app_size;
pub mod fonts;
pub mod persist_zoom;
pub mod setup;
pub mod theme;
pub mod timed_serializer;
mod app;
pub use app::Notedeck;
#[cfg(target_os = "android")]
mod android;

View File

@@ -1,7 +1,8 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use notedeck_chrome::{setup::generate_native_options, Notedeck};
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
// hide console window on Windows in release
use notedeck_chrome::setup::{generate_native_options, setup_chrome};
use notedeck::{DataPath, DataPathType};
use notedeck::{DataPath, DataPathType, Notedeck};
use notedeck_columns::Damus;
use tracing_subscriber::EnvFilter;
@@ -72,10 +73,13 @@ async fn main() {
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 ctx = &cc.egui_ctx;
let mut notedeck = Notedeck::new(ctx, base_path, &args);
setup_chrome(ctx, &notedeck.args(), notedeck.theme());
let damus = Damus::new(&mut notedeck.app_context(), &args);
notedeck.add_app(damus);
// TODO: move "chrome" frame over Damus app somehow
notedeck.set_app(damus);
Ok(Box::new(notedeck))
}),

View File

@@ -1,26 +0,0 @@
use egui::Context;
use notedeck::{DataPath, DataPathType};
use crate::timed_serializer::TimedSerializer;
pub struct ZoomHandler {
serializer: TimedSerializer<f32>,
}
impl ZoomHandler {
pub fn new(path: &DataPath) -> Self {
let serializer =
TimedSerializer::new(path, DataPathType::Setting, "zoom_level.json".to_owned());
Self { serializer }
}
pub fn try_save_zoom_factor(&mut self, ctx: &Context) {
let cur_zoom_level = ctx.zoom_factor();
self.serializer.try_save(cur_zoom_level);
}
pub fn get_zoom_factor(&self) -> Option<f32> {
self.serializer.get_item()
}
}

View File

@@ -1,6 +1,5 @@
use notedeck::DataPath;
use notedeck_chrome::setup::generate_native_options;
use notedeck_chrome::Notedeck;
use notedeck::{DataPath, Notedeck};
use notedeck_chrome::setup::{generate_native_options, setup_chrome};
use notedeck_columns::ui::configure_deck::ConfigureDeckView;
use notedeck_columns::ui::edit_deck::EditDeckView;
use notedeck_columns::ui::profile::EditProfileView;
@@ -31,9 +30,12 @@ impl PreviewRunner {
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 ctx = &cc.egui_ctx;
notedeck.add_app(PreviewApp::new(preview));
let mut notedeck = Notedeck::new(ctx, &base_path, &args);
setup_chrome(ctx, &notedeck.args(), notedeck.theme());
notedeck.set_app(PreviewApp::new(preview));
Ok(Box::new(notedeck))
}),

View File

@@ -1,9 +1,35 @@
use crate::{app_size::AppSizeHandler, fonts, theme};
use crate::{fonts, theme};
use eframe::NativeOptions;
use notedeck::DataPath;
use egui::ThemePreference;
use notedeck::{AppSizeHandler, DataPath};
use tracing::info;
pub fn setup_cc(ctx: &egui::Context, is_mobile: bool, light: bool) {
pub fn setup_chrome(ctx: &egui::Context, args: &notedeck::Args, theme: ThemePreference) {
let is_mobile = args
.is_mobile
.unwrap_or(notedeck::ui::is_compiled_as_mobile());
// Some people have been running notedeck in debug, let's catch that!
if !args.tests && cfg!(debug_assertions) && !args.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!("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!("---------------------------------");
panic!();
}
ctx.options_mut(|o| {
info!("Loaded theme {:?} from disk", theme);
o.theme_preference = theme;
});
ctx.set_visuals_of(egui::Theme::Dark, theme::dark_mode(is_mobile));
ctx.set_visuals_of(egui::Theme::Light, theme::light_mode());
setup_cc(ctx, is_mobile);
}
pub fn setup_cc(ctx: &egui::Context, is_mobile: bool) {
fonts::setup_fonts(ctx);
//ctx.set_pixels_per_point(ctx.pixels_per_point() + UI_SCALE_FACTOR);
@@ -14,12 +40,6 @@ pub fn setup_cc(ctx: &egui::Context, is_mobile: bool, light: bool) {
egui_extras::install_image_loaders(ctx);
if light {
ctx.set_visuals(theme::light_mode())
} else {
ctx.set_visuals(theme::dark_mode(is_mobile));
}
ctx.all_styles_mut(|style| theme::add_custom_style(is_mobile, style));
}

View File

@@ -1,86 +0,0 @@
use std::time::{Duration, Instant};
use notedeck::{storage, DataPath, DataPathType, Directory};
use serde::{Deserialize, Serialize};
use tracing::info;
pub struct TimedSerializer<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> {
directory: Directory,
file_name: String,
delay: Duration,
last_saved: Instant,
saved_item: Option<T>,
}
impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerializer<T> {
pub fn new(path: &DataPath, path_type: DataPathType, file_name: String) -> Self {
let directory = Directory::new(path.path(path_type));
let delay = Duration::from_millis(1000);
Self {
directory,
file_name,
delay,
last_saved: Instant::now() - delay,
saved_item: None,
}
}
pub fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
fn should_save(&self) -> bool {
self.last_saved.elapsed() >= self.delay
}
// returns whether successful
pub fn try_save(&mut self, cur_item: T) -> bool {
if self.should_save() {
if let Some(saved_item) = self.saved_item {
if saved_item != cur_item {
return self.save(cur_item);
}
} else {
return self.save(cur_item);
}
}
false
}
pub fn get_item(&self) -> Option<T> {
if self.saved_item.is_some() {
return self.saved_item;
}
if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) {
if let Ok(item) = serde_json::from_str::<T>(&file_contents) {
return Some(item);
}
} else {
info!("Could not find file {}", self.file_name);
}
None
}
fn save(&mut self, cur_item: T) -> bool {
if let Ok(serialized_item) = serde_json::to_string(&cur_item) {
if storage::write_file(
&self.directory.file_path,
self.file_name.clone(),
&serialized_item,
)
.is_ok()
{
info!("wrote item {}", serialized_item);
self.last_saved = Instant::now();
self.saved_item = Some(cur_item);
return true;
}
}
false
}
}