tauri_plugin_notifications/
desktop.rs1use 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
20pub 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 #[cfg(windows)]
139 use std::path::MAIN_SEPARATOR as SEP;
140
141 #[allow(dead_code)]
145 #[derive(Debug, Default)]
146 pub struct Notification {
147 body: Option<String>,
149 title: Option<String>,
151 icon: Option<String>,
153 identifier: String,
155 }
156
157 impl Notification {
158 pub fn new(identifier: impl Into<String>) -> Self {
160 Self {
161 identifier: identifier.into(),
162 ..Default::default()
163 }
164 }
165
166 #[must_use]
168 pub fn body(mut self, body: impl Into<String>) -> Self {
169 self.body = Some(body.into());
170 self
171 }
172
173 #[must_use]
175 pub fn title(mut self, title: impl Into<String>) -> Self {
176 self.title = Some(title.into());
177 self
178 }
179
180 #[must_use]
182 pub fn icon(mut self, icon: impl Into<String>) -> Self {
183 self.icon = Some(icon.into());
184 self
185 }
186
187 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 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}