Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 7 additions & 12 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -405,23 +405,18 @@ impl App {
let handle_app_events = |app: &mut Self, event: events::AppEvent| -> Result<bool> {
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 {
let msg = match type_id {
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()
Expand Down
43 changes: 43 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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<String> 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<String> 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]
Expand Down
4 changes: 2 additions & 2 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -24,7 +24,7 @@ pub enum TuiEvent {

#[derive(Clone, Debug)]
pub enum AppEvent {
ClockDone(ClockTypeId, String),
ClockDone(ClockTypeId, ClockName, Option<ClockDescription>),
SetCursor(Option<Position>),
}

Expand Down
26 changes: 18 additions & 8 deletions src/widgets/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -177,7 +177,8 @@ pub const MAX_DONE_COUNT: u64 = RANGE_OF_DONE_COUNT * 5;

pub struct ClockState<T> {
type_id: ClockTypeId,
name: Option<String>,
name: Option<ClockName>,
description: Option<ClockDescription>,
initial_value: DurationEx,
current_value: DurationEx,
prev_value: DurationEx,
Expand All @@ -203,19 +204,25 @@ pub struct ClockStateArgs {
}

impl<T> ClockState<T> {
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
}
Expand Down Expand Up @@ -449,9 +456,10 @@ impl<T> ClockState<T> {
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);
}
Expand Down Expand Up @@ -494,6 +502,7 @@ impl ClockState<Countdown> {
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(),
Expand Down Expand Up @@ -580,6 +589,7 @@ impl ClockState<Timer> {
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(),
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/countdown.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/widgets/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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`
Expand Down
26 changes: 20 additions & 6 deletions src/widgets/pomodoro.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -208,22 +208,36 @@ 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"
} else {
"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) {
Expand Down
Loading