feat(settings): persist settings to storage
This commit is contained in:
committed by
William Casarin
parent
5280028a82
commit
b8207106d7
@@ -3,12 +3,11 @@ use crate::i18n::Localization;
|
||||
use crate::persist::{AppSizeHandler, ZoomHandler};
|
||||
use crate::wallet::GlobalWallet;
|
||||
use crate::zaps::Zaps;
|
||||
use crate::JobPool;
|
||||
use crate::{
|
||||
frame_history::FrameHistory, AccountStorage, Accounts, AppContext, Args, DataPath,
|
||||
DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, ThemeHandler,
|
||||
UnknownIds,
|
||||
DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, UnknownIds,
|
||||
};
|
||||
use crate::{JobPool, SettingsHandler};
|
||||
use egui::Margin;
|
||||
use egui::ThemePreference;
|
||||
use egui_winit::clipboard::Clipboard;
|
||||
@@ -19,6 +18,7 @@ use std::collections::BTreeSet;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use tracing::{error, info};
|
||||
use unic_langid::{LanguageIdentifier, LanguageIdentifierError};
|
||||
|
||||
pub enum AppAction {
|
||||
Note(NoteAction),
|
||||
@@ -40,7 +40,7 @@ pub struct Notedeck {
|
||||
global_wallet: GlobalWallet,
|
||||
path: DataPath,
|
||||
args: Args,
|
||||
theme: ThemeHandler,
|
||||
settings_handler: SettingsHandler,
|
||||
app: Option<Rc<RefCell<dyn App>>>,
|
||||
zoom: ZoomHandler,
|
||||
app_size: AppSizeHandler,
|
||||
@@ -159,7 +159,10 @@ impl Notedeck {
|
||||
1024usize * 1024usize * 1024usize * 1024usize
|
||||
};
|
||||
|
||||
let theme = ThemeHandler::new(&path);
|
||||
let mut settings_handler = SettingsHandler::new(&path);
|
||||
|
||||
settings_handler.load();
|
||||
|
||||
let config = Config::new().set_ingester_threads(2).set_mapsize(map_size);
|
||||
|
||||
let keystore = if parsed_args.use_keystore {
|
||||
@@ -231,6 +234,16 @@ impl Notedeck {
|
||||
|
||||
// Initialize localization
|
||||
let mut i18n = Localization::new();
|
||||
|
||||
let setting_locale: Result<LanguageIdentifier, LanguageIdentifierError> =
|
||||
settings_handler.locale().parse();
|
||||
|
||||
if setting_locale.is_ok() {
|
||||
if let Err(err) = i18n.set_locale(setting_locale.unwrap()) {
|
||||
error!("{err}");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(locale) = &parsed_args.locale {
|
||||
if let Err(err) = i18n.set_locale(locale.to_owned()) {
|
||||
error!("{err}");
|
||||
@@ -250,7 +263,7 @@ impl Notedeck {
|
||||
global_wallet,
|
||||
path: path.clone(),
|
||||
args: parsed_args,
|
||||
theme,
|
||||
settings_handler,
|
||||
app: None,
|
||||
zoom,
|
||||
app_size,
|
||||
@@ -279,7 +292,7 @@ impl Notedeck {
|
||||
global_wallet: &mut self.global_wallet,
|
||||
path: &self.path,
|
||||
args: &self.args,
|
||||
theme: &mut self.theme,
|
||||
settings_handler: &mut self.settings_handler,
|
||||
clipboard: &mut self.clipboard,
|
||||
zaps: &mut self.zaps,
|
||||
frame_history: &mut self.frame_history,
|
||||
@@ -297,7 +310,7 @@ impl Notedeck {
|
||||
}
|
||||
|
||||
pub fn theme(&self) -> ThemePreference {
|
||||
self.theme.load()
|
||||
self.settings_handler.theme()
|
||||
}
|
||||
|
||||
pub fn unrecognized_args(&self) -> &BTreeSet<String> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
account::accounts::Accounts, frame_history::FrameHistory, i18n::Localization,
|
||||
wallet::GlobalWallet, zaps::Zaps, Args, DataPath, Images, JobPool, NoteCache, ThemeHandler,
|
||||
wallet::GlobalWallet, zaps::Zaps, Args, DataPath, Images, JobPool, NoteCache, SettingsHandler,
|
||||
UnknownIds,
|
||||
};
|
||||
use egui_winit::clipboard::Clipboard;
|
||||
@@ -20,7 +20,7 @@ pub struct AppContext<'a> {
|
||||
pub global_wallet: &'a mut GlobalWallet,
|
||||
pub path: &'a DataPath,
|
||||
pub args: &'a Args,
|
||||
pub theme: &'a mut ThemeHandler,
|
||||
pub settings_handler: &'a mut SettingsHandler,
|
||||
pub clipboard: &'a mut Clipboard,
|
||||
pub zaps: &'a mut Zaps,
|
||||
pub frame_history: &'a mut FrameHistory,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod app_size;
|
||||
mod settings_handler;
|
||||
mod theme_handler;
|
||||
mod token_handler;
|
||||
mod zoom;
|
||||
|
||||
pub use app_size::AppSizeHandler;
|
||||
pub use settings_handler::SettingsHandler;
|
||||
pub use theme_handler::ThemeHandler;
|
||||
pub use token_handler::TokenHandler;
|
||||
pub use zoom::ZoomHandler;
|
||||
|
||||
208
crates/notedeck/src/persist/settings_handler.rs
Normal file
208
crates/notedeck/src/persist/settings_handler.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
use crate::{
|
||||
storage::{self, delete_file},
|
||||
DataPath, DataPathType, Directory,
|
||||
};
|
||||
use egui::ThemePreference;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, info};
|
||||
|
||||
const THEME_FILE: &str = "theme.txt";
|
||||
const SETTINGS_FILE: &str = "settings.json";
|
||||
|
||||
const DEFAULT_THEME: ThemePreference = ThemePreference::Dark;
|
||||
const DEFAULT_LOCALE: &str = "es-US";
|
||||
const DEFAULT_ZOOM_FACTOR: f32 = 1.0;
|
||||
const DEFAULT_SHOW_SOURCE_CLIENT: &str = "hide";
|
||||
|
||||
fn deserialize_theme(serialized_theme: &str) -> Option<ThemePreference> {
|
||||
match serialized_theme {
|
||||
"dark" => Some(ThemePreference::Dark),
|
||||
"light" => Some(ThemePreference::Light),
|
||||
"system" => Some(ThemePreference::System),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub theme: ThemePreference,
|
||||
pub locale: String,
|
||||
pub zoom_factor: f32,
|
||||
pub show_source_client: String,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
// Use the same fallback theme as before
|
||||
Self {
|
||||
theme: DEFAULT_THEME,
|
||||
locale: DEFAULT_LOCALE.to_string(),
|
||||
zoom_factor: DEFAULT_ZOOM_FACTOR,
|
||||
show_source_client: "Hide".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct SettingsHandler {
|
||||
directory: Directory,
|
||||
current_settings: Option<Settings>,
|
||||
}
|
||||
|
||||
impl SettingsHandler {
|
||||
fn read_legacy_theme(&self) -> Option<ThemePreference> {
|
||||
match self.directory.get_file(THEME_FILE.to_string()) {
|
||||
Ok(contents) => deserialize_theme(contents.trim()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_to_settings_file(&mut self) -> Result<(), ()> {
|
||||
// if theme.txt exists migrate
|
||||
if let Some(theme_from_file) = self.read_legacy_theme() {
|
||||
info!("migrating theme preference from theme.txt file");
|
||||
_ = delete_file(&self.directory.file_path, THEME_FILE.to_string());
|
||||
|
||||
self.current_settings = Some(Settings {
|
||||
theme: theme_from_file,
|
||||
..Settings::default()
|
||||
});
|
||||
|
||||
self.save();
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(path: &DataPath) -> Self {
|
||||
let directory = Directory::new(path.path(DataPathType::Setting));
|
||||
let current_settings: Option<Settings> = None;
|
||||
|
||||
Self {
|
||||
directory,
|
||||
current_settings,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&mut self) {
|
||||
if self.migrate_to_settings_file().is_ok() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.directory.get_file(SETTINGS_FILE.to_string()) {
|
||||
Ok(contents_str) => {
|
||||
// Parse JSON content
|
||||
match serde_json::from_str::<Settings>(&contents_str) {
|
||||
Ok(settings) => {
|
||||
self.current_settings = Some(settings);
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Invalid settings format. Using defaults");
|
||||
self.current_settings = Some(Settings::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Could not read settings. Using defaults");
|
||||
self.current_settings = Some(Settings::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
let settings = self.current_settings.as_ref().unwrap();
|
||||
match serde_json::to_string(settings) {
|
||||
Ok(serialized) => {
|
||||
if let Err(e) = storage::write_file(
|
||||
&self.directory.file_path,
|
||||
SETTINGS_FILE.to_string(),
|
||||
&serialized,
|
||||
) {
|
||||
error!("Could not save settings: {}", e);
|
||||
} else {
|
||||
info!("Settings saved successfully");
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Failed to serialize settings: {}", e),
|
||||
};
|
||||
}
|
||||
|
||||
fn get_settings_mut(&mut self) -> &mut Settings {
|
||||
if self.current_settings.is_none() {
|
||||
self.current_settings = Some(Settings::default());
|
||||
}
|
||||
self.current_settings.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_theme(&mut self, theme: ThemePreference) {
|
||||
self.get_settings_mut().theme = theme;
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn set_locale<S>(&mut self, locale: S)
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.get_settings_mut().locale = locale.into();
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn set_zoom_factor(&mut self, zoom_factor: f32) {
|
||||
self.get_settings_mut().zoom_factor = zoom_factor;
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn set_show_source_client<S>(&mut self, option: S)
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.get_settings_mut().show_source_client = option.into();
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn update_batch<F>(&mut self, update_fn: F)
|
||||
where
|
||||
F: FnOnce(&mut Settings),
|
||||
{
|
||||
let settings = self.get_settings_mut();
|
||||
update_fn(settings);
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn update_settings(&mut self, new_settings: Settings) {
|
||||
self.current_settings = Some(new_settings);
|
||||
self.save();
|
||||
}
|
||||
|
||||
pub fn theme(&self) -> ThemePreference {
|
||||
self.current_settings
|
||||
.as_ref()
|
||||
.map(|s| s.theme)
|
||||
.unwrap_or(DEFAULT_THEME)
|
||||
}
|
||||
|
||||
pub fn locale(&self) -> String {
|
||||
self.current_settings
|
||||
.as_ref()
|
||||
.map(|s| s.locale.clone())
|
||||
.unwrap_or_else(|| DEFAULT_LOCALE.to_string())
|
||||
}
|
||||
|
||||
pub fn zoom_factor(&self) -> f32 {
|
||||
self.current_settings
|
||||
.as_ref()
|
||||
.map(|s| s.zoom_factor)
|
||||
.unwrap_or(DEFAULT_ZOOM_FACTOR)
|
||||
}
|
||||
|
||||
pub fn show_source_client(&self) -> String {
|
||||
self.current_settings
|
||||
.as_ref()
|
||||
.map(|s| s.show_source_client.to_string())
|
||||
.unwrap_or(DEFAULT_SHOW_SOURCE_CLIENT.to_string())
|
||||
}
|
||||
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
self.current_settings.is_some()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user