reovim_plugin_notification/
state.rs1use std::{
6 collections::{HashMap, VecDeque},
7 sync::{Arc, RwLock},
8};
9
10use crate::{
11 notification::{Notification, NotificationLevel},
12 progress::{ProgressBarConfig, ProgressNotification},
13 style::NotificationStyles,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18pub enum NotificationPosition {
19 TopRight,
21 TopLeft,
23 #[default]
25 BottomRight,
26 BottomLeft,
28 TopCenter,
30 BottomCenter,
32}
33
34#[derive(Debug, Clone)]
36pub struct NotificationConfig {
37 pub default_duration_ms: u64,
39 pub max_notifications: usize,
41 pub position: NotificationPosition,
43 pub progress_bar: ProgressBarConfig,
45}
46
47impl Default for NotificationConfig {
48 fn default() -> Self {
49 Self {
50 default_duration_ms: 3000,
51 max_notifications: 5,
52 position: NotificationPosition::BottomRight,
53 progress_bar: ProgressBarConfig::default(),
54 }
55 }
56}
57
58#[derive(Default)]
60struct NotificationManagerInner {
61 notifications: VecDeque<Notification>,
62 progress: HashMap<String, ProgressNotification>,
63 config: NotificationConfig,
64 styles: NotificationStyles,
65}
66
67pub struct SharedNotificationManager {
69 inner: RwLock<NotificationManagerInner>,
70}
71
72impl Default for SharedNotificationManager {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl SharedNotificationManager {
79 #[must_use]
81 pub fn new() -> Self {
82 Self {
83 inner: RwLock::new(NotificationManagerInner::default()),
84 }
85 }
86
87 #[allow(clippy::significant_drop_tightening)]
93 pub fn show(
94 &self,
95 level: NotificationLevel,
96 message: String,
97 duration_ms: Option<u64>,
98 source: Option<String>,
99 ) {
100 let mut inner = self.inner.write().unwrap();
101 let duration = duration_ms.unwrap_or(inner.config.default_duration_ms);
102 let max = inner.config.max_notifications;
103
104 let mut notification = Notification::new(level, message).with_duration(duration);
105 if let Some(src) = source {
106 notification = notification.with_source(src);
107 }
108
109 inner.notifications.push_front(notification);
110
111 while inner.notifications.len() > max {
113 inner.notifications.pop_back();
114 }
115 }
116
117 pub fn dismiss(&self, id: &str) {
123 let mut inner = self.inner.write().unwrap();
124 inner.notifications.retain(|n| n.id != id);
125 }
126
127 pub fn dismiss_all(&self) {
133 let mut inner = self.inner.write().unwrap();
134 inner.notifications.clear();
135 }
136
137 pub fn update_progress(
143 &self,
144 id: String,
145 title: String,
146 source: String,
147 progress: Option<u8>,
148 detail: Option<String>,
149 ) {
150 let mut inner = self.inner.write().unwrap();
151
152 let mut notification = ProgressNotification::new(&id, title, source);
153 if let Some(pct) = progress {
154 notification = notification.with_progress(pct);
155 }
156 if let Some(det) = detail {
157 notification = notification.with_detail(det);
158 }
159
160 inner.progress.insert(id, notification);
161 }
162
163 #[allow(clippy::significant_drop_tightening)]
169 pub fn complete_progress(&self, id: &str, message: Option<String>) {
170 let mut inner = self.inner.write().unwrap();
171 let max = inner.config.max_notifications;
172
173 inner.progress.remove(id);
174
175 if let Some(msg) = message {
177 let notification =
178 Notification::new(NotificationLevel::Success, msg).with_duration(2000);
179 inner.notifications.push_front(notification);
180
181 while inner.notifications.len() > max {
183 inner.notifications.pop_back();
184 }
185 }
186 }
187
188 pub fn cleanup_expired(&self) {
194 let mut inner = self.inner.write().unwrap();
195 inner.notifications.retain(|n| !n.is_expired());
196 }
197
198 #[must_use]
204 pub fn config(&self) -> NotificationConfig {
205 self.inner.read().unwrap().config.clone()
206 }
207
208 #[must_use]
214 pub fn styles(&self) -> NotificationStyles {
215 self.inner.read().unwrap().styles.clone()
216 }
217
218 #[must_use]
224 pub fn notifications(&self) -> Vec<Notification> {
225 self.inner
226 .read()
227 .unwrap()
228 .notifications
229 .iter()
230 .cloned()
231 .collect()
232 }
233
234 #[must_use]
240 pub fn progress_items(&self) -> Vec<ProgressNotification> {
241 self.inner
242 .read()
243 .unwrap()
244 .progress
245 .values()
246 .cloned()
247 .collect()
248 }
249
250 #[must_use]
256 pub fn has_visible(&self) -> bool {
257 let inner = self.inner.read().unwrap();
258 !inner.notifications.is_empty() || !inner.progress.is_empty()
259 }
260
261 pub fn set_position(&self, position: NotificationPosition) {
267 self.inner.write().unwrap().config.position = position;
268 }
269
270 pub fn set_default_duration(&self, duration_ms: u64) {
276 self.inner.write().unwrap().config.default_duration_ms = duration_ms;
277 }
278
279 #[allow(clippy::significant_drop_tightening)]
285 pub fn set_max_notifications(&self, max: usize) {
286 let mut inner = self.inner.write().unwrap();
287 inner.config.max_notifications = max;
288 while inner.notifications.len() > max {
290 inner.notifications.pop_back();
291 }
292 }
293}
294
295pub type NotificationManagerHandle = Arc<SharedNotificationManager>;