tauri_plugin_notifications/
desktop.rs1use serde::de::DeserializeOwned;
2use tauri::{
3 plugin::{PermissionState, PluginApi},
4 AppHandle, Runtime,
5};
6
7use crate::NotificationsBuilder;
8
9pub fn init<R: Runtime, C: DeserializeOwned>(
10 app: &AppHandle<R>,
11 _api: PluginApi<R, C>,
12) -> crate::Result<Notifications<R>> {
13 Ok(Notifications(app.clone()))
14}
15
16pub struct Notifications<R: Runtime>(AppHandle<R>);
20
21impl<R: Runtime> crate::NotificationsBuilder<R> {
22 pub async fn show(self) -> crate::Result<()> {
23 let mut notification = imp::Notification::new(self.app.config().identifier.clone());
24
25 if let Some(title) = self
26 .data
27 .title
28 .or_else(|| self.app.config().product_name.clone())
29 {
30 notification = notification.title(title);
31 }
32 if let Some(body) = self.data.body {
33 notification = notification.body(body);
34 }
35 if let Some(icon) = self.data.icon {
36 notification = notification.icon(icon);
37 }
38
39 notification.show()?;
40
41 Ok(())
42 }
43}
44
45impl<R: Runtime> Notifications<R> {
46 pub fn builder(&self) -> NotificationsBuilder<R> {
47 NotificationsBuilder::new(self.0.clone())
48 }
49
50 pub async fn request_permission(&self) -> crate::Result<PermissionState> {
51 Ok(PermissionState::Granted)
52 }
53
54 pub async fn register_for_push_notifications(&self) -> crate::Result<String> {
55 Err(crate::Error::Io(std::io::Error::other(
56 "Push notifications are not supported on desktop platforms",
57 )))
58 }
59
60 pub fn unregister_for_push_notifications(&self) -> crate::Result<()> {
61 Err(crate::Error::Io(std::io::Error::other(
62 "Push notifications are not supported on desktop platforms",
63 )))
64 }
65
66 pub async fn permission_state(&self) -> crate::Result<PermissionState> {
67 Ok(PermissionState::Granted)
68 }
69
70 pub async fn pending(&self) -> crate::Result<Vec<crate::PendingNotification>> {
71 Err(crate::Error::Io(std::io::Error::other(
72 "Pending notifications are not supported with notify-rust",
73 )))
74 }
75
76 pub async fn active(&self) -> crate::Result<Vec<crate::ActiveNotification>> {
77 Err(crate::Error::Io(std::io::Error::other(
78 "Active notifications are not supported with notify-rust",
79 )))
80 }
81
82 pub fn set_click_listener_active(&self, _active: bool) -> crate::Result<()> {
83 Err(crate::Error::Io(std::io::Error::other(
84 "Click listeners are not supported with notify-rust",
85 )))
86 }
87
88 pub fn remove_active(&self, _ids: Vec<i32>) -> crate::Result<()> {
89 Err(crate::Error::Io(std::io::Error::other(
90 "Removing active notifications is not supported with notify-rust",
91 )))
92 }
93
94 pub fn cancel(&self, _notifications: Vec<i32>) -> crate::Result<()> {
95 Err(crate::Error::Io(std::io::Error::other(
96 "Canceling notifications is not supported with notify-rust",
97 )))
98 }
99
100 pub fn cancel_all(&self) -> crate::Result<()> {
101 Err(crate::Error::Io(std::io::Error::other(
102 "Canceling notifications is not supported with notify-rust",
103 )))
104 }
105
106 pub fn register_action_types(&self, _types: Vec<crate::ActionType>) -> crate::Result<()> {
107 Err(crate::Error::Io(std::io::Error::other(
108 "Action types are not supported with notify-rust",
109 )))
110 }
111
112 pub fn create_channel(&self, _channel: crate::Channel) -> crate::Result<()> {
113 Err(crate::Error::Io(std::io::Error::other(
114 "Notification channels are not supported with notify-rust",
115 )))
116 }
117
118 pub fn delete_channel(&self, _id: impl Into<String>) -> crate::Result<()> {
119 Err(crate::Error::Io(std::io::Error::other(
120 "Notification channels are not supported with notify-rust",
121 )))
122 }
123
124 pub fn list_channels(&self) -> crate::Result<Vec<crate::Channel>> {
125 Err(crate::Error::Io(std::io::Error::other(
126 "Notification channels are not supported with notify-rust",
127 )))
128 }
129}
130
131mod imp {
132 #[cfg(windows)]
135 use std::path::MAIN_SEPARATOR as SEP;
136
137 #[allow(dead_code)]
141 #[derive(Debug, Default)]
142 pub struct Notification {
143 body: Option<String>,
145 title: Option<String>,
147 icon: Option<String>,
149 identifier: String,
151 }
152
153 impl Notification {
154 pub fn new(identifier: impl Into<String>) -> Self {
156 Self {
157 identifier: identifier.into(),
158 ..Default::default()
159 }
160 }
161
162 #[must_use]
164 pub fn body(mut self, body: impl Into<String>) -> Self {
165 self.body = Some(body.into());
166 self
167 }
168
169 #[must_use]
171 pub fn title(mut self, title: impl Into<String>) -> Self {
172 self.title = Some(title.into());
173 self
174 }
175
176 #[must_use]
178 pub fn icon(mut self, icon: impl Into<String>) -> Self {
179 self.icon = Some(icon.into());
180 self
181 }
182
183 pub fn show(self) -> crate::Result<()> {
185 let mut notification = notify_rust::Notification::new();
186 if let Some(body) = self.body {
187 notification.body(&body);
188 }
189 if let Some(title) = self.title {
190 notification.summary(&title);
191 }
192 if let Some(icon) = self.icon {
193 notification.icon(&icon);
194 } else {
195 notification.auto_icon();
196 }
197 #[cfg(windows)]
198 {
199 let exe = tauri::utils::platform::current_exe()?;
200 let exe_dir = exe.parent().expect("failed to get exe directory");
201 let curr_dir = exe_dir.display().to_string();
202 if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str())
204 || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str()))
205 {
206 notification.app_id(&self.identifier);
207 }
208 }
209 #[cfg(target_os = "macos")]
210 {
211 let _ = notify_rust::set_application(if tauri::is_dev() {
212 "com.apple.Terminal"
213 } else {
214 &self.identifier
215 });
216 }
217
218 tauri::async_runtime::spawn(async move {
219 let _ = notification.show();
220 });
221
222 Ok(())
223 }
224 }
225}