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}