fjordgard

A desktop clock application
Log | Files | Refs | README | LICENSE

commit 6a90ef4b75d5cf8fac94a11f6bd3e0c0455a4677
parent 8322ab86f1e79514395ff925a4021313484caa77
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu, 19 Jun 2025 21:18:57 -0700

Allow config to be saved and loaded

Diffstat:
MCargo.lock | 52++++++++++++++++++++++++++++++++++++++++++++++++++--
MCargo.toml | 4++++
Msrc/background.rs | 3+--
Msrc/config.rs | 47++++++++++++++++++++++++++++++++++++++++++++---
Msrc/main.rs | 3++-
Msrc/settings.rs | 17+++++++++++++++--
6 files changed, 116 insertions(+), 10 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -165,6 +165,12 @@ dependencies = [ ] [[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -986,12 +992,21 @@ dependencies = [ ] [[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys 0.5.0", +] + +[[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] @@ -1001,11 +1016,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] [[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + +[[package]] name = "dispatch" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1283,7 +1310,9 @@ dependencies = [ name = "fjordgard" version = "0.1.0" dependencies = [ + "anyhow", "chrono", + "directories", "env_logger", "fjordgard-unsplash", "fjordgard-weather", @@ -1291,6 +1320,8 @@ dependencies = [ "log", "open", "rfd", + "serde", + "serde_json", "strum", "tokio", ] @@ -3185,6 +3216,12 @@ dependencies = [ ] [[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] name = "orbclient" version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3697,6 +3734,17 @@ dependencies = [ ] [[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", +] + +[[package]] name = "ref-cast" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -10,7 +10,9 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.98" chrono = "0.4.41" +directories = "6.0.0" env_logger = "0.11.8" fjordgard-unsplash = { version = "0.1.0", path = "crates/unsplash" } fjordgard-weather = { version = "0.1.0", path = "crates/weather" } @@ -18,5 +20,7 @@ iced = { version = "0.13.1", features = ["tokio", "canvas", "image", "svg"] } log = "0.4.27" open = "5.3.2" rfd = "0.15.3" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" strum = { version = "0.27.1", features = ["derive"] } tokio = { version = "1.45.1", features = ["full"] } diff --git a/src/background.rs b/src/background.rs @@ -3,8 +3,7 @@ use fjordgard_unsplash::{ model::{Collection, CollectionPhotos, CollectionPhotosOptions, Format, PhotoFetchOptions}, }; use iced::{ - Color, ContentFit, Element, Length, Point, Renderer, Size, Task, Theme, - mouse, + Color, ContentFit, Element, Length, Point, Renderer, Size, Task, Theme, mouse, widget::{button, canvas, container, image, row, stack, text}, }; use log::{debug, error}; diff --git a/src/config.rs b/src/config.rs @@ -1,4 +1,12 @@ -#[derive(Debug, Clone, Copy, PartialEq, strum::Display, strum::VariantArray)] +use std::fs; + +use anyhow::bail; +use directories::ProjectDirs; +use serde::{Deserialize, Serialize}; + +#[derive( + Serialize, Deserialize, Debug, Clone, Copy, PartialEq, strum::Display, strum::VariantArray, +)] pub enum BackgroundMode { Unsplash, Solid, @@ -24,14 +32,14 @@ impl BackgroundMode { } } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone)] pub struct Location { pub longitude: f64, pub latitude: f64, pub name: Option<String>, } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone)] pub struct Config { pub time_format: String, pub background_mode: BackgroundMode, @@ -40,6 +48,39 @@ pub struct Config { pub location: Option<Location>, } +impl Config { + pub fn load() -> anyhow::Result<Config> { + if let Some(dir) = ProjectDirs::from("net.sivory", "", "fjordgard") { + let config_file = dir.config_dir().join("config.json"); + + if !config_file.exists() { + return Ok(Config::default()); + } + + let data = fs::read_to_string(config_file)?; + + Ok(serde_json::from_str(&data)?) + } else { + Ok(Config::default()) + } + } + + pub async fn save(&self) -> anyhow::Result<()> { + if let Some(dir) = ProjectDirs::from("net.sivory", "", "fjordgard") { + let config_dir = dir.config_dir(); + tokio::fs::create_dir_all(config_dir).await?; + + let contents = serde_json::to_string(self)?; + + tokio::fs::write(config_dir.join("config.json"), contents).await?; + + Ok(()) + } else { + bail!("no config directory found") + } + } +} + impl Default for Config { fn default() -> Self { Self { diff --git a/src/main.rs b/src/main.rs @@ -76,7 +76,7 @@ impl Fjordgard { let main_window_size = settings.size; let (id, open) = window::open(settings); - let config = Config::default(); + let config = Config::load().unwrap(); let format_string = config.time_format.clone(); let format_parsed = StrftimeItems::new_lenient(&format_string) @@ -106,6 +106,7 @@ impl Fjordgard { Task::batch([ open.map(|_| Message::MainWindowOpened), task.map(Message::Background), + Task::done(Message::RequestForecastUpdate), ]), ) } diff --git a/src/settings.rs b/src/settings.rs @@ -65,6 +65,7 @@ pub enum Message { Save, Committed, + Saved(Result<(), String>), } impl Settings { @@ -217,7 +218,6 @@ impl Settings { Task::none() } Message::Save => { - // TODO; this should also commit to a file let mut config = self.config.borrow_mut(); config.time_format = self.time_format.clone(); @@ -245,8 +245,21 @@ impl Settings { } } - Task::done(Message::Committed) + let cloned = config.clone(); + + Task::batch([ + Task::done(Message::Committed), + Task::future(async move { cloned.save().await }) + .map(|r| Message::Saved(r.map_err(|e| e.to_string()))), + ]) } + Message::Saved(res) => match res { + Err(e) => { + error!("failed to save config: {e}"); + Task::none() + } + Ok(()) => Task::none(), + }, _ => Task::none(), } }