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 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    #[allow(dead_code)]
79    #[derive(Debug, Default)]
80    pub struct Notification {
81        /// The notification body.
82        body: Option<String>,
83        /// The notification title.
84        title: Option<String>,
85        /// The notification icon.
86        icon: Option<String>,
87        /// The notification identifier
88        identifier: String,
89    }
90
91    impl Notification {
92        /// Initializes a instance of a Notification.
93        pub fn new(identifier: impl Into<String>) -> Self {
94            Self {
95                identifier: identifier.into(),
96                ..Default::default()
97            }
98        }
99
100        /// Sets the notification body.
101        #[must_use]
102        pub fn body(mut self, body: impl Into<String>) -> Self {
103            self.body = Some(body.into());
104            self
105        }
106
107        /// Sets the notification title.
108        #[must_use]
109        pub fn title(mut self, title: impl Into<String>) -> Self {
110            self.title = Some(title.into());
111            self
112        }
113
114        /// Sets the notification icon.
115        #[must_use]
116        pub fn icon(mut self, icon: impl Into<String>) -> Self {
117            self.icon = Some(icon.into());
118            self
119        }
120
121        /// Shows the notification.
122        pub fn show(self) -> crate::Result<()> {
123            let mut notification = notify_rust::Notification::new();
124            if let Some(body) = self.body {
125                notification.body(&body);
126            }
127            if let Some(title) = self.title {
128                notification.summary(&title);
129            }
130            if let Some(icon) = self.icon {
131                notification.icon(&icon);
132            } else {
133                notification.auto_icon();
134            }
135            #[cfg(windows)]
136            {
137                let exe = tauri::utils::platform::current_exe()?;
138                let exe_dir = exe.parent().expect("failed to get exe directory");
139                let curr_dir = exe_dir.display().to_string();
140                // set the notification's System.AppUserModel.ID only when running the installed app
141                if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
142                    || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
143                {
144                    notification.app_id(&self.identifier);
145                }
146            }
147            #[cfg(target_os = "macos")]
148            {
149                let _ = notify_rust::set_application(if tauri::is_dev() {
150                    "com.apple.Terminal"
151                } else {
152                    &self.identifier
153                });
154            }
155
156            tauri::async_runtime::spawn(async move {
157                let _ = notification.show();
158            });
159
160            Ok(())
161        }
162    }
163}