dioxus_nox_timer/
countdown.rs1use dioxus::prelude::*;
4
5use crate::time;
6use crate::types::{CountdownControls, TimerState};
7
8pub fn use_countdown(
26 on_complete: Option<Callback<()>>,
27) -> (Signal<i64>, Signal<TimerState>, CountdownControls) {
28 let mut end_time_ms = use_signal(|| None::<i64>);
30 let mut paused_remaining_ms = use_signal(|| None::<i64>);
32 let mut state = use_signal(|| TimerState::Idle);
34 let mut remaining = use_signal(|| 0i64);
36 let mut generation = use_signal(|| 0u32);
38
39 let gen_value = *generation.read();
41 use_effect(move || {
42 let current_gen = gen_value;
43 spawn(async move {
44 loop {
45 time::sleep_ms(100).await;
46 if *generation.read() != current_gen {
48 break;
49 }
50 let current_state = *state.read();
51 if current_state != TimerState::Running {
52 if current_state == TimerState::Idle || current_state == TimerState::Complete {
53 break;
54 }
55 continue;
57 }
58 let end_val = *end_time_ms.read();
59 if let Some(end) = end_val {
60 let now = time::now_ms();
61 let remain_ms = (end - now).max(0);
62 let remain_secs = (remain_ms + 999) / 1000; remaining.set(remain_secs);
64
65 if remain_ms <= 0 {
66 state.set(TimerState::Complete);
67 remaining.set(0);
68 end_time_ms.set(None);
69 if let Some(ref cb) = on_complete {
70 cb.call(());
71 }
72 break;
73 }
74 }
75 }
76 });
77 });
78
79 let controls = CountdownControls {
80 start: Callback::new(move |duration_secs: i64| {
81 let now = time::now_ms();
82 end_time_ms.set(Some(now + duration_secs * 1000));
83 paused_remaining_ms.set(None);
84 remaining.set(duration_secs);
85 state.set(TimerState::Running);
86 let next_gen = generation.read().wrapping_add(1);
87 generation.set(next_gen);
88 }),
89 pause: Callback::new(move |()| {
90 if *state.read() == TimerState::Running {
91 let end = *end_time_ms.read();
92 if let Some(end) = end {
93 let remain = (end - time::now_ms()).max(0);
94 paused_remaining_ms.set(Some(remain));
95 end_time_ms.set(None);
96 state.set(TimerState::Paused);
97 }
98 }
99 }),
100 resume: Callback::new(move |()| {
101 if *state.read() == TimerState::Paused {
102 let remain = *paused_remaining_ms.read();
103 if let Some(remain) = remain {
104 let now = time::now_ms();
105 end_time_ms.set(Some(now + remain));
106 paused_remaining_ms.set(None);
107 state.set(TimerState::Running);
108 let next_gen = generation.read().wrapping_add(1);
109 generation.set(next_gen);
110 }
111 }
112 }),
113 skip: Callback::new(move |()| {
114 end_time_ms.set(None);
115 paused_remaining_ms.set(None);
116 remaining.set(0);
117 state.set(TimerState::Idle);
118 let next_gen = generation.read().wrapping_add(1);
119 generation.set(next_gen);
120 }),
121 adjust: Callback::new(move |delta_secs: i64| {
122 let current_state = *state.read();
123 match current_state {
124 TimerState::Running => {
125 let end = *end_time_ms.read();
126 if let Some(end) = end {
127 let new_end = end + delta_secs * 1000;
128 let now = time::now_ms();
129 if new_end <= now {
130 end_time_ms.set(Some(now));
131 remaining.set(0);
132 } else {
133 end_time_ms.set(Some(new_end));
134 let remain_ms = new_end - now;
135 remaining.set((remain_ms + 999) / 1000);
136 }
137 }
138 }
139 TimerState::Paused => {
140 let remain = *paused_remaining_ms.read();
141 if let Some(remain) = remain {
142 let new_remain = (remain + delta_secs * 1000).max(0);
143 paused_remaining_ms.set(Some(new_remain));
144 remaining.set((new_remain + 999) / 1000);
145 }
146 }
147 _ => {}
148 }
149 }),
150 dismiss: Callback::new(move |()| {
151 if *state.read() == TimerState::Complete {
152 end_time_ms.set(None);
153 paused_remaining_ms.set(None);
154 remaining.set(0);
155 state.set(TimerState::Idle);
156 let next_gen = generation.read().wrapping_add(1);
157 generation.set(next_gen);
158 }
159 }),
160 };
161
162 (remaining, state, controls)
163}