1use dioxus::prelude::*;
2use std::fmt::Display;
3
4#[cfg(target_arch = "wasm32")]
5use async_std::task::sleep;
6#[cfg(target_arch = "wasm32")]
7use instant::{Duration, Instant};
8#[cfg(not(target_arch = "wasm32"))]
9use std::time::{Duration, Instant};
10#[cfg(not(target_arch = "wasm32"))]
11use tokio::time::sleep;
12
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub enum TimerState {
15 Inactive,
16 Working,
17 Finished,
18 Paused,
19}
20
21impl Display for TimerState {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 let text = match self {
24 TimerState::Inactive => "Inactive",
25 TimerState::Working => "Working",
26 TimerState::Finished => "Finished",
27 TimerState::Paused => "Paused",
28 };
29 write!(f, "{text}")
30 }
31}
32
33#[derive(Debug, Clone)]
34pub struct DioxusTimer {
35 preset_duration: Duration,
36 target_time: Instant,
37 state: TimerState,
38 current_time: Instant,
40 paused_time: Option<Instant>,
42}
43
44impl DioxusTimer {
45 pub fn new() -> Self {
47 let current_time = Instant::now();
48 let target_time = current_time;
49 Self {
50 preset_duration: Duration::ZERO,
51 target_time,
52 state: TimerState::Inactive,
53 current_time,
54 paused_time: None,
55 }
56 }
57
58 pub fn set_preset_time(&mut self, preset_duration: Duration) {
60 if self.state == TimerState::Finished {
61 return;
62 }
63 self.preset_duration = preset_duration;
64 self.target_time = self
65 .current_time
66 .checked_add(preset_duration)
67 .unwrap_or(self.current_time);
68 }
69
70 pub fn remaining_time(&self) -> Duration {
72 self.target_time
73 .checked_duration_since(self.current_time)
74 .unwrap_or(Duration::ZERO)
75 }
76
77 pub fn state(&self) -> TimerState {
79 self.state
80 }
81
82 pub fn start(&mut self) {
86 match self.state {
87 TimerState::Inactive => {
88 if self.preset_duration.is_zero() {
89 return;
90 }
91 self.target_time = self
92 .current_time
93 .checked_add(self.preset_duration)
94 .unwrap_or(self.current_time);
95 self.state = TimerState::Working;
96 }
97 TimerState::Paused => {
98 self.state = TimerState::Working;
99 }
100 _ => {}
101 }
102 }
103
104 pub fn pause(&mut self) {
106 if let TimerState::Working = self.state {
107 self.state = TimerState::Paused;
108 self.paused_time = Some(Instant::now());
109 }
110 }
111
112 pub fn reset(&mut self) {
116 if self.state == TimerState::Finished {
117 self.state = TimerState::Inactive;
118 return;
119 }
120 self.target_time = self
121 .current_time
122 .checked_add(self.preset_duration)
123 .unwrap_or(self.current_time);
124 }
125
126 pub fn update(&mut self) {
132 self.current_time = Instant::now();
133 match self.state {
134 TimerState::Working => {
135 if self
136 .target_time
137 .checked_duration_since(self.current_time)
138 .is_none()
139 {
140 self.state = TimerState::Finished;
141 }
142 }
143 TimerState::Paused => {
144 self.target_time = self
145 .target_time
146 .checked_add(self.current_time - self.paused_time.unwrap())
147 .unwrap_or(self.current_time);
148 self.paused_time = Some(self.current_time);
149 }
150 TimerState::Inactive => {
151 self.reset();
152 }
153 _ => {}
154 }
155 }
156}
157
158impl Default for DioxusTimer {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl Display for DioxusTimer {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 let rem_time = self.remaining_time().as_secs();
167 write!(
168 f,
169 "{:0>2}:{:0>2}:{:0>2}",
170 rem_time / 3600,
171 rem_time % 3600 / 60,
172 rem_time % 60,
173 )
174 }
175}
176
177pub fn use_timer(tick: Duration) -> Signal<DioxusTimer> {
192 let mut timer = use_signal(DioxusTimer::new);
193 use_future(move || async move {
194 loop {
195 timer.write().update();
196 sleep(tick).await;
197 }
198 });
199 timer
200}