diff --git a/CHANGELOG.md b/CHANGELOG.md index 01fcc05..adcd58b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - (notification) detailed pomodoro status in system notification [#204](https://github.com/sectore/timr-tui/pull/204) - (cli) Add `--auto-switch` argument [#203](https://github.com/sectore/timr-tui/pull/203) +### Fix + +- (notification) show `Pomodoro` or `Tabata` instead of `Countdown` [#209](https://github.com/sectore/timr-tui/pull/209) + ## v1.10.0 - 2026-06-16 ### Features diff --git a/src/app.rs b/src/app.rs index 3e98cb0..4ba8285 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,6 @@ use crate::{ args::Args, - common::{AppEditMode, AppTime, AppTimeFormat, ClockTypeId, Content, Style, Toggle}, + common::{AppEditMode, AppTime, AppTimeFormat, ClockName, ClockTypeId, Content, Style, Toggle}, constants::{TABATA_MAX_ROUNDS, TABATA_PAUSE, TABATA_WORK, TICK_VALUE_MS}, event::Event, events::{self, TuiEventHandler}, @@ -245,7 +245,7 @@ impl App { with_decis, app_tx: Some(app_tx.clone()), }) - .with_name("Timer".to_owned()), + .with_name(ClockName::from("Timer")), vim_motions, ), pomodoro: PomodoroState::new(PomodoroStateArgs { @@ -405,7 +405,7 @@ impl App { let handle_app_events = |app: &mut Self, event: events::AppEvent| -> Result { let mut trigger_redraw = false; match event { - events::AppEvent::ClockDone(type_id, name) => { + events::AppEvent::ClockDone(type_id, name, description) => { debug!("AppEvent::ClockDone"); if app.notification == Toggle::On { @@ -413,15 +413,10 @@ impl App { ClockTypeId::Timer => { format!("{name} stopped by reaching its maximum value.") } - ClockTypeId::Countdown if app.content == Content::Pomodoro => { - let prefix = if app.pomodoro.is_tabata() { - "Tabata" - } else { - "Pomodoro" - }; - format!("{prefix} {name} done!") - } - _ => format!("{type_id:?} {name} done!"), + _ => match description { + Some(desc) => format!("{name} {desc} done!"), + None => format!("{name} done!"), + }, }; // notification let result = notify_rust::Notification::new() diff --git a/src/common.rs b/src/common.rs index 416622b..19d7c21 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 std::fmt; use strum::EnumString; use time::{OffsetDateTime, format_description}; @@ -50,6 +51,48 @@ pub enum ClockTypeId { Event, } +#[derive(Debug, Clone)] +pub struct ClockName(String); + +impl fmt::Display for ClockName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for ClockName { + fn from(s: String) -> Self { + Self(s) + } +} + +impl From<&str> for ClockName { + fn from(s: &str) -> Self { + Self(s.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub struct ClockDescription(String); + +impl fmt::Display for ClockDescription { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for ClockDescription { + fn from(s: String) -> Self { + Self(s) + } +} + +impl From<&str> for ClockDescription { + fn from(s: &str) -> Self { + Self(s.to_owned()) + } +} + #[derive(Debug, Copy, Clone, ValueEnum, Default, Serialize, Deserialize)] pub enum Style { #[default] diff --git a/src/events.rs b/src/events.rs index a8c222d..3ea1761 100644 --- a/src/events.rs +++ b/src/events.rs @@ -6,7 +6,7 @@ use tokio::sync::mpsc; use tokio::time::interval; use tokio_stream::{StreamMap, wrappers::IntervalStream}; -use crate::common::ClockTypeId; +use crate::common::{ClockDescription, ClockName, ClockTypeId}; use crate::constants::TICK_VALUE_MS; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -24,7 +24,7 @@ pub enum TuiEvent { #[derive(Clone, Debug)] pub enum AppEvent { - ClockDone(ClockTypeId, String), + ClockDone(ClockTypeId, ClockName, Option), SetCursor(Option), } diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index 9c7c690..510a982 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -13,7 +13,7 @@ use ratatui::{ use crate::widgets::clock_elements::FOUR_DIGITS_WIDTH; use crate::{ - common::{ClockTypeId, Style as DigitStyle}, + common::{ClockDescription, ClockName, ClockTypeId, Style as DigitStyle}, duration::{ ClockDuration, DurationEx, MAX_DURATION, ONE_DAY, ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_YEAR, @@ -177,7 +177,8 @@ pub const MAX_DONE_COUNT: u64 = RANGE_OF_DONE_COUNT * 5; pub struct ClockState { type_id: ClockTypeId, - name: Option, + name: Option, + description: Option, initial_value: DurationEx, current_value: DurationEx, prev_value: DurationEx, @@ -203,19 +204,25 @@ pub struct ClockStateArgs { } impl ClockState { - pub fn with_name(mut self, name: String) -> Self { + pub fn with_name(mut self, name: ClockName) -> Self { self.name = Some(name); self } - pub fn get_name(&self) -> String { - self.name.clone().unwrap_or_default() + pub fn get_name_or_default(&self) -> ClockName { + self.name + .clone() + .unwrap_or_else(|| ClockName::from(format!("{:?}", self.type_id))) } - pub fn set_name(&mut self, name: String) { + pub fn set_name(&mut self, name: ClockName) { self.name = Some(name); } + pub fn set_description(&mut self, description: ClockDescription) { + self.description = Some(description); + } + pub fn get_type_id(&self) -> &ClockTypeId { &self.type_id } @@ -449,9 +456,10 @@ impl ClockState { if !self.is_done() { self.mode = Mode::Done; let type_id = self.get_type_id().clone(); - let name = self.get_name(); + let name = self.get_name_or_default(); + let description = self.description.clone(); if let Some(tx) = &self.app_tx { - _ = tx.send(AppEvent::ClockDone(type_id, name)); + _ = tx.send(AppEvent::ClockDone(type_id, name, description)); }; self.done_count = Some(MAX_DONE_COUNT); } @@ -494,6 +502,7 @@ impl ClockState { let mut instance = Self { type_id: ClockTypeId::Countdown, name: None, + description: None, initial_value: initial_value.into(), current_value: current_value.into(), prev_value: current_value.into(), @@ -580,6 +589,7 @@ impl ClockState { let mut instance = Self { type_id: ClockTypeId::Timer, name: None, + description: None, initial_value: initial_value.into(), current_value: current_value.into(), prev_value: current_value.into(), diff --git a/src/widgets/countdown.rs b/src/widgets/countdown.rs index 3607094..6c9181b 100644 --- a/src/widgets/countdown.rs +++ b/src/widgets/countdown.rs @@ -1,5 +1,5 @@ use crate::{ - common::{AppTime, AppTimeFormat, Style}, + common::{AppTime, AppTimeFormat, ClockName, Style}, constants::TICK_VALUE_MS, duration::{DurationEx, MAX_DURATION}, events::{AppEventTx, TuiEvent, TuiEventHandler}, @@ -76,7 +76,7 @@ impl CountdownState { with_decis: false, app_tx: None, }) - .with_name("MET".to_owned()) + .with_name(ClockName::from("MET")) // A previous `elapsed_value > 0` means the `Clock` was running before, // but not in `Initial` state anymore. Updating `Mode` here // is needed to handle `Event::Tick` in `EventHandler::update` properly diff --git a/src/widgets/event.rs b/src/widgets/event.rs index 0ecbc67..f8b0cd3 100644 --- a/src/widgets/event.rs +++ b/src/widgets/event.rs @@ -12,7 +12,7 @@ use tui_input::Input; use tui_input::backend::crossterm::EventHandler; use crate::{ - common::{AppTime, AppTimeFormat, ClockTypeId, Style as DigitStyle}, + common::{AppTime, AppTimeFormat, ClockName, ClockTypeId, Style as DigitStyle}, duration::CalendarDuration, event::Event, events::{AppEvent, AppEventTx, TuiEvent, TuiEventHandler}, @@ -165,7 +165,8 @@ impl EventState { // send notification _ = self.app_tx.send(AppEvent::ClockDone( ClockTypeId::Event, - self.title.clone().unwrap_or("".into()), + ClockName::from(self.title.clone().unwrap_or_default()), + None, )); } // count (possible) `done` diff --git a/src/widgets/pomodoro.rs b/src/widgets/pomodoro.rs index 0baa95f..f91d374 100644 --- a/src/widgets/pomodoro.rs +++ b/src/widgets/pomodoro.rs @@ -1,5 +1,5 @@ use crate::{ - common::Style, + common::{ClockDescription, ClockName, Style}, constants::{TABATA_MAX_ROUNDS, TABATA_PAUSE, TABATA_WORK, TICK_VALUE_MS}, events::{AppEventTx, TuiEvent, TuiEventHandler}, widgets::clock::{ClockState, ClockStateArgs, ClockWidget, Countdown}, @@ -208,13 +208,25 @@ impl PomodoroState { } } + fn pomodoro_name(&self) -> ClockName { + ClockName::from(if self.is_tabata() { + "Tabata" + } else { + "Pomodoro" + }) + } + fn update_work_name(&mut self) { - let name = format!("work ({})", self.round_label()); - self.get_clock_work_mut().set_name(name); + let name = self.pomodoro_name(); + let description = ClockDescription::from(format!("work ({})", self.round_label())); + let clock = self.get_clock_work_mut(); + clock.set_name(name); + clock.set_description(description); } fn update_pause_name(&mut self) { - let name = format!( + let name = self.pomodoro_name(); + let description = ClockDescription::from(format!( "{} ({})", if self.pause_duration.is_special_round(self.round) { "pause special" @@ -222,8 +234,10 @@ impl PomodoroState { "pause" }, self.round_label() - ); - self.get_clock_pause_mut().set_name(name); + )); + let clock = self.get_clock_pause_mut(); + clock.set_name(name); + clock.set_description(description); } fn update_clock_names(&mut self) {