tauri_plugin_notifications/
lib.rs1use serde::Serialize;
8#[cfg(mobile)]
9use tauri::plugin::PluginHandle;
10#[cfg(desktop)]
11use tauri::AppHandle;
12use tauri::{
13 plugin::{Builder, TauriPlugin},
14 Manager, Runtime,
15};
16
17pub use models::*;
18pub use tauri::plugin::PermissionState;
19
20#[cfg(desktop)]
21mod desktop;
22#[cfg(mobile)]
23mod mobile;
24
25mod commands;
26mod error;
27mod models;
28
29pub use error::{Error, Result};
30
31#[cfg(desktop)]
32pub use desktop::Notifications;
33#[cfg(mobile)]
34pub use mobile::Notifications;
35
36#[derive(Debug)]
38pub struct NotificationsBuilder<R: Runtime> {
39 #[cfg(desktop)]
40 app: AppHandle<R>,
41 #[cfg(mobile)]
42 handle: PluginHandle<R>,
43 pub(crate) data: NotificationData,
44}
45
46impl<R: Runtime> NotificationsBuilder<R> {
47 #[cfg(desktop)]
48 fn new(app: AppHandle<R>) -> Self {
49 Self {
50 app,
51 data: Default::default(),
52 }
53 }
54
55 #[cfg(mobile)]
56 fn new(handle: PluginHandle<R>) -> Self {
57 Self {
58 handle,
59 data: Default::default(),
60 }
61 }
62
63 pub fn id(mut self, id: i32) -> Self {
65 self.data.id = id;
66 self
67 }
68
69 pub fn channel_id(mut self, id: impl Into<String>) -> Self {
74 self.data.channel_id.replace(id.into());
75 self
76 }
77
78 pub fn title(mut self, title: impl Into<String>) -> Self {
80 self.data.title.replace(title.into());
81 self
82 }
83
84 pub fn body(mut self, body: impl Into<String>) -> Self {
86 self.data.body.replace(body.into());
87 self
88 }
89
90 pub fn schedule(mut self, schedule: Schedule) -> Self {
92 self.data.schedule.replace(schedule);
93 self
94 }
95
96 pub fn large_body(mut self, large_body: impl Into<String>) -> Self {
100 self.data.large_body.replace(large_body.into());
101 self
102 }
103
104 pub fn summary(mut self, summary: impl Into<String>) -> Self {
106 self.data.summary.replace(summary.into());
107 self
108 }
109
110 pub fn action_type_id(mut self, action_type_id: impl Into<String>) -> Self {
112 self.data.action_type_id.replace(action_type_id.into());
113 self
114 }
115
116 pub fn group(mut self, group: impl Into<String>) -> Self {
120 self.data.group.replace(group.into());
121 self
122 }
123
124 pub fn group_summary(mut self) -> Self {
126 self.data.group_summary = true;
127 self
128 }
129
130 pub fn sound(mut self, sound: impl Into<String>) -> Self {
132 self.data.sound.replace(sound.into());
133 self
134 }
135
136 pub fn inbox_line(mut self, line: impl Into<String>) -> Self {
142 self.data.inbox_lines.push(line.into());
143 self
144 }
145
146 pub fn icon(mut self, icon: impl Into<String>) -> Self {
150 self.data.icon.replace(icon.into());
151 self
152 }
153
154 pub fn large_icon(mut self, large_icon: impl Into<String>) -> Self {
158 self.data.large_icon.replace(large_icon.into());
159 self
160 }
161
162 pub fn icon_color(mut self, icon_color: impl Into<String>) -> Self {
164 self.data.icon_color.replace(icon_color.into());
165 self
166 }
167
168 pub fn attachment(mut self, attachment: Attachment) -> Self {
170 self.data.attachments.push(attachment);
171 self
172 }
173
174 pub fn extra(mut self, key: impl Into<String>, value: impl Serialize) -> Self {
176 if let Ok(value) = serde_json::to_value(value) {
177 self.data.extra.insert(key.into(), value);
178 }
179 self
180 }
181
182 pub fn ongoing(mut self) -> Self {
188 self.data.ongoing = true;
189 self
190 }
191
192 pub fn auto_cancel(mut self) -> Self {
194 self.data.auto_cancel = true;
195 self
196 }
197
198 pub fn silent(mut self) -> Self {
200 self.data.silent = true;
201 self
202 }
203}
204
205pub trait NotificationsExt<R: Runtime> {
207 fn notifications(&self) -> &Notifications<R>;
208}
209
210impl<R: Runtime, T: Manager<R>> crate::NotificationsExt<R> for T {
211 fn notifications(&self) -> &Notifications<R> {
212 self.state::<Notifications<R>>().inner()
213 }
214}
215
216pub fn init<R: Runtime>() -> TauriPlugin<R> {
218 Builder::new("notifications")
219 .invoke_handler(tauri::generate_handler![
220 commands::notify,
221 commands::request_permission,
222 commands::register_for_push_notifications,
223 commands::unregister_for_push_notifications,
224 commands::is_permission_granted,
225 ])
226 .setup(|app, api| {
227 #[cfg(mobile)]
228 let notification = mobile::init(app, api)?;
229 #[cfg(desktop)]
230 let notification = desktop::init(app, api)?;
231 app.manage(notification);
232 Ok(())
233 })
234 .build()
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[cfg(desktop)]
243 fn create_test_data() -> NotificationData {
244 NotificationData::default()
245 }
246
247 #[cfg(mobile)]
248 fn create_test_data() -> NotificationData {
249 NotificationData::default()
250 }
251
252 #[test]
253 fn test_notification_data_id() {
254 let mut data = create_test_data();
255 data.id = 42;
256 assert_eq!(data.id, 42);
257 }
258
259 #[test]
260 fn test_notification_data_channel_id() {
261 let mut data = create_test_data();
262 data.channel_id = Some("test_channel".to_string());
263 assert_eq!(data.channel_id, Some("test_channel".to_string()));
264 }
265
266 #[test]
267 fn test_notification_data_title() {
268 let mut data = create_test_data();
269 data.title = Some("Test Title".to_string());
270 assert_eq!(data.title, Some("Test Title".to_string()));
271 }
272
273 #[test]
274 fn test_notification_data_body() {
275 let mut data = create_test_data();
276 data.body = Some("Test Body".to_string());
277 assert_eq!(data.body, Some("Test Body".to_string()));
278 }
279
280 #[test]
281 fn test_notification_data_large_body() {
282 let mut data = create_test_data();
283 data.large_body = Some("Large Body Text".to_string());
284 assert_eq!(data.large_body, Some("Large Body Text".to_string()));
285 }
286
287 #[test]
288 fn test_notification_data_summary() {
289 let mut data = create_test_data();
290 data.summary = Some("Summary Text".to_string());
291 assert_eq!(data.summary, Some("Summary Text".to_string()));
292 }
293
294 #[test]
295 fn test_notification_data_action_type_id() {
296 let mut data = create_test_data();
297 data.action_type_id = Some("action_type".to_string());
298 assert_eq!(data.action_type_id, Some("action_type".to_string()));
299 }
300
301 #[test]
302 fn test_notification_data_group() {
303 let mut data = create_test_data();
304 data.group = Some("test_group".to_string());
305 assert_eq!(data.group, Some("test_group".to_string()));
306 }
307
308 #[test]
309 fn test_notification_data_group_summary() {
310 let mut data = create_test_data();
311 data.group_summary = true;
312 assert!(data.group_summary);
313 }
314
315 #[test]
316 fn test_notification_data_sound() {
317 let mut data = create_test_data();
318 data.sound = Some("notification_sound".to_string());
319 assert_eq!(data.sound, Some("notification_sound".to_string()));
320 }
321
322 #[test]
323 fn test_notification_data_inbox_lines() {
324 let mut data = create_test_data();
325 data.inbox_lines.push("Line 1".to_string());
326 data.inbox_lines.push("Line 2".to_string());
327 assert_eq!(data.inbox_lines.len(), 2);
328 assert_eq!(data.inbox_lines[0], "Line 1");
329 assert_eq!(data.inbox_lines[1], "Line 2");
330 }
331
332 #[test]
333 fn test_notification_data_icon() {
334 let mut data = create_test_data();
335 data.icon = Some("icon_name".to_string());
336 assert_eq!(data.icon, Some("icon_name".to_string()));
337 }
338
339 #[test]
340 fn test_notification_data_large_icon() {
341 let mut data = create_test_data();
342 data.large_icon = Some("large_icon_name".to_string());
343 assert_eq!(data.large_icon, Some("large_icon_name".to_string()));
344 }
345
346 #[test]
347 fn test_notification_data_icon_color() {
348 let mut data = create_test_data();
349 data.icon_color = Some("#FF0000".to_string());
350 assert_eq!(data.icon_color, Some("#FF0000".to_string()));
351 }
352
353 #[test]
354 fn test_notification_data_attachments() {
355 let mut data = create_test_data();
356 let url = url::Url::parse("https://example.com/image.png").expect("Failed to parse URL");
357 let attachment = Attachment::new("attachment1", url);
358 data.attachments.push(attachment);
359 assert_eq!(data.attachments.len(), 1);
360 }
361
362 #[test]
363 fn test_notification_data_extra() {
364 let mut data = create_test_data();
365 data.extra
366 .insert("key1".to_string(), serde_json::json!("value1"));
367 data.extra.insert("key2".to_string(), serde_json::json!(42));
368 assert_eq!(data.extra.len(), 2);
369 assert_eq!(data.extra.get("key1"), Some(&serde_json::json!("value1")));
370 assert_eq!(data.extra.get("key2"), Some(&serde_json::json!(42)));
371 }
372
373 #[test]
374 fn test_notification_data_ongoing() {
375 let mut data = create_test_data();
376 data.ongoing = true;
377 assert!(data.ongoing);
378 }
379
380 #[test]
381 fn test_notification_data_auto_cancel() {
382 let mut data = create_test_data();
383 data.auto_cancel = true;
384 assert!(data.auto_cancel);
385 }
386
387 #[test]
388 fn test_notification_data_silent() {
389 let mut data = create_test_data();
390 data.silent = true;
391 assert!(data.silent);
392 }
393
394 #[test]
395 fn test_notification_data_schedule() {
396 let mut data = create_test_data();
397 let schedule = Schedule::Every {
398 interval: ScheduleEvery::Day,
399 count: 1,
400 allow_while_idle: false,
401 };
402 data.schedule = Some(schedule);
403 assert!(data.schedule.is_some());
404 assert!(matches!(data.schedule, Some(Schedule::Every { .. })));
405 }
406}