tauri_plugin_notifications/
desktop.rs

1use serde::de::DeserializeOwned;
2use tauri::{
3    plugin::{PermissionState, PluginApi},
4    AppHandle, Runtime,
5};
6
7use crate::NotificationsBuilder;
8
9pub fn init<R: Runtime, C: DeserializeOwned>(
10    app: &AppHandle<R>,
11    _api: PluginApi<R, C>,
12) -> crate::Result<Notifications<R>> {
13    Ok(Notifications(app.clone()))
14}
15
16/// Access to the notification APIs.
17///
18/// You can get an instance of this type via [`NotificationsExt`](crate::NotificationsExt)
19pub struct Notifications<R: Runtime>(AppHandle<R>);
20
21impl<R: Runtime> crate::NotificationsBuilder<R> {
22    pub async fn show(self) -> crate::Result<()> {
23        let mut notification = imp::Notification::new(self.app.config().identifier.clone());
24
25        if let Some(title) = self
26            .data
27            .title
28            .or_else(|| self.app.config().product_name.clone())
29        {
30            notification = notification.title(title);
31        }
32        if let Some(body) = self.data.body {
33            notification = notification.body(body);
34        }
35        if let Some(icon) = self.data.icon {
36            notification = notification.icon(icon);
37        }
38
39        notification.show()?;
40
41        Ok(())
42    }
43}
44
45impl<R: Runtime> Notifications<R> {
46    pub fn builder(&self) -> NotificationsBuilder<R> {
47        NotificationsBuilder::new(self.0.clone())
48    }
49
50    pub async fn request_permission(&self) -> crate::Result<PermissionState> {
51        Ok(PermissionState::Granted)
52    }
53
54    pub async fn register_for_push_notifications(&self) -> crate::Result<String> {
55        Err(crate::Error::Io(std::io::Error::other(
56            "Push notifications are not supported on desktop platforms",
57        )))
58    }
59
60    pub fn unregister_for_push_notifications(&self) -> crate::Result<()> {
61        Err(crate::Error::Io(std::io::Error::other(
62            "Push notifications are not supported on desktop platforms",
63        )))
64    }
65
66    pub async fn permission_state(&self) -> crate::Result<PermissionState> {
67        Ok(PermissionState::Granted)
68    }
69
70    pub async fn pending(&self) -> crate::Result<Vec<crate::PendingNotification>> {
71        Err(crate::Error::Io(std::io::Error::other(
72            "Pending notifications are not supported with notify-rust",
73        )))
74    }
75
76    pub async fn active(&self) -> crate::Result<Vec<crate::ActiveNotification>> {
77        Err(crate::Error::Io(std::io::Error::other(
78            "Active notifications are not supported with notify-rust",
79        )))
80    }
81
82    pub fn set_click_listener_active(&self, _active: bool) -> crate::Result<()> {
83        Err(crate::Error::Io(std::io::Error::other(
84            "Click listeners are not supported with notify-rust",
85        )))
86    }
87
88    pub fn remove_active(&self, _ids: Vec<i32>) -> crate::Result<()> {
89        Err(crate::Error::Io(std::io::Error::other(
90            "Removing active notifications is not supported with notify-rust",
91        )))
92    }
93
94    pub fn cancel(&self, _notifications: Vec<i32>) -> crate::Result<()> {
95        Err(crate::Error::Io(std::io::Error::other(
96            "Canceling notifications is not supported with notify-rust",
97        )))
98    }
99
100    pub fn cancel_all(&self) -> crate::Result<()> {
101        Err(crate::Error::Io(std::io::Error::other(
102            "Canceling notifications is not supported with notify-rust",
103        )))
104    }
105
106    pub fn register_action_types(&self, _types: Vec<crate::ActionType>) -> crate::Result<()> {
107        Err(crate::Error::Io(std::io::Error::other(
108            "Action types are not supported with notify-rust",
109        )))
110    }
111
112    pub fn create_channel(&self, _channel: crate::Channel) -> crate::Result<()> {
113        Err(crate::Error::Io(std::io::Error::other(
114            "Notification channels are not supported with notify-rust",
115        )))
116    }
117
118    pub fn delete_channel(&self, _id: impl Into<String>) -> crate::Result<()> {
119        Err(crate::Error::Io(std::io::Error::other(
120            "Notification channels are not supported with notify-rust",
121        )))
122    }
123
124    pub fn list_channels(&self) -> crate::Result<Vec<crate::Channel>> {
125        Err(crate::Error::Io(std::io::Error::other(
126            "Notification channels are not supported with notify-rust",
127        )))
128    }
129}
130
131mod imp {
132    //! Types and functions related to desktop notifications.
133
134    #[cfg(windows)]
135    use std::path::MAIN_SEPARATOR as SEP;
136
137    /// The desktop notification definition.
138    ///
139    /// Allows you to construct a Notification data and send it.
140    #[allow(dead_code)]
141    #[derive(Debug, Default)]
142    pub struct Notification {
143        /// The notification body.
144        body: Option<String>,
145        /// The notification title.
146        title: Option<String>,
147        /// The notification icon.
148        icon: Option<String>,
149        /// The notification identifier
150        identifier: String,
151    }
152
153    impl Notification {
154        /// Initializes a instance of a Notification.
155        pub fn new(identifier: impl Into<String>) -> Self {
156            Self {
157                identifier: identifier.into(),
158                ..Default::default()
159            }
160        }
161
162        /// Sets the notification body.
163        #[must_use]
164        pub fn body(mut self, body: impl Into<String>) -> Self {
165            self.body = Some(body.into());
166            self
167        }
168
169        /// Sets the notification title.
170        #[must_use]
171        pub fn title(mut self, title: impl Into<String>) -> Self {
172            self.title = Some(title.into());
173            self
174        }
175
176        /// Sets the notification icon.
177        #[must_use]
178        pub fn icon(mut self, icon: impl Into<String>) -> Self {
179            self.icon = Some(icon.into());
180            self
181        }
182
183        /// Shows the notification.
184        pub fn show(self) -> crate::Result<()> {
185            let mut notification = notify_rust::Notification::new();
186            if let Some(body) = self.body {
187                notification.body(&body);
188            }
189            if let Some(title) = self.title {
190                notification.summary(&title);
191            }
192            if let Some(icon) = self.icon {
193                notification.icon(&icon);
194            } else {
195                notification.auto_icon();
196            }
197            #[cfg(windows)]
198            {
199                let exe = tauri::utils::platform::current_exe()?;
200                let exe_dir = exe.parent().expect("failed to get exe directory");
201                let curr_dir = exe_dir.display().to_string();
202                // set the notification's System.AppUserModel.ID only when running the installed app
203                if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
204                    || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
205                {
206                    notification.app_id(&self.identifier);
207                }
208            }
209            #[cfg(target_os = "macos")]
210            {
211                let _ = notify_rust::set_application(if tauri::is_dev() {
212                    "com.apple.Terminal"
213                } else {
214                    &self.identifier
215                });
216            }
217
218            tauri::async_runtime::spawn(async move {
219                let _ = notification.show();
220            });
221
222            Ok(())
223        }
224    }
225}