diff --git a/src/app.rs b/src/app.rs index 8591020..f795cbd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -44,6 +44,7 @@ pub struct App { #[allow(dead_code)] // w/ `--features sound` available only sound_path: Option, app_time: AppTime, + app_time_format: AppTimeFormat, countdown: CountdownState, timer: TimerState, pomodoro: PomodoroState, @@ -72,6 +73,7 @@ pub struct AppArgs { pub current_value_timer: Duration, pub app_tx: events::AppEventTx, pub sound_path: Option, + pub footer_toggle_app_time: Toggle, } pub struct FromAppArgs { @@ -131,6 +133,7 @@ impl From for App { sound_path: args.sound, #[cfg(not(feature = "sound"))] sound_path: None, + footer_toggle_app_time: stg.footer_app_time, }) } } @@ -164,6 +167,7 @@ impl App { blink, sound_path, app_tx, + footer_toggle_app_time, } = args; let app_time = get_app_time(); @@ -174,6 +178,7 @@ impl App { sound_path, content, app_time, + app_time_format, style, with_decis, countdown: CountdownState::new(CountdownStateArgs { @@ -204,7 +209,14 @@ impl App { round: pomodoro_round, app_tx: app_tx.clone(), }), - footer: FooterState::new(show_menu, app_time_format), + footer: FooterState::new( + show_menu, + if footer_toggle_app_time == Toggle::On { + Some(app_time_format) + } else { + None + }, + ), } } @@ -222,7 +234,26 @@ impl App { KeyCode::Char('t') => app.content = Content::Timer, KeyCode::Char('p') => app.content = Content::Pomodoro, // toogle app time format - KeyCode::Char(':') => app.footer.toggle_app_time_format(), + KeyCode::Char(':') => { + // + // TODO: Check content != LocalClock + let new_format = match app.footer.app_time_format() { + // footer is hidden in footer -> + None => Some(AppTimeFormat::first()), + Some(v) => { + if v != &AppTimeFormat::last() { + Some(v.next()) + } else { + None + } + } + }; + + if let Some(format) = new_format { + app.app_time_format = format; + } + app.footer.set_app_time_format(new_format); + } // toogle menu KeyCode::Char('m') => app.footer.set_show_menu(!app.footer.get_show_menu()), KeyCode::Char(',') => { @@ -374,7 +405,7 @@ impl App { show_menu: self.footer.get_show_menu(), notification: self.notification, blink: self.blink, - app_time_format: *self.footer.app_time_format(), + app_time_format: self.app_time_format, style: self.style, with_decis: self.with_decis, pomodoro_mode: self.pomodoro.get_mode().clone(), @@ -393,6 +424,7 @@ impl App { ), elapsed_value_countdown: Duration::from(*self.countdown.get_elapsed_value()), current_value_timer: Duration::from(*self.timer.get_clock().get_current_value()), + footer_app_time: self.footer.app_time_format().is_some().into(), } } } diff --git a/src/common.rs b/src/common.rs index 656bb97..b9327a9 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,6 +1,7 @@ use clap::ValueEnum; use ratatui::symbols::shade; use serde::{Deserialize, Serialize}; +use strum::EnumString; use time::OffsetDateTime; use time::format_description; @@ -15,6 +16,8 @@ pub enum Content { Timer, #[value(name = "pomodoro", alias = "p")] Pomodoro, + // #[value(name = "localclock", alias = "l")] + // LocalClock, } #[derive(Clone, Debug)] @@ -71,7 +74,7 @@ impl Style { } } -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Default, PartialEq, EnumString, Serialize, Deserialize)] pub enum AppTimeFormat { /// `hh:mm:ss` #[default] @@ -80,17 +83,22 @@ pub enum AppTimeFormat { HhMm, /// `hh:mm AM` (or PM) Hh12Mm, - /// `` (empty) - Hidden, } impl AppTimeFormat { + pub const fn first() -> Self { + Self::HhMmSs + } + + pub const fn last() -> Self { + Self::Hh12Mm + } + pub fn next(&self) -> Self { match self { AppTimeFormat::HhMmSs => AppTimeFormat::HhMm, AppTimeFormat::HhMm => AppTimeFormat::Hh12Mm, - AppTimeFormat::Hh12Mm => AppTimeFormat::Hidden, - AppTimeFormat::Hidden => AppTimeFormat::HhMmSs, + AppTimeFormat::Hh12Mm => AppTimeFormat::HhMmSs, } } } @@ -113,24 +121,19 @@ impl From for OffsetDateTime { impl AppTime { pub fn format(&self, app_format: &AppTimeFormat) -> String { let parse_str = match app_format { - AppTimeFormat::HhMmSs => Some("[hour]:[minute]:[second]"), - AppTimeFormat::HhMm => Some("[hour]:[minute]"), - AppTimeFormat::Hh12Mm => Some("[hour repr:12 padding:none]:[minute] [period]"), - AppTimeFormat::Hidden => None, + AppTimeFormat::HhMmSs => "[hour]:[minute]:[second]", + AppTimeFormat::HhMm => "[hour]:[minute]", + AppTimeFormat::Hh12Mm => "[hour repr:12 padding:none]:[minute] [period]", }; - if let Some(str) = parse_str { - format_description::parse(str) - .map_err(|_| "parse error") - .and_then(|fd| { - OffsetDateTime::from(*self) - .format(&fd) - .map_err(|_| "format error") - }) - .unwrap_or_else(|e| e.to_string()) - } else { - "".to_owned() - } + format_description::parse(parse_str) + .map_err(|_| "parse error") + .and_then(|fd| { + OffsetDateTime::from(*self) + .format(&fd) + .map_err(|_| "format error") + }) + .unwrap_or_else(|e| e.to_string()) } } @@ -150,6 +153,15 @@ pub enum Toggle { Off, } +impl From for Toggle { + fn from(value: bool) -> Self { + match value { + true => Toggle::On, + false => Toggle::Off, + } + } +} + #[cfg(test)] mod tests { @@ -196,12 +208,5 @@ mod tests { "6:06 PM", "local" ); - // hidden - assert_eq!(AppTime::Utc(dt).format(&AppTimeFormat::Hidden), "", "utc"); - assert_eq!( - AppTime::Local(dt).format(&AppTimeFormat::Hidden), - "", - "local" - ); } } diff --git a/src/storage.rs b/src/storage.rs index ff8796f..000645e 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -3,17 +3,30 @@ use crate::{ widgets::pomodoro::Mode as PomodoroMode, }; use color_eyre::eyre::Result; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::fs; use std::path::PathBuf; use std::time::Duration; +fn deserialize_app_time_format<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + match s.as_str() { + // Hidden is deprecated - use `default` value instead + "Hidden" => Ok(AppTimeFormat::default()), + _ => s.parse().map_err(serde::de::Error::custom), + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct AppStorage { pub content: Content, pub show_menu: bool, pub notification: Toggle, pub blink: Toggle, + #[serde(deserialize_with = "deserialize_app_time_format")] pub app_time_format: AppTimeFormat, pub style: Style, pub with_decis: bool, @@ -31,6 +44,8 @@ pub struct AppStorage { pub elapsed_value_countdown: Duration, // timer pub current_value_timer: Duration, + // footer + pub footer_app_time: Toggle, } impl Default for AppStorage { @@ -60,6 +75,8 @@ impl Default for AppStorage { elapsed_value_countdown: Duration::ZERO, // timer current_value_timer: Duration::ZERO, + // footer + footer_app_time: Toggle::Off, } } } diff --git a/src/widgets/footer.rs b/src/widgets/footer.rs index 988157f..b6cda48 100644 --- a/src/widgets/footer.rs +++ b/src/widgets/footer.rs @@ -13,11 +13,11 @@ use ratatui::{ #[derive(Debug, Clone)] pub struct FooterState { show_menu: bool, - app_time_format: AppTimeFormat, + app_time_format: Option, } impl FooterState { - pub const fn new(show_menu: bool, app_time_format: AppTimeFormat) -> Self { + pub const fn new(show_menu: bool, app_time_format: Option) -> Self { Self { show_menu, app_time_format, @@ -32,12 +32,12 @@ impl FooterState { self.show_menu } - pub const fn app_time_format(&self) -> &AppTimeFormat { + pub const fn app_time_format(&self) -> &Option { &self.app_time_format } - pub fn toggle_app_time_format(&mut self) { - self.app_time_format = self.app_time_format.next(); + pub const fn set_app_time_format(&mut self, value: Option) { + self.app_time_format = value; } } @@ -73,9 +73,9 @@ impl StatefulWidget for Footer { Line::from( match state.app_time_format { // `Hidden` -> no (empty) title - AppTimeFormat::Hidden => "".into(), + None => "".into(), // others -> add some space around - _ => format!(" {} ", self.app_time.format(&state.app_time_format)) + Some(v) => format!(" {} ", self.app_time.format(&v)) } ).right_aligned()) .border_set(border::PLAIN)