feat(countdown): persist elapsed time (#46)

This commit is contained in:
Jens Krause 2025-01-10 16:01:03 +01:00 committed by GitHub
parent c8af76c9e5
commit 9ea9f88266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 43 deletions

View File

@ -57,6 +57,7 @@ pub struct AppArgs {
pub current_value_pause: Duration, pub current_value_pause: Duration,
pub initial_value_countdown: Duration, pub initial_value_countdown: Duration,
pub current_value_countdown: Duration, pub current_value_countdown: Duration,
pub elapsed_value_countdown: Duration,
pub current_value_timer: Duration, pub current_value_timer: Duration,
} }
@ -80,6 +81,7 @@ impl From<(Args, AppStorage)> for AppArgs {
initial_value_countdown: args.countdown.unwrap_or(stg.inital_value_countdown), initial_value_countdown: args.countdown.unwrap_or(stg.inital_value_countdown),
// invalidate `current_value_countdown` if an initial value is set via args // invalidate `current_value_countdown` if an initial value is set via args
current_value_countdown: args.countdown.unwrap_or(stg.current_value_countdown), current_value_countdown: args.countdown.unwrap_or(stg.current_value_countdown),
elapsed_value_countdown: stg.elapsed_value_countdown,
current_value_timer: stg.current_value_timer, current_value_timer: stg.current_value_timer,
} }
} }
@ -104,6 +106,7 @@ impl App {
current_value_work, current_value_work,
current_value_pause, current_value_pause,
current_value_countdown, current_value_countdown,
elapsed_value_countdown,
current_value_timer, current_value_timer,
content, content,
with_decis, with_decis,
@ -115,12 +118,15 @@ impl App {
app_time: get_app_time(), app_time: get_app_time(),
style, style,
with_decis, with_decis,
countdown: CountdownState::new(ClockState::<clock::Countdown>::new(ClockStateArgs { countdown: CountdownState::new(
initial_value: initial_value_countdown, ClockState::<clock::Countdown>::new(ClockStateArgs {
current_value: current_value_countdown, initial_value: initial_value_countdown,
tick_value: Duration::from_millis(TICK_VALUE_MS), current_value: current_value_countdown,
with_decis, tick_value: Duration::from_millis(TICK_VALUE_MS),
})), with_decis,
}),
elapsed_value_countdown,
),
timer: TimerState::new(ClockState::<clock::Timer>::new(ClockStateArgs { timer: TimerState::new(ClockState::<clock::Timer>::new(ClockStateArgs {
initial_value: Duration::ZERO, initial_value: Duration::ZERO,
current_value: current_value_timer, current_value: current_value_timer,
@ -247,6 +253,7 @@ impl App {
current_value_countdown: Duration::from( current_value_countdown: Duration::from(
*self.countdown.get_clock().get_current_value(), *self.countdown.get_clock().get_current_value(),
), ),
elapsed_value_countdown: Duration::from(*self.countdown.get_elapsed_value()),
current_value_timer: Duration::from(*self.timer.get_clock().get_current_value()), current_value_timer: Duration::from(*self.timer.get_clock().get_current_value()),
} }
} }

View File

@ -25,6 +25,7 @@ pub struct AppStorage {
// countdown // countdown
pub inital_value_countdown: Duration, pub inital_value_countdown: Duration,
pub current_value_countdown: Duration, pub current_value_countdown: Duration,
pub elapsed_value_countdown: Duration,
// timer // timer
pub current_value_timer: Duration, pub current_value_timer: Duration,
} }
@ -50,6 +51,7 @@ impl Default for AppStorage {
// countdown // countdown
inital_value_countdown: DEFAULT_COUNTDOWN, inital_value_countdown: DEFAULT_COUNTDOWN,
current_value_countdown: DEFAULT_COUNTDOWN, current_value_countdown: DEFAULT_COUNTDOWN,
elapsed_value_countdown: Duration::ZERO,
// timer // timer
current_value_timer: Duration::ZERO, current_value_timer: Duration::ZERO,
} }

View File

@ -91,6 +91,27 @@ pub struct ClockStateArgs {
} }
impl<T> ClockState<T> { impl<T> ClockState<T> {
pub fn with_mode(mut self, mode: Mode) -> Self {
self.mode = mode;
self
}
pub fn get_mode(&self) -> &Mode {
&self.mode
}
pub fn is_initial(&self) -> bool {
self.mode == Mode::Initial
}
pub fn run(&mut self) {
self.mode = Mode::Tick
}
pub fn is_running(&self) -> bool {
self.mode == Mode::Tick
}
pub fn toggle_pause(&mut self) { pub fn toggle_pause(&mut self) {
self.mode = if self.mode == Mode::Tick { self.mode = if self.mode == Mode::Tick {
Mode::Pause Mode::Pause
@ -185,6 +206,7 @@ impl<T> ClockState<T> {
}; };
self.update_format(); self.update_format();
} }
pub fn edit_current_down(&mut self) { pub fn edit_current_down(&mut self) {
self.current_value = match self.mode { self.current_value = match self.mode {
Mode::Editable(Time::Decis, _) => { Mode::Editable(Time::Decis, _) => {
@ -203,22 +225,6 @@ impl<T> ClockState<T> {
self.update_mode(); self.update_mode();
} }
pub fn get_mode(&self) -> &Mode {
&self.mode
}
pub fn run(&mut self) {
self.mode = Mode::Tick
}
pub fn is_running(&self) -> bool {
self.mode == Mode::Tick
}
pub fn is_initial(&self) -> bool {
self.mode == Mode::Initial
}
pub fn is_edit_mode(&self) -> bool { pub fn is_edit_mode(&self) -> bool {
matches!(self.mode, Mode::Editable(_, _)) matches!(self.mode, Mode::Editable(_, _))
} }

View File

@ -10,9 +10,10 @@ use std::{cmp::max, time::Duration};
use crate::{ use crate::{
common::Style, common::Style,
constants::TICK_VALUE_MS, constants::TICK_VALUE_MS,
duration::DurationEx,
events::{Event, EventHandler}, events::{Event, EventHandler},
utils::center, utils::center,
widgets::clock::{self, ClockState, ClockStateArgs, ClockWidget}, widgets::clock::{self, ClockState, ClockStateArgs, ClockWidget, Mode as ClockMode},
}; };
/// State for Countdown Widget /// State for Countdown Widget
@ -20,26 +21,34 @@ use crate::{
pub struct CountdownState { pub struct CountdownState {
/// clock to count down /// clock to count down
clock: ClockState<clock::Countdown>, clock: ClockState<clock::Countdown>,
/// clock to count up afterwards /// clock to count time after `DONE` - similar to Mission Elapsed Time (MET)
timer: ClockState<clock::Timer>, elapsed_clock: ClockState<clock::Timer>,
} }
impl CountdownState { impl CountdownState {
pub fn new(clock: ClockState<clock::Countdown>) -> Self { pub fn new(clock: ClockState<clock::Countdown>, elapsed_value: Duration) -> Self {
Self { Self {
clock, clock,
timer: ClockState::<clock::Timer>::new(ClockStateArgs { elapsed_clock: ClockState::<clock::Timer>::new(ClockStateArgs {
initial_value: Duration::ZERO, initial_value: Duration::ZERO,
current_value: Duration::ZERO, current_value: elapsed_value,
tick_value: Duration::from_millis(TICK_VALUE_MS), tick_value: Duration::from_millis(TICK_VALUE_MS),
with_decis: false, with_decis: false,
})
// 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
.with_mode(if elapsed_value.gt(&Duration::ZERO) {
ClockMode::Pause
} else {
ClockMode::Initial
}), }),
} }
} }
pub fn set_with_decis(&mut self, with_decis: bool) { pub fn set_with_decis(&mut self, with_decis: bool) {
self.clock.with_decis = with_decis; self.clock.with_decis = with_decis;
self.timer.with_decis = with_decis; self.elapsed_clock.with_decis = with_decis;
} }
pub fn get_clock(&self) -> &ClockState<clock::Countdown> { pub fn get_clock(&self) -> &ClockState<clock::Countdown> {
@ -47,7 +56,11 @@ impl CountdownState {
} }
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
self.clock.is_running() || self.timer.is_running() self.clock.is_running() || self.elapsed_clock.is_running()
}
pub fn get_elapsed_value(&self) -> &DurationEx {
self.elapsed_clock.get_current_value()
} }
} }
@ -59,9 +72,9 @@ impl EventHandler for CountdownState {
if !self.clock.is_done() { if !self.clock.is_done() {
self.clock.tick(); self.clock.tick();
} else { } else {
self.timer.tick(); self.elapsed_clock.tick();
if self.timer.is_initial() { if self.elapsed_clock.is_initial() {
self.timer.run(); self.elapsed_clock.run();
} }
} }
} }
@ -69,21 +82,21 @@ impl EventHandler for CountdownState {
KeyCode::Char('r') => { KeyCode::Char('r') => {
// reset both clocks // reset both clocks
self.clock.reset(); self.clock.reset();
self.timer.reset(); self.elapsed_clock.reset();
} }
KeyCode::Char('s') => { KeyCode::Char('s') => {
// toggle pause status depending on who is running // toggle pause status depending on which clock is running
if !self.clock.is_done() { if !self.clock.is_done() {
self.clock.toggle_pause(); self.clock.toggle_pause();
} else { } else {
self.timer.toggle_pause(); self.elapsed_clock.toggle_pause();
} }
} }
KeyCode::Char('e') => { KeyCode::Char('e') => {
self.clock.toggle_edit(); self.clock.toggle_edit();
// stop + reset timer entering `edit` mode // stop + reset timer entering `edit` mode
if self.timer.is_running() { if self.elapsed_clock.is_running() {
self.timer.toggle_pause(); self.elapsed_clock.toggle_pause();
} }
} }
KeyCode::Left if edit_mode => { KeyCode::Left if edit_mode => {
@ -94,13 +107,13 @@ impl EventHandler for CountdownState {
} }
KeyCode::Up if edit_mode => { KeyCode::Up if edit_mode => {
self.clock.edit_up(); self.clock.edit_up();
// whenever clock value is changed, reset timer // whenever `clock`'s value is changed, reset `elapsed_clock`
self.timer.reset(); self.elapsed_clock.reset();
} }
KeyCode::Down if edit_mode => { KeyCode::Down if edit_mode => {
self.clock.edit_down(); self.clock.edit_down();
// whenever clock value is changed, reset timer // whenever clock value is changed, reset timer
self.timer.reset(); self.elapsed_clock.reset();
} }
_ => return Some(event), _ => return Some(event),
}, },
@ -125,13 +138,16 @@ impl StatefulWidget for Countdown {
format!( format!(
"Countdown {} +{}", "Countdown {} +{}",
state.clock.get_mode(), state.clock.get_mode(),
state.timer.get_current_value().to_string_with_decis() state
.elapsed_clock
.get_current_value()
.to_string_with_decis()
) )
} else { } else {
format!( format!(
"Countdown {} +{}", "Countdown {} +{}",
state.clock.get_mode(), state.clock.get_mode(),
state.timer.get_current_value() state.elapsed_clock.get_current_value()
) )
} }
} else { } else {