aster/notifications/
manager.rs1use super::desktop::{play_sound, send_desktop_notification};
6use super::types::*;
7use std::sync::{Arc, RwLock};
8use std::time::{SystemTime, UNIX_EPOCH};
9
10pub struct NotificationManager {
12 config: NotificationConfig,
14 notifications: Arc<RwLock<Vec<Notification>>>,
16 max_notifications: usize,
18}
19
20impl NotificationManager {
21 pub fn new(config: NotificationConfig) -> Self {
23 Self {
24 config,
25 notifications: Arc::new(RwLock::new(Vec::new())),
26 max_notifications: 100,
27 }
28 }
29
30 pub fn is_enabled(&self) -> bool {
32 if !self.config.enabled {
33 return false;
34 }
35
36 if let (Some(start), Some(end)) =
38 (self.config.quiet_hours_start, self.config.quiet_hours_end)
39 {
40 use chrono::Timelike;
41 let now = chrono::Local::now().hour() as u8;
42 if start <= end {
43 if now >= start && now < end {
44 return false;
45 }
46 } else {
47 if now >= start || now < end {
49 return false;
50 }
51 }
52 }
53
54 true
55 }
56
57 fn meets_priority(&self, notification_type: NotificationType) -> bool {
59 let Some(min_priority) = self.config.min_priority else {
60 return true;
61 };
62
63 let priority_order = [
64 NotificationType::Info,
65 NotificationType::Success,
66 NotificationType::Warning,
67 NotificationType::Error,
68 ];
69
70 let type_index = priority_order
71 .iter()
72 .position(|&t| t == notification_type)
73 .unwrap_or(0);
74 let min_index = priority_order
75 .iter()
76 .position(|&t| t == min_priority)
77 .unwrap_or(0);
78
79 type_index >= min_index
80 }
81
82 pub fn notify(
84 &self,
85 title: &str,
86 message: &str,
87 notification_type: NotificationType,
88 kind: NotificationKind,
89 ) -> Option<Notification> {
90 if !self.is_enabled() || !self.meets_priority(notification_type) {
91 return None;
92 }
93
94 let timestamp = SystemTime::now()
95 .duration_since(UNIX_EPOCH)
96 .map(|d| d.as_millis() as u64)
97 .unwrap_or(0);
98
99 let notification = Notification {
100 id: format!("notif_{}_{}", timestamp, rand::random::<u32>()),
101 notification_type,
102 kind,
103 title: title.to_string(),
104 message: message.to_string(),
105 timestamp,
106 read: false,
107 actions: Vec::new(),
108 };
109
110 if let Ok(mut notifications) = self.notifications.write() {
112 notifications.insert(0, notification.clone());
113 if notifications.len() > self.max_notifications {
114 notifications.truncate(self.max_notifications);
115 }
116 }
117
118 if self.config.desktop_notifications {
120 let _ = send_desktop_notification(¬ification);
121 }
122
123 if self.config.sound_enabled {
125 let _ = play_sound(notification_type);
126 }
127
128 Some(notification)
129 }
130
131 pub fn get_all(&self) -> Vec<Notification> {
133 self.notifications
134 .read()
135 .map(|n| n.clone())
136 .unwrap_or_default()
137 }
138
139 pub fn get_unread(&self) -> Vec<Notification> {
141 self.notifications
142 .read()
143 .map(|n| n.iter().filter(|n| !n.read).cloned().collect())
144 .unwrap_or_default()
145 }
146
147 pub fn get_unread_count(&self) -> usize {
149 self.notifications
150 .read()
151 .map(|n| n.iter().filter(|n| !n.read).count())
152 .unwrap_or(0)
153 }
154
155 pub fn mark_as_read(&self, id: &str) -> bool {
157 if let Ok(mut notifications) = self.notifications.write() {
158 if let Some(n) = notifications.iter_mut().find(|n| n.id == id) {
159 n.read = true;
160 return true;
161 }
162 }
163 false
164 }
165
166 pub fn mark_all_as_read(&self) {
168 if let Ok(mut notifications) = self.notifications.write() {
169 for n in notifications.iter_mut() {
170 n.read = true;
171 }
172 }
173 }
174
175 pub fn clear(&self) {
177 if let Ok(mut notifications) = self.notifications.write() {
178 notifications.clear();
179 }
180 }
181
182 pub fn info(&self, title: &str, message: &str) -> Option<Notification> {
184 self.notify(
185 title,
186 message,
187 NotificationType::Info,
188 NotificationKind::Custom,
189 )
190 }
191
192 pub fn success(&self, title: &str, message: &str) -> Option<Notification> {
194 self.notify(
195 title,
196 message,
197 NotificationType::Success,
198 NotificationKind::TaskComplete,
199 )
200 }
201
202 pub fn warn(&self, title: &str, message: &str) -> Option<Notification> {
204 self.notify(
205 title,
206 message,
207 NotificationType::Warning,
208 NotificationKind::Custom,
209 )
210 }
211
212 pub fn error(&self, title: &str, message: &str) -> Option<Notification> {
214 self.notify(
215 title,
216 message,
217 NotificationType::Error,
218 NotificationKind::Error,
219 )
220 }
221}
222
223impl Default for NotificationManager {
224 fn default() -> Self {
225 Self::new(NotificationConfig::default())
226 }
227}