From fa1ac7bc957f3b8b13b97e85eac40729f16b510b Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 29 Dec 2025 20:54:33 -0600 Subject: feat(desktop): system notifications --- packages/desktop/package.json | 1 + packages/desktop/src-tauri/Cargo.lock | 58 ++++++++++++++++++++++ packages/desktop/src-tauri/Cargo.toml | 1 + .../desktop/src-tauri/capabilities/default.json | 5 ++ packages/desktop/src-tauri/src/lib.rs | 1 + packages/desktop/src/index.tsx | 29 +++++++++++ 6 files changed, 95 insertions(+) (limited to 'packages/desktop') diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 9c791f25c..3edb5baf2 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -18,6 +18,7 @@ "@tauri-apps/plugin-dialog": "~2", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-os": "~2", + "@tauri-apps/plugin-notification": "~2", "@tauri-apps/plugin-process": "~2", "@tauri-apps/plugin-shell": "~2", "@tauri-apps/plugin-store": "~2", diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index 0bf5f7013..11afce91e 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -2210,6 +2210,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mac-notification-sys" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fd3f75411f4725061682ed91f131946e912859d0044d39c4ec0aac818d7621" +dependencies = [ + "cc", + "objc2 0.6.3", + "objc2-foundation 0.3.2", + "time", +] + [[package]] name = "markup5ever" version = "0.14.1" @@ -2384,6 +2396,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "notify-rust" +version = "4.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" +dependencies = [ + "futures-lite", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2758,6 +2784,7 @@ dependencies = [ "tauri-plugin-clipboard-manager", "tauri-plugin-dialog", "tauri-plugin-http", + "tauri-plugin-notification", "tauri-plugin-opener", "tauri-plugin-os", "tauri-plugin-process", @@ -4519,6 +4546,25 @@ dependencies = [ "urlpattern", ] +[[package]] +name = "tauri-plugin-notification" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fc2c5ff41105bd1f7242d8201fdf3efd70749b82fa013a17f2126357d194cc" +dependencies = [ + "log", + "notify-rust", + "rand 0.9.2", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.17", + "time", + "url", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.2" @@ -4754,6 +4800,18 @@ dependencies = [ "toml 0.9.8", ] +[[package]] +name = "tauri-winrt-notification" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" +dependencies = [ + "quick-xml 0.37.5", + "thiserror 2.0.17", + "windows", + "windows-version", +] + [[package]] name = "tempfile" version = "3.23.0" diff --git a/packages/desktop/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml index 0463966c0..b7c238f06 100644 --- a/packages/desktop/src-tauri/Cargo.toml +++ b/packages/desktop/src-tauri/Cargo.toml @@ -28,6 +28,7 @@ tauri-plugin-store = "2" tauri-plugin-window-state = "2" tauri-plugin-clipboard-manager = "2" tauri-plugin-http = "2" +tauri-plugin-notification = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/packages/desktop/src-tauri/capabilities/default.json b/packages/desktop/src-tauri/capabilities/default.json index c805f623b..1b305aebe 100644 --- a/packages/desktop/src-tauri/capabilities/default.json +++ b/packages/desktop/src-tauri/capabilities/default.json @@ -8,6 +8,10 @@ "opener:default", "core:window:allow-start-dragging", "core:webview:allow-set-webview-zoom", + "core:window:allow-is-focused", + "core:window:allow-show", + "core:window:allow-unminimize", + "core:window:allow-set-focus", "shell:default", "updater:default", "dialog:default", @@ -15,6 +19,7 @@ "store:default", "window-state:default", "os:default", + "notification:default", { "identifier": "http:default", "allow": [{ "url": "http://*" }, { "url": "https://*" }, { "url": "http://*:*/*" }] diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 3c08841ab..46c0ab256 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -198,6 +198,7 @@ pub fn run() { .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_notification::init()) .plugin(PinchZoomDisablePlugin) .invoke_handler(tauri::generate_handler![ kill_sidecar, diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 58aca8fd1..a36da4125 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -12,6 +12,8 @@ import { UPDATER_ENABLED } from "./updater" import { createMenu } from "./menu" import { check, Update } from "@tauri-apps/plugin-updater" import { invoke } from "@tauri-apps/api/core" +import { getCurrentWindow } from "@tauri-apps/api/window" +import { isPermissionGranted, requestPermission } from "@tauri-apps/plugin-notification" import { relaunch } from "@tauri-apps/plugin-process" import pkg from "../package.json" @@ -94,6 +96,33 @@ const platform: Platform = { await relaunch() }, + notify: async (title, description, href) => { + const granted = await isPermissionGranted().catch(() => false) + const permission = granted ? "granted" : await requestPermission().catch(() => "denied") + if (permission !== "granted") return + + const win = getCurrentWindow() + const focused = await win.isFocused().catch(() => document.hasFocus()) + if (focused) return + + await Promise.resolve() + .then(() => { + const notification = new Notification(title, { body: description ?? "" }) + notification.onclick = () => { + const win = getCurrentWindow() + void win.show().catch(() => undefined) + void win.unminimize().catch(() => undefined) + void win.setFocus().catch(() => undefined) + if (href) { + window.history.pushState(null, "", href) + window.dispatchEvent(new PopStateEvent("popstate")) + } + notification.close() + } + }) + .catch(() => undefined) + }, + // @ts-expect-error fetch: tauriFetch, } -- cgit v1.2.3