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