use std::{
collections::HashMap,
sync::{Arc, Mutex},
time::Duration,
};
use tokio::time::Instant;
use tokio_util::sync::CancellationToken;
use wayle_core::Property;
use crate::core::notification::Notification;
struct PopupTimer {
started_at: Instant,
duration: Duration,
cancel: CancellationToken,
}
impl PopupTimer {
fn remaining(&self) -> Duration {
self.duration.saturating_sub(self.started_at.elapsed())
}
}
pub(crate) struct PopupTimerManager {
timers: Mutex<HashMap<u32, PopupTimer>>,
popups: Property<Vec<Arc<Notification>>>,
}
impl PopupTimerManager {
pub(crate) fn new(popups: Property<Vec<Arc<Notification>>>) -> Self {
Self {
timers: Mutex::new(HashMap::new()),
popups,
}
}
pub(crate) fn start(self: &Arc<Self>, id: u32, duration: Duration) {
let cancel = CancellationToken::new();
{
let Ok(mut timers) = self.timers.lock() else {
return;
};
if let Some(existing) = timers.get(&id) {
existing.cancel.cancel();
}
timers.insert(
id,
PopupTimer {
started_at: Instant::now(),
duration,
cancel: cancel.clone(),
},
);
}
let manager = Arc::clone(self);
tokio::spawn(async move {
tokio::select! {
() = tokio::time::sleep(duration) => {
manager.remove_popup(id);
}
() = cancel.cancelled() => {}
}
});
}
pub(crate) fn pause(&self, id: u32) {
let Ok(mut timers) = self.timers.lock() else {
return;
};
let Some(timer) = timers.get_mut(&id) else {
return;
};
let remaining = timer.remaining();
timer.cancel.cancel();
timer.duration = remaining;
}
pub(crate) fn resume(self: &Arc<Self>, id: u32) {
let remaining = {
let Ok(timers) = self.timers.lock() else {
return;
};
let Some(timer) = timers.get(&id) else {
return;
};
timer.duration
};
if remaining.is_zero() {
self.remove_popup(id);
return;
}
self.start(id, remaining);
}
pub(crate) fn cancel(&self, id: u32) {
if let Ok(mut timers) = self.timers.lock()
&& let Some(timer) = timers.remove(&id)
{
timer.cancel.cancel();
}
}
fn remove_popup(&self, id: u32) {
tracing::debug!(id = id, "popup timer expired");
{
let Ok(mut timers) = self.timers.lock() else {
return;
};
timers.remove(&id);
}
let mut list = self.popups.get();
list.retain(|popup| popup.id != id);
self.popups.set(list);
}
}