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 async 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 async fn request_permission(&self) -> crate::Result<PermissionState> {
55        Ok(PermissionState::Granted)
56    }
57
58    pub async 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 async fn permission_state(&self) -> crate::Result<PermissionState> {
71        Ok(PermissionState::Granted)
72    }
73
74    pub async fn pending(&self) -> crate::Result<Vec<crate::PendingNotification>> {
75        Err(crate::Error::Io(std::io::Error::other(
76            "Pending notifications are not supported with notify-rust",
77        )))
78    }
79
80    pub async fn active(&self) -> crate::Result<Vec<crate::ActiveNotification>> {
81        Err(crate::Error::Io(std::io::Error::other(
82            "Active notifications are not supported with notify-rust",
83        )))
84    }
85
86    pub fn set_click_listener_active(&self, _active: bool) -> crate::Result<()> {
87        Err(crate::Error::Io(std::io::Error::other(
88            "Click listeners are not supported with notify-rust",
89        )))
90    }
91
92    pub fn remove_active(&self, _ids: Vec<i32>) -> crate::Result<()> {
93        Err(crate::Error::Io(std::io::Error::other(
94            "Removing active notifications is not supported with notify-rust",
95        )))
96    }
97
98    pub fn cancel(&self, _notifications: Vec<i32>) -> crate::Result<()> {
99        Err(crate::Error::Io(std::io::Error::other(
100            "Canceling notifications is not supported with notify-rust",
101        )))
102    }
103
104    pub fn cancel_all(&self) -> crate::Result<()> {
105        Err(crate::Error::Io(std::io::Error::other(
106            "Canceling notifications is not supported with notify-rust",
107        )))
108    }
109
110    pub fn register_action_types(&self, _types: Vec<crate::ActionType>) -> crate::Result<()> {
111        Err(crate::Error::Io(std::io::Error::other(
112            "Action types are not supported with notify-rust",
113        )))
114    }
115
116    pub fn create_channel(&self, _channel: crate::Channel) -> crate::Result<()> {
117        Err(crate::Error::Io(std::io::Error::other(
118            "Notification channels are not supported with notify-rust",
119        )))
120    }
121
122    pub fn delete_channel(&self, _id: impl Into<String>) -> crate::Result<()> {
123        Err(crate::Error::Io(std::io::Error::other(
124            "Notification channels are not supported with notify-rust",
125        )))
126    }
127
128    pub fn list_channels(&self) -> crate::Result<Vec<crate::Channel>> {
129        Err(crate::Error::Io(std::io::Error::other(
130            "Notification channels are not supported with notify-rust",
131        )))
132    }
133}
134
135mod imp {
136    //! Types and functions related to desktop notifications.
137
138    #[cfg(windows)]
139    use std::path::MAIN_SEPARATOR as SEP;
140
141    /// The desktop notification definition.
142    ///
143    /// Allows you to construct a Notification data and send it.
144    #[allow(dead_code)]
145    #[derive(Debug, Default)]
146    pub struct Notification {
147        /// The notification body.
148        body: Option<String>,
149        /// The notification title.
150        title: Option<String>,
151        /// The notification icon.
152        icon: Option<String>,
153        /// The notification identifier
154        identifier: String,
155    }
156
157    impl Notification {
158        /// Initializes a instance of a Notification.
159        pub fn new(identifier: impl Into<String>) -> Self {
160            Self {
161                identifier: identifier.into(),
162                ..Default::default()
163            }
164        }
165
166        /// Sets the notification body.
167        #[must_use]
168        pub fn body(mut self, body: impl Into<String>) -> Self {
169            self.body = Some(body.into());
170            self
171        }
172
173        /// Sets the notification title.
174        #[must_use]
175        pub fn title(mut self, title: impl Into<String>) -> Self {
176            self.title = Some(title.into());
177            self
178        }
179
180        /// Sets the notification icon.
181        #[must_use]
182        pub fn icon(mut self, icon: impl Into<String>) -> Self {
183            self.icon = Some(icon.into());
184            self
185        }
186
187        /// Shows the notification.
188        pub fn show(self) -> crate::Result<()> {
189            let mut notification = notify_rust::Notification::new();
190            if let Some(body) = self.body {
191                notification.body(&body);
192            }
193            if let Some(title) = self.title {
194                notification.summary(&title);
195            }
196            if let Some(icon) = self.icon {
197                notification.icon(&icon);
198            } else {
199                notification.auto_icon();
200            }
201            #[cfg(windows)]
202            {
203                let exe = tauri::utils::platform::current_exe()?;
204                let exe_dir = exe.parent().expect("failed to get exe directory");
205                let curr_dir = exe_dir.display().to_string();
206                // set the notification's System.AppUserModel.ID only when running the installed app
207                if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
208                    || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
209                {
210                    notification.app_id(&self.identifier);
211                }
212            }
213            #[cfg(target_os = "macos")]
214            {
215                let _ = notify_rust::set_application(if tauri::is_dev() {
216                    "com.apple.Terminal"
217                } else {
218                    &self.identifier
219                });
220            }
221
222            tauri::async_runtime::spawn(async move {
223                let _ = notification.show();
224            });
225
226            Ok(())
227        }
228    }
229}