Skip to main content

egui_charts/ui_kit/notifications/
toasts.rs

1//! Toasts container
2//!
3//! Keep a [`Toasts`] queue in the state of the host component that displays it.
4//! Separate queues keep unrelated notification panels independent.
5
6use super::toast::Toast;
7
8/// Current wall-clock time in seconds.
9///
10/// Uses [`web_time`] so the same call resolves on native and `wasm32` targets;
11/// the standard-library clock panics in the browser, which would crash any
12/// host embedding this crate in WebAssembly.
13pub(crate) fn current_time_seconds() -> f64 {
14    web_time::SystemTime::now()
15        .duration_since(web_time::UNIX_EPOCH)
16        .map(|d| d.as_secs_f64())
17        .unwrap_or(0.0)
18}
19
20/// Toasts container for per-instance usage.
21#[derive(Default)]
22pub struct Toasts {
23    toasts: Vec<Toast>,
24    /// Whether the user expanded the stack; a new burst starts collapsed.
25    pub(super) expanded: bool,
26}
27
28impl Toasts {
29    /// Create a new toasts container
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Add a toast
35    pub fn add(&mut self, mut toast: Toast) {
36        toast.created_at = current_time_seconds();
37        self.toasts.push(toast);
38    }
39
40    /// Add an info toast
41    pub fn info(&mut self, message: impl Into<String>) {
42        self.add(Toast::info(message));
43    }
44
45    /// Add a success toast
46    pub fn success(&mut self, message: impl Into<String>) {
47        self.add(Toast::success(message));
48    }
49
50    /// Add a warning toast
51    pub fn warning(&mut self, message: impl Into<String>) {
52        self.add(Toast::warning(message));
53    }
54
55    /// Add an error toast
56    pub fn error(&mut self, message: impl Into<String>) {
57        self.add(Toast::error(message));
58    }
59
60    /// Remove a toast by ID
61    pub fn remove(&mut self, id: u64) {
62        self.toasts.retain(|t| t.id != id);
63    }
64
65    /// Clear all toasts
66    pub fn clear(&mut self) {
67        self.toasts.clear();
68        self.expanded = false;
69    }
70
71    /// Get the toasts
72    pub fn toasts(&self) -> &[Toast] {
73        &self.toasts
74    }
75
76    /// Get the toasts mutably
77    pub fn toasts_mut(&mut self) -> &mut Vec<Toast> {
78        &mut self.toasts
79    }
80
81    /// Remove expired toasts
82    pub fn cleanup_expired(&mut self) {
83        let current_time = current_time_seconds();
84        self.toasts.retain(|t| !t.is_expired(current_time));
85    }
86
87    /// Get the number of toasts
88    pub fn len(&self) -> usize {
89        self.toasts.len()
90    }
91
92    /// Check if there are no toasts
93    pub fn is_empty(&self) -> bool {
94        self.toasts.is_empty()
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn cleanup_keeps_permanent_and_fresh_toasts() {
104        let mut toasts = Toasts::new();
105        toasts.add(Toast::info("permanent").permanent());
106        toasts.add(Toast::info("fresh").duration(60.0));
107        toasts.cleanup_expired();
108        assert_eq!(toasts.len(), 2);
109    }
110}