tauri_plugin_notifications/
desktop.rs

1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use serde::de::DeserializeOwned;
6use tauri::{
7    plugin::{PermissionState, PluginApi},
8    AppHandle, Runtime,
9};
10
11use crate::NotificationsBuilder;
12
13pub fn init<R: Runtime, C: DeserializeOwned>(
14    app: &AppHandle<R>,
15    _api: PluginApi<R, C>,
16) -> crate::Result<Notifications<R>> {
17    Ok(Notifications(app.clone()))
18}
19
20/// Access to the notification APIs.
21///
22/// You can get an instance of this type via [`NotificationsExt`](crate::NotificationsExt)
23pub struct Notifications<R: Runtime>(AppHandle<R>);
24
25impl<R: Runtime> crate::NotificationsBuilder<R> {
26    pub fn show(self) -> crate::Result<()> {
27        let mut notification = imp::Notification::new(self.app.config().identifier.clone());
28
29        if let Some(title) = self
30            .data
31            .title
32            .or_else(|| self.app.config().product_name.clone())
33        {
34            notification = notification.title(title);
35        }
36        if let Some(body) = self.data.body {
37            notification = notification.body(body);
38        }
39        if let Some(icon) = self.data.icon {
40            notification = notification.icon(icon);
41        }
42
43        notification.show()?;
44
45        Ok(())
46    }
47}
48
49impl<R: Runtime> Notifications<R> {
50    pub fn builder(&self) -> NotificationsBuilder<R> {
51        NotificationsBuilder::new(self.0.clone())
52    }
53
54    pub fn request_permission(&self) -> crate::Result<PermissionState> {
55        Ok(PermissionState::Granted)
56    }
57
58    pub fn register_for_push_notifications(&self) -> crate::Result<String> {
59        Err(crate::Error::Io(std::io::Error::other(
60            "Push notifications are not supported on desktop platforms",
61        )))
62    }
63
64    pub fn unregister_for_push_notifications(&self) -> crate::Result<()> {
65        Err(crate::Error::Io(std::io::Error::other(
66            "Push notifications are not supported on desktop platforms",
67        )))
68    }
69
70    pub fn permission_state(&self) -> crate::Result<PermissionState> {
71        Ok(PermissionState::Granted)
72    }
73}
74
75mod imp {
76    //! Types and functions related to desktop notifications.
77
78    #[cfg(windows)]
79    use std::path::MAIN_SEPARATOR as SEP;
80
81    /// The desktop notification definition.
82    ///
83    /// Allows you to construct a Notification data and send it.
84    #[allow(dead_code)]
85    #[derive(Debug, Default)]
86    pub struct Notification {
87        /// The notification body.
88        body: Option<String>,
89        /// The notification title.
90        title: Option<String>,
91        /// The notification icon.
92        icon: Option<String>,
93        /// The notification identifier
94        identifier: String,
95    }
96
97    impl Notification {
98        /// Initializes a instance of a Notification.
99        pub fn new(identifier: impl Into<String>) -> Self {
100            Self {
101                identifier: identifier.into(),
102                ..Default::default()
103            }
104        }
105
106        /// Sets the notification body.
107        #[must_use]
108        pub fn body(mut self, body: impl Into<String>) -> Self {
109            self.body = Some(body.into());
110            self
111        }
112
113        /// Sets the notification title.
114        #[must_use]
115        pub fn title(mut self, title: impl Into<String>) -> Self {
116            self.title = Some(title.into());
117            self
118        }
119
120        /// Sets the notification icon.
121        #[must_use]
122        pub fn icon(mut self, icon: impl Into<String>) -> Self {
123            self.icon = Some(icon.into());
124            self
125        }
126
127        /// Shows the notification.
128        pub fn show(self) -> crate::Result<()> {
129            let mut notification = notify_rust::Notification::new();
130            if let Some(body) = self.body {
131                notification.body(&body);
132            }
133            if let Some(title) = self.title {
134                notification.summary(&title);
135            }
136            if let Some(icon) = self.icon {
137                notification.icon(&icon);
138            } else {
139                notification.auto_icon();
140            }
141            #[cfg(windows)]
142            {
143                let exe = tauri::utils::platform::current_exe()?;
144                let exe_dir = exe.parent().expect("failed to get exe directory");
145                let curr_dir = exe_dir.display().to_string();
146                // set the notification's System.AppUserModel.ID only when running the installed app
147                if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
148                    || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
149                {
150                    notification.app_id(&self.identifier);
151                }
152            }
153            #[cfg(target_os = "macos")]
154            {
155                let _ = notify_rust::set_application(if tauri::is_dev() {
156                    "com.apple.Terminal"
157                } else {
158                    &self.identifier
159                });
160            }
161
162            tauri::async_runtime::spawn(async move {
163                let _ = notification.show();
164            });
165
166            Ok(())
167        }
168    }
169}