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 [`NotificationExt`](crate::NotificationExt)
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 permission_state(&self) -> crate::Result<PermissionState> {
65        Ok(PermissionState::Granted)
66    }
67}
68
69mod imp {
70    //! Types and functions related to desktop notifications.
71
72    #[cfg(windows)]
73    use std::path::MAIN_SEPARATOR as SEP;
74
75    /// The desktop notification definition.
76    ///
77    /// Allows you to construct a Notification data and send it.
78    ///
79    /// # Examples
80    /// ```rust,no_run
81    /// use tauri_plugin_notification::NotificationExt;
82    /// // first we build the application to access the Tauri configuration
83    /// let app = tauri::Builder::default()
84    ///   // on an actual app, remove the string argument
85    ///   .build(tauri::generate_context!("test/tauri.conf.json"))
86    ///   .expect("error while building tauri application");
87    ///
88    /// // shows a notification with the given title and body
89    /// app.notification()
90    ///   .builder()
91    ///   .title("New message")
92    ///   .body("You've got a new message.")
93    ///   .show();
94    ///
95    /// // run the app
96    /// app.run(|_app_handle, _event| {});
97    /// ```
98    #[allow(dead_code)]
99    #[derive(Debug, Default)]
100    pub struct Notification {
101        /// The notification body.
102        body: Option<String>,
103        /// The notification title.
104        title: Option<String>,
105        /// The notification icon.
106        icon: Option<String>,
107        /// The notification identifier
108        identifier: String,
109    }
110
111    impl Notification {
112        /// Initializes a instance of a Notification.
113        pub fn new(identifier: impl Into<String>) -> Self {
114            Self {
115                identifier: identifier.into(),
116                ..Default::default()
117            }
118        }
119
120        /// Sets the notification body.
121        #[must_use]
122        pub fn body(mut self, body: impl Into<String>) -> Self {
123            self.body = Some(body.into());
124            self
125        }
126
127        /// Sets the notification title.
128        #[must_use]
129        pub fn title(mut self, title: impl Into<String>) -> Self {
130            self.title = Some(title.into());
131            self
132        }
133
134        /// Sets the notification icon.
135        #[must_use]
136        pub fn icon(mut self, icon: impl Into<String>) -> Self {
137            self.icon = Some(icon.into());
138            self
139        }
140
141        /// Shows the notification.
142        ///
143        /// # Examples
144        ///
145        /// ```no_run
146        /// use tauri_plugin_notification::NotificationExt;
147        ///
148        /// tauri::Builder::default()
149        ///   .setup(|app| {
150        ///     app.notification()
151        ///       .builder()
152        ///       .title("Tauri")
153        ///       .body("Tauri is awesome!")
154        ///       .show()
155        ///       .unwrap();
156        ///     Ok(())
157        ///   })
158        ///   .run(tauri::generate_context!("test/tauri.conf.json"))
159        ///   .expect("error while running tauri application");
160        /// ```
161        ///
162        pub fn show(self) -> crate::Result<()> {
163            let mut notification = notify_rust::Notification::new();
164            if let Some(body) = self.body {
165                notification.body(&body);
166            }
167            if let Some(title) = self.title {
168                notification.summary(&title);
169            }
170            if let Some(icon) = self.icon {
171                notification.icon(&icon);
172            } else {
173                notification.auto_icon();
174            }
175            #[cfg(windows)]
176            {
177                let exe = tauri::utils::platform::current_exe()?;
178                let exe_dir = exe.parent().expect("failed to get exe directory");
179                let curr_dir = exe_dir.display().to_string();
180                // set the notification's System.AppUserModel.ID only when running the installed app
181                if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
182                    || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
183                {
184                    notification.app_id(&self.identifier);
185                }
186            }
187            #[cfg(target_os = "macos")]
188            {
189                let _ = notify_rust::set_application(if tauri::is_dev() {
190                    "com.apple.Terminal"
191                } else {
192                    &self.identifier
193                });
194            }
195
196            tauri::async_runtime::spawn(async move {
197                let _ = notification.show();
198            });
199
200            Ok(())
201        }
202    }
203}