// 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::{plugin::PluginApi, AppHandle, Runtime}; use crate::{models::*, NotificationBuilder}; pub fn init( app: &AppHandle, _api: PluginApi, ) -> crate::Result> { Ok(Notification(app.clone())) } /// Access to the notification APIs. pub struct Notification(AppHandle); impl crate::NotificationBuilder { 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); } #[cfg(feature = "windows7-compat")] { notification.notify(&self.app)?; } #[cfg(not(feature = "windows7-compat"))] notification.show()?; Ok(()) } } impl Notification { pub fn builder(&self) -> NotificationBuilder { NotificationBuilder::new(self.0.clone()) } pub fn request_permission(&self) -> crate::Result { Ok(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 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 } /// 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"); /// ``` /// /// ## Platform-specific /// /// - **Windows**: Not supported on Windows 7. If your app targets it, enable the `windows7-compat` feature and use [`Self::notify`]. #[cfg_attr( all(not(doc_cfg), feature = "windows7-compat"), deprecated = "This function does not work on Windows 7. Use `Self::notify` instead." )] pub fn show(self) -> crate::Result<()> { let mut notification = crate::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(); } #[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 _ = crate::notify_rust::set_application(if cfg!(feature = "custom-protocol") { &self.identifier } else { "com.apple.Terminal" }); } tauri::async_runtime::spawn(async move { let _ = notification.show(); }); Ok(()) } /// Shows the notification. This API is similar to [`Self::show`], but it also works on Windows 7. /// /// # Examples /// /// ```no_run /// use tauri_plugin_notification::NotificationExt; /// /// tauri::Builder::default() /// .setup(move |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"); /// ``` #[cfg(feature = "windows7-compat")] #[cfg_attr(doc_cfg, doc(cfg(feature = "windows7-compat")))] #[allow(unused_variables)] pub fn notify(self, app: &tauri::AppHandle) -> crate::Result<()> { #[cfg(windows)] { fn is_windows_7() -> bool { let v = windows_version::OsVersion::current(); // windows 7 is 6.1 v.major == 6 && v.minor == 1 } if is_windows_7() { self.notify_win7(app) } else { #[allow(deprecated)] self.show() } } #[cfg(not(windows))] { #[allow(deprecated)] self.show() } } #[cfg(all(windows, feature = "windows7-compat"))] fn notify_win7(self, app: &tauri::AppHandle) -> crate::Result<()> { let app_ = app.clone(); let _ = app.clone().run_on_main_thread(move || { let mut notification = win7_notifications::Notification::new(); if let Some(body) = self.body { notification.body(&body); } if let Some(title) = self.title { notification.summary(&title); } if let Some(icon) = app_.default_window_icon() { notification.icon(icon.rgba().to_vec(), icon.width(), icon.height()); } let _ = notification.show(); }); Ok(()) } } }