diff --git a/src/app.rs b/src/app.rs index 0f475170..b77d5608 100644 --- a/src/app.rs +++ b/src/app.rs @@ -15,7 +15,7 @@ use crate::{ notecache::{CachedNote, NoteCache}, notes_holder::NotesHolderStorage, profile::Profile, - storage::{Directory, FileKeyStorage, KeyStorageType}, + storage::{DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageType}, subscriptions::{SubKind, Subscriptions}, support::Support, thread::Thread, @@ -23,7 +23,7 @@ use crate::{ ui::{self, DesktopSidePanel}, unknowns::UnknownIds, view_state::ViewState, - DataPaths, Result, + Result, }; use enostr::{ClientMessage, RelayEvent, RelayMessage, RelayPool}; @@ -67,6 +67,7 @@ pub struct Damus { frame_history: crate::frame_history::FrameHistory, + pub path: DataPath, // TODO: make these bitflags pub debug: bool, pub since_optimize: bool, @@ -660,31 +661,25 @@ impl Damus { let data_path = parsed_args .datapath .unwrap_or(data_path.as_ref().to_str().expect("db path ok").to_string()); - let dbpath = parsed_args.dbpath.unwrap_or(data_path.clone()); + let path = DataPath::new(&data_path); + let dbpath_ = path.path(DataPathType::Db); + let dbpath = dbpath_.to_str().unwrap(); - let _ = std::fs::create_dir_all(dbpath.clone()); + let _ = std::fs::create_dir_all(dbpath); - let imgcache_dir = format!("{}/{}", data_path, ImageCache::rel_datadir()); + let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir()); let _ = std::fs::create_dir_all(imgcache_dir.clone()); let mut config = Config::new(); config.set_ingester_threads(4); let keystore = if parsed_args.use_keystore { - if let Ok(keys_path) = DataPaths::Keys.get_path() { - if let Ok(selected_key_path) = DataPaths::SelectedKey.get_path() { - KeyStorageType::FileSystem(FileKeyStorage::new( - Directory::new(keys_path), - Directory::new(selected_key_path), - )) - } else { - error!("Could not find path for selected key"); - KeyStorageType::None - } - } else { - error!("Could not find data path for keys"); - KeyStorageType::None - } + 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 }; @@ -725,7 +720,7 @@ impl Damus { .get_selected_account() .as_ref() .map(|a| a.pubkey.bytes()); - let ndb = Ndb::new(&dbpath, &config).expect("ndb"); + let ndb = Ndb::new(dbpath, &config).expect("ndb"); let mut columns: Columns = Columns::new(); for col in parsed_args.columns { @@ -740,6 +735,9 @@ impl Damus { columns.new_column_picker(); } + let app_rect_handler = AppSizeHandler::new(&path); + let support = Support::new(&path); + Self { pool, debug, @@ -750,7 +748,7 @@ impl Damus { profiles: NotesHolderStorage::default(), drafts: Drafts::default(), state: DamusState::Initializing, - img_cache: ImageCache::new(imgcache_dir.into()), + img_cache: ImageCache::new(imgcache_dir), note_cache: NoteCache::default(), columns, textmode: parsed_args.textmode, @@ -758,8 +756,9 @@ impl Damus { accounts, frame_history: FrameHistory::default(), view_state: ViewState::default(), - app_rect_handler: AppSizeHandler::default(), - support: Support::default(), + path, + app_rect_handler, + support, } } @@ -819,12 +818,17 @@ impl Damus { columns.add_new_timeline_column(timeline); - let imgcache_dir = data_path.as_ref().join(ImageCache::rel_datadir()); + let path = DataPath::new(&data_path); + let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir()); let _ = std::fs::create_dir_all(imgcache_dir.clone()); let debug = true; + let app_rect_handler = AppSizeHandler::new(&path); + let support = Support::new(&path); + let mut config = Config::new(); config.set_ingester_threads(2); + Self { debug, unknown_ids: UnknownIds::default(), @@ -839,12 +843,20 @@ impl Damus { note_cache: NoteCache::default(), columns, textmode: false, - ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"), + ndb: Ndb::new( + path.path(DataPathType::Db) + .to_str() + .expect("db path should be ok"), + &config, + ) + .expect("ndb"), accounts: AccountManager::new(KeyStorageType::None), frame_history: FrameHistory::default(), view_state: ViewState::default(), - app_rect_handler: AppSizeHandler::default(), - support: Support::default(), + + path, + app_rect_handler, + support, } } diff --git a/src/app_creation.rs b/src/app_creation.rs index f8f28f3e..55924f07 100644 --- a/src/app_creation.rs +++ b/src/app_creation.rs @@ -1,25 +1,32 @@ -use crate::app_size_handler::AppSizeHandler; -use crate::app_style::{ - create_custom_style, dark_mode, desktop_font_size, light_mode, mobile_font_size, +use crate::{ + app_size_handler::AppSizeHandler, + app_style::{create_custom_style, dark_mode, desktop_font_size, light_mode, mobile_font_size}, + fonts::setup_fonts, + storage::DataPath, }; -use crate::fonts::setup_fonts; + use eframe::NativeOptions; //pub const UI_SCALE_FACTOR: f32 = 0.2; -pub fn generate_native_options() -> NativeOptions { - generate_native_options_with_builder_modifiers(|builder| { +pub fn generate_native_options(paths: DataPath) -> NativeOptions { + let window_builder = Box::new(move |builder: egui::ViewportBuilder| { let builder = builder .with_fullsize_content_view(true) .with_titlebar_shown(false) .with_title_shown(false); - if let Some(window_size) = AppSizeHandler::default().get_app_size() { + if let Some(window_size) = AppSizeHandler::new(&paths).get_app_size() { builder.with_inner_size(window_size) } else { builder } - }) + }); + + eframe::NativeOptions { + window_builder: Some(window_builder), + ..Default::default() + } } fn generate_native_options_with_builder_modifiers( diff --git a/src/app_size_handler.rs b/src/app_size_handler.rs index f1140bba..3e8d718a 100644 --- a/src/app_size_handler.rs +++ b/src/app_size_handler.rs @@ -1,15 +1,12 @@ use std::time::{Duration, Instant}; use egui::Context; -use tracing::{error, info}; +use tracing::info; -use crate::{ - storage::{write_file, Directory}, - DataPaths, -}; +use crate::storage::{write_file, DataPath, DataPathType, Directory}; pub struct AppSizeHandler { - directory: Option, + directory: Directory, saved_size: Option, last_saved: Instant, } @@ -17,15 +14,9 @@ pub struct AppSizeHandler { static FILE_NAME: &str = "app_size.json"; static DELAY: Duration = Duration::from_millis(500); -impl Default for AppSizeHandler { - fn default() -> Self { - let directory = match DataPaths::Setting.get_path() { - Ok(path) => Some(Directory::new(path)), - Err(e) => { - error!("Could not load settings path: {}", e); - None - } - }; +impl AppSizeHandler { + pub fn new(path: &DataPath) -> Self { + let directory = Directory::new(path.path(DataPathType::Setting)); Self { directory, @@ -33,16 +24,12 @@ impl Default for AppSizeHandler { last_saved: Instant::now() - DELAY, } } -} -impl AppSizeHandler { pub fn try_save_app_size(&mut self, ctx: &Context) { - if let Some(interactor) = &self.directory { - // 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 - if self.last_saved.elapsed() >= DELAY { - internal_try_save_app_size(interactor, &mut self.saved_size, ctx); - self.last_saved = Instant::now(); - } + // 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 + if self.last_saved.elapsed() >= DELAY { + internal_try_save_app_size(&self.directory, &mut self.saved_size, ctx); + self.last_saved = Instant::now(); } } @@ -51,14 +38,12 @@ impl AppSizeHandler { return self.saved_size; } - if let Some(directory) = &self.directory { - if let Ok(file_contents) = directory.get_file(FILE_NAME.to_owned()) { - if let Ok(rect) = serde_json::from_str::(&file_contents) { - return Some(rect); - } - } else { - info!("Could not find {}", FILE_NAME); + if let Ok(file_contents) = self.directory.get_file(FILE_NAME.to_owned()) { + if let Ok(rect) = serde_json::from_str::(&file_contents) { + return Some(rect); } + } else { + info!("Could not find {}", FILE_NAME); } None diff --git a/src/bin/notedeck.rs b/src/bin/notedeck.rs index ced8f42a..4b226a9b 100644 --- a/src/bin/notedeck.rs +++ b/src/bin/notedeck.rs @@ -1,7 +1,12 @@ #![warn(clippy::all, rust_2018_idioms)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release -use notedeck::app_creation::generate_native_options; -use notedeck::Damus; +use notedeck::{ + app_creation::generate_native_options, + storage::{DataPath, DataPathType}, + Damus, +}; +use std::{path::PathBuf, str::FromStr}; + use tracing_subscriber::EnvFilter; // Entry point for wasm @@ -12,34 +17,35 @@ use tracing_subscriber::EnvFilter; #[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); + #[allow(unused_variables)] // need guard to live for lifetime of program - let (maybe_non_blocking, maybe_guard) = - if let Ok(log_path) = notedeck::DataPaths::Log.get_path() { - // Setup logging to file - use std::panic; + let (maybe_non_blocking, maybe_guard) = { + let log_path = path.path(DataPathType::Log); + // Setup logging to file + use std::panic; - use tracing::error; - use tracing_appender::{ - non_blocking, - rolling::{RollingFileAppender, Rotation}, - }; - - let file_appender = RollingFileAppender::new( - Rotation::DAILY, - log_path, - format!("notedeck-{}.log", env!("CARGO_PKG_VERSION")), - ); - panic::set_hook(Box::new(|panic_info| { - error!("Notedeck panicked: {:?}", panic_info); - })); - - let (non_blocking, _guard) = non_blocking(file_appender); - - (Some(non_blocking), Some(_guard)) - } else { - (None, None) + use tracing::error; + use tracing_appender::{ + non_blocking, + rolling::{RollingFileAppender, Rotation}, }; + let file_appender = RollingFileAppender::new( + Rotation::DAILY, + log_path, + format!("notedeck-{}.log", env!("CARGO_PKG_VERSION")), + ); + panic::set_hook(Box::new(|panic_info| { + error!("Notedeck panicked: {:?}", panic_info); + })); + + let (non_blocking, _guard) = non_blocking(file_appender); + + (Some(non_blocking), Some(_guard)) + }; + // Log to stdout (if you run with `RUST_LOG=debug`). if let Some(non_blocking_writer) = maybe_non_blocking { use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -68,8 +74,14 @@ async fn main() { let _res = eframe::run_native( "Damus NoteDeck", - generate_native_options(), - Box::new(|cc| Ok(Box::new(Damus::new(cc, ".", std::env::args().collect())))), + generate_native_options(path), + Box::new(|cc| { + Ok(Box::new(Damus::new( + cc, + base_path, + std::env::args().collect(), + ))) + }), ); } diff --git a/src/imgcache.rs b/src/imgcache.rs index bcf357ea..5916ac60 100644 --- a/src/imgcache.rs +++ b/src/imgcache.rs @@ -25,8 +25,8 @@ impl ImageCache { } } - pub fn rel_datadir() -> &'static str { - "cache/img" + pub fn rel_dir() -> &'static str { + "img" } pub fn write(cache_dir: &path::Path, url: &str, data: ColorImage) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index 4ed39bfd..70565dd5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,12 +46,11 @@ mod view_state; #[macro_use] mod test_utils; -mod storage; +pub mod storage; pub use app::Damus; pub use error::Error; pub use profile::DisplayName; -pub use storage::DataPaths; #[cfg(target_os = "android")] use winit::platform::android::EventLoopBuilderExtAndroid; diff --git a/src/storage/file_storage.rs b/src/storage/file_storage.rs index e9191745..753075d6 100644 --- a/src/storage/file_storage.rs +++ b/src/storage/file_storage.rs @@ -8,33 +8,45 @@ use std::{ use crate::Error; -pub enum DataPaths { +#[derive(Debug, Clone)] +pub struct DataPath { + base: PathBuf, +} + +impl DataPath { + pub fn new(base: impl AsRef) -> Self { + let base = base.as_ref().to_path_buf(); + Self { base } + } + + pub fn default_base() -> Option { + dirs::data_local_dir().map(|pb| pb.join("notedeck")) + } +} + +pub enum DataPathType { Log, Setting, Keys, SelectedKey, + Db, + Cache, } -impl DataPaths { - pub fn get_path(&self) -> Result { - let base_path = match self { - DataPaths::Log => dirs::data_local_dir(), - DataPaths::Setting | DataPaths::Keys | DataPaths::SelectedKey => { - dirs::config_local_dir() - } +impl DataPath { + pub fn rel_path(&self, typ: DataPathType) -> PathBuf { + match typ { + DataPathType::Log => PathBuf::from("logs"), + DataPathType::Setting => PathBuf::from("settings"), + DataPathType::Keys => PathBuf::from("storage").join("accounts"), + DataPathType::SelectedKey => PathBuf::from("storage").join("selected_account"), + DataPathType::Db => PathBuf::from("db"), + DataPathType::Cache => PathBuf::from("cache"), } - .ok_or(Error::Generic( - "Could not open well known OS directory".to_owned(), - ))?; + } - let specific_path = match self { - DataPaths::Log => PathBuf::from("logs"), - DataPaths::Setting => PathBuf::from("settings"), - DataPaths::Keys => PathBuf::from("storage").join("accounts"), - DataPaths::SelectedKey => PathBuf::from("storage").join("selected_account"), - }; - - Ok(base_path.join("notedeck").join(specific_path)) + pub fn path(&self, typ: DataPathType) -> PathBuf { + self.base.join(self.rel_path(typ)) } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index b84fd420..4896af22 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -3,8 +3,8 @@ mod file_storage; pub use file_key_storage::FileKeyStorage; pub use file_storage::write_file; -pub use file_storage::DataPaths; pub use file_storage::Directory; +pub use file_storage::{DataPath, DataPathType}; #[cfg(target_os = "macos")] mod security_framework_key_storage; diff --git a/src/support.rs b/src/support.rs index 3d8222e3..aef26f3a 100644 --- a/src/support.rs +++ b/src/support.rs @@ -1,26 +1,20 @@ use tracing::error; -use crate::{storage::Directory, DataPaths}; +use crate::storage::{DataPath, DataPathType, Directory}; pub struct Support { - directory: Option, + directory: Directory, mailto_url: String, most_recent_log: Option, } -fn new_log_dir() -> Option { - match DataPaths::Log.get_path() { - Ok(path) => Some(Directory::new(path)), - Err(e) => { - error!("Support could not open directory: {}", e.to_string()); - None - } - } +fn new_log_dir(paths: &DataPath) -> Directory { + Directory::new(paths.path(DataPathType::Log)) } -impl Default for Support { - fn default() -> Self { - let directory = new_log_dir(); +impl Support { + pub fn new(path: &DataPath) -> Self { + let directory = new_log_dir(path); Self { mailto_url: MailtoBuilder::new(SUPPORT_EMAIL.to_string()) @@ -39,11 +33,7 @@ static EMAIL_TEMPLATE: &str = "Describe the bug you have encountered:\n<-- your impl Support { pub fn refresh(&mut self) { - if let Some(directory) = &self.directory { - self.most_recent_log = get_log_str(directory); - } else { - self.directory = new_log_dir(); - } + self.most_recent_log = get_log_str(&self.directory); } pub fn get_mailto_url(&self) -> &str { @@ -51,7 +41,7 @@ impl Support { } pub fn get_log_dir(&self) -> Option<&str> { - self.directory.as_ref()?.file_path.to_str() + self.directory.file_path.to_str() } pub fn get_most_recent_log(&self) -> Option<&String> { diff --git a/src/ui/profile/preview.rs b/src/ui/profile/preview.rs index 223ae17f..6327d033 100644 --- a/src/ui/profile/preview.rs +++ b/src/ui/profile/preview.rs @@ -1,5 +1,6 @@ use crate::app_style::NotedeckTextStyle; use crate::imgcache::ImageCache; +use crate::storage::{DataPath, DataPathType}; use crate::ui::ProfilePic; use crate::user_account::UserAccount; use crate::{colors, images, DisplayName}; @@ -126,7 +127,10 @@ mod previews { impl<'a> ProfilePreviewPreview<'a> { pub fn new() -> Self { let profile = test_profile_record(); - let cache = ImageCache::new(ImageCache::rel_datadir().into()); + let path = DataPath::new("previews") + .path(DataPathType::Cache) + .join(ImageCache::rel_dir()); + let cache = ImageCache::new(path); ProfilePreviewPreview { profile, cache } } } diff --git a/src/ui_preview/main.rs b/src/ui_preview/main.rs index 9a0d6f99..186d3dd6 100644 --- a/src/ui_preview/main.rs +++ b/src/ui_preview/main.rs @@ -1,10 +1,11 @@ -use notedeck::app_creation::{ - generate_mobile_emulator_native_options, generate_native_options, setup_cc, -}; -use notedeck::ui::add_column::AddColumnView; use notedeck::ui::{ - account_login_view::AccountLoginView, account_management::AccountsView, DesktopSidePanel, - PostView, Preview, PreviewApp, PreviewConfig, ProfilePic, ProfilePreview, RelayView, + account_login_view::AccountLoginView, account_management::AccountsView, + add_column::AddColumnView, DesktopSidePanel, PostView, Preview, PreviewApp, PreviewConfig, + ProfilePic, ProfilePreview, RelayView, +}; +use notedeck::{ + app_creation::{generate_mobile_emulator_native_options, generate_native_options, setup_cc}, + storage::DataPath, }; use std::env; @@ -30,7 +31,8 @@ impl PreviewRunner { let native_options = if self.force_mobile { generate_mobile_emulator_native_options() } else { - generate_native_options() + // TODO: tmp preview pathbuf? + generate_native_options(DataPath::new("previews")) }; let is_mobile = self.force_mobile;