fix: keep GNOME notifications open (fix #2566) (#3588)

* experimenting...

Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>

* omit unconfirmed fact from .changes

Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>

* fix(notification): send untracked Linux notifications on the shared connection

Holding notify-rust's handle with `on_close` blocked an async runtime
worker until the server closed the notification, and opened a D-Bus
connection per notification. Since GNOME keeps notifications in its tray,
a handful of them could stall every async task of the app.

Send every notification on the connection the plugin already keeps open
for its action listener instead: it keeps the sender's name alive for
GNOME at the cost of a single connection and thread, and the listener
runs the `on_close` callbacks.

* fix(notification): offer `on_close` on the BSDs too

The BSDs share the D-Bus implementation with Linux, including the GNOME
issue, so gate `NotificationBuilder::on_close` like the rest of that
implementation and document when the callback runs.

* fix(notification): run `on_close` without the tracker lock held

The callback ran on the listener thread while the guard of the callback
map was alive, and a panic in it ended that thread, which then reported
no action of any notification for the rest of the app's life. Take the
callback out of the map before running it and contain its panics.

* fix(notification): use the covector package names in the change files

`tauri-plugin-notification` and `@tauri-apps/plugin-notification` are
not packages of .changes/config.json, so covector refused the change
file. Use `notification` and `notification-js`, and record the new
`on_close` API as a feature of its own.

Also let `on_close` take an `FnOnce`, since it runs at most once.

---------

Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
BestCodesandLucas Nogueira authored and GitHub committed 2026-10-12 09:46:43 -03:00
1 parent 8efe845612
commit 4356356dab
4 files changed
+121 -18

No files matched your search

+31
View File
@@ -14,6 +14,8 @@
)]
use serde::Serialize;
#[cfg(all(desktop, unix, not(target_os = "macos")))]
use std::fmt;
#[cfg(desktop)]
use tauri::AppHandle;
#[cfg(mobile)]
@@ -50,6 +52,18 @@ pub struct NotificationBuilder<R: Runtime> {
#[cfg(mobile)]
handle: PluginHandle<R>,
pub(crate) data: NotificationData,
#[cfg(all(desktop, unix, not(target_os = "macos")))]
on_close: Option<OnClose>,
}
#[cfg(all(desktop, unix, not(target_os = "macos")))]
pub(crate) struct OnClose(Box<dyn FnOnce() + Send + 'static>);
#[cfg(all(desktop, unix, not(target_os = "macos")))]
impl fmt::Debug for OnClose {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("OnClose")
}
}
impl<R: Runtime> NotificationBuilder<R> {
@@ -58,6 +72,8 @@ impl<R: Runtime> NotificationBuilder<R> {
Self {
app,
data: Default::default(),
#[cfg(all(desktop, unix, not(target_os = "macos")))]
on_close: None,
}
}
@@ -212,6 +228,21 @@ impl<R: Runtime> NotificationBuilder<R> {
self.data.silent = true;
self
}
/// Sets a callback to run when the notification closes, whether the user dismissed it, it
/// expired or the app removed it.
///
/// The callback runs on a background thread and should return quickly. It does not run when
/// the session bus cannot be reached, or when the app exits first.
///
/// ## Platform-specific
///
/// - **Linux and the BSDs**: only available there.
#[cfg(all(desktop, unix, not(target_os = "macos")))]
pub fn on_close<F: FnOnce() + Send + 'static>(mut self, handler: F) -> Self {
self.on_close = Some(OnClose(Box::new(handler)));
self
}
}
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the notification APIs.