// Copyright 2019-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use serde::de::DeserializeOwned; use tauri::{ AppHandle, Runtime, plugin::{PermissionState, PluginApi}, }; use crate::NotificationBuilder; /// Initializes the desktop implementation of the notification APIs. pub fn init( app: &AppHandle, _api: PluginApi, ) -> crate::Result> { Ok(Notification(app.clone())) } /// Access to the notification APIs. /// /// You can get an instance of this type via [`NotificationExt`](crate::NotificationExt) pub struct Notification(AppHandle); impl crate::NotificationBuilder { /// Shows the notification. /// /// When no title was set with [`Self::title`], the `productName` from the Tauri configuration is used instead. /// Only the title, body, icon and sound of the notification are used on desktop; /// the scheduling, grouping and action related options are ignored. /// /// The notification is dispatched on a background task, so this returns as soon as the payload is prepared. /// /// # Errors /// /// Returns an error when the notification could not be prepared, /// e.g. when the path of the running executable cannot be resolved on Windows. pub fn show(self) -> crate::Result<()> { let mut notification = imp::Notification::new(self.app.config().identifier.clone()); if let Some(title) = self .data .title .or_else(|| self.app.config().product_name.clone()) { notification = notification.title(title); } if let Some(body) = self.data.body { notification = notification.body(body); } if let Some(icon) = self.data.icon { notification = notification.icon(icon); } if let Some(sound) = self.data.sound { notification = notification.sound(sound); } notification.show()?; Ok(()) } } impl Notification { /// Creates a new builder for a notification. /// /// # Examples /// /// ```no_run /// use tauri_plugin_notification::NotificationExt; /// /// fn notify(app: &tauri::AppHandle) { /// app.notification() /// .builder() /// .title("Tauri") /// .body("Tauri is awesome!") /// .show() /// .unwrap(); /// } /// ``` pub fn builder(&self) -> NotificationBuilder { NotificationBuilder::new(self.0.clone()) } /// Requests the permission to send notifications. /// /// Desktop applications do not need to ask for this permission, /// so this always resolves to [`PermissionState::Granted`] without prompting the user. pub fn request_permission(&self) -> crate::Result { Ok(PermissionState::Granted) } /// Checks whether the permission to send notifications was granted. /// /// Desktop applications do not need to ask for this permission, /// so this always resolves to [`PermissionState::Granted`]. pub fn permission_state(&self) -> crate::Result { Ok(PermissionState::Granted) } } mod imp { //! Types and functions related to desktop notifications. #[cfg(windows)] use std::path::MAIN_SEPARATOR as SEP; /// The desktop notification definition. /// /// Allows you to construct a Notification data and send it. /// /// # Examples /// ```rust,no_run /// use tauri_plugin_notification::NotificationExt; /// // first we build the application to access the Tauri configuration /// let app = tauri::Builder::default() /// // on an actual app, remove the string argument /// .build(tauri::generate_context!("test/tauri.conf.json")) /// .expect("error while building tauri application"); /// /// // shows a notification with the given title and body /// app.notification() /// .builder() /// .title("New message") /// .body("You've got a new message.") /// .show(); /// /// // run the app /// app.run(|_app_handle, _event| {}); /// ``` #[allow(dead_code)] #[derive(Debug, Default)] pub struct Notification { /// The notification body. body: Option, /// The notification title. title: Option, /// The notification icon. icon: Option, /// The notification sound. sound: Option, /// The notification identifier identifier: String, } impl Notification { /// Initializes a instance of a Notification. pub fn new(identifier: impl Into) -> Self { Self { identifier: identifier.into(), ..Default::default() } } /// Sets the notification body. #[must_use] pub fn body(mut self, body: impl Into) -> Self { self.body = Some(body.into()); self } /// Sets the notification title. #[must_use] pub fn title(mut self, title: impl Into) -> Self { self.title = Some(title.into()); self } /// Sets the notification icon. #[must_use] pub fn icon(mut self, icon: impl Into) -> Self { self.icon = Some(icon.into()); self } /// Sets the notification sound file. #[must_use] pub fn sound(mut self, sound: impl Into) -> Self { self.sound = Some(sound.into()); self } /// Shows the notification. /// /// # Examples /// /// ```no_run /// use tauri_plugin_notification::NotificationExt; /// /// tauri::Builder::default() /// .setup(|app| { /// app.notification() /// .builder() /// .title("Tauri") /// .body("Tauri is awesome!") /// .show() /// .unwrap(); /// Ok(()) /// }) /// .run(tauri::generate_context!("test/tauri.conf.json")) /// .expect("error while running tauri application"); /// ``` pub fn show(self) -> crate::Result<()> { let mut notification = notify_rust::Notification::new(); if let Some(body) = self.body { notification.body(&body); } if let Some(title) = self.title { notification.summary(&title); } if let Some(icon) = self.icon { notification.icon(&icon); } else { notification.auto_icon(); } if let Some(sound) = self.sound { notification.sound_name(&sound); } #[cfg(windows)] { let exe = tauri::utils::platform::current_exe()?; let exe_dir = exe.parent().expect("failed to get exe directory"); let curr_dir = exe_dir.display().to_string(); // set the notification's System.AppUserModel.ID only when running the installed app if !(curr_dir.ends_with(format!("{SEP}target{SEP}debug").as_str()) || curr_dir.ends_with(format!("{SEP}target{SEP}release").as_str())) { notification.app_id(&self.identifier); } } #[cfg(target_os = "macos")] { let _ = notify_rust::set_application(if tauri::is_dev() { "com.apple.Terminal" } else { &self.identifier }); } tauri::async_runtime::spawn(async move { let _ = notification.show(); }); Ok(()) } /// Shows the notification. Same as [`Self::show`]. #[cfg(feature = "windows7-compat")] #[cfg_attr(docsrs, doc(cfg(feature = "windows7-compat")))] #[deprecated = "Tauri no longer supports Windows 7, use `Self::show` instead."] pub fn notify(self, _app: &tauri::AppHandle) -> crate::Result<()> { self.show() } } }