use std::time::{Duration, Instant};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ConfirmKeyAction {
FireNow,
Armed,
Disarmed,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CountdownTickOutcome {
NotArmed,
Pending,
ReadyToFire,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub enum ConfirmButton {
Confirm,
#[default]
Cancel,
}
#[derive(Debug, Default)]
pub struct ConfirmModal {
focus: ConfirmButton,
started_at: Option<Instant>,
}
impl ConfirmModal {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
self.started_at = None;
self.focus = ConfirmButton::default();
}
pub fn focused_button(&self) -> ConfirmButton {
self.focus
}
pub fn focus_confirm(&mut self) {
self.focus = ConfirmButton::Confirm;
}
pub fn focus_cancel(&mut self) {
self.focus = ConfirmButton::Cancel;
}
pub fn toggle_focus(&mut self) {
self.focus = match self.focus {
ConfirmButton::Confirm => ConfirmButton::Cancel,
ConfirmButton::Cancel => ConfirmButton::Confirm,
};
}
pub fn is_armed(&self) -> bool {
self.started_at.is_some()
}
pub fn press_y(&mut self, now: Instant, total: Duration) -> ConfirmKeyAction {
if total.is_zero() {
self.started_at = None;
return ConfirmKeyAction::FireNow;
}
if self.started_at.is_some() {
self.started_at = None;
ConfirmKeyAction::Disarmed
} else {
self.started_at = Some(now);
ConfirmKeyAction::Armed
}
}
pub fn dismiss(&mut self) {
self.started_at = None;
}
pub fn tick(&mut self, now: Instant, total: Duration) -> CountdownTickOutcome {
let Some(started) = self.started_at else {
return CountdownTickOutcome::NotArmed;
};
if total.is_zero() {
self.started_at = None;
return CountdownTickOutcome::NotArmed;
}
if now.saturating_duration_since(started) < total {
CountdownTickOutcome::Pending
} else {
self.started_at = None;
CountdownTickOutcome::ReadyToFire
}
}
pub fn progress(&self, now: Instant, total: Duration) -> f64 {
let Some(started) = self.started_at else {
return 0.0;
};
if total.is_zero() {
return 0.0;
}
let elapsed = now.saturating_duration_since(started).as_secs_f64();
(elapsed / total.as_secs_f64()).min(1.0)
}
pub fn remaining_secs(&self, now: Instant, total: Duration) -> u64 {
let Some(started) = self.started_at else {
return 0;
};
if total.is_zero() {
return 0;
}
let remaining = total.saturating_sub(now.saturating_duration_since(started));
if remaining.is_zero() {
return 0;
}
let extra = if remaining.subsec_nanos() > 0 { 1 } else { 0 };
remaining.as_secs() + extra
}
}