Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion desktop/src/features/notifications/lib/desktop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import {
isPermissionGranted,
onAction,
requestPermission,
sendNotification,
} from "@tauri-apps/plugin-notification";
import { isLinuxPlatform, isMacPlatform } from "@/shared/lib/platform";
import {
isLinuxPlatform,
isMacPlatform,
isWindowsPlatform,
} from "@/shared/lib/platform";

// Backend event emitted when a native Linux notification is clicked or a
// queued macOS activation becomes available. See src-tauri notification code.
Expand Down Expand Up @@ -146,6 +151,19 @@ export async function getDesktopNotificationPermissionState(): Promise<DesktopNo
}
}

// On Windows the app runs in WebView2, whose DOM `Notification.permission`
// reports "denied" by default even though native toasts are delivered by the
// Tauri notification plugin. Trusting the DOM value falsely surfaces the
// "notifications are blocked" banner and refuses the enable toggle, so read
// the plugin (the real delivery path here) as the source of truth instead.
if (isTauri() && isWindowsPlatform()) {
try {
return (await isPermissionGranted()) ? "granted" : "default";
} catch {
return "default";
}
}

if (window.Notification.permission !== "default") {
return window.Notification.permission;
}
Expand Down Expand Up @@ -443,6 +461,26 @@ export async function sendDesktopNotification(
}
}

// On Windows the DOM `Notification` API inside WebView2 does not surface an OS
// toast. Post through the Tauri notification plugin, which uses the native
// Windows toast system. Click-through comes back via the plugin's `onAction`
// listener (registered for non-Linux, non-macOS platforms in
// listenForDesktopNotificationActions), which reads the target from `extra`.
// `silent` avoids the toast doubling the app's own in-app notification sound.
if (isTauri() && isWindowsPlatform()) {
try {
sendNotification({
title: payload.title,
body: payload.body,
extra: notificationExtra(payload.target),
silent: true,
});
return true;
} catch {
return false;
}
}

// block/buzz#5081 — WebKit throws `NotificationError` from the constructor
// when the notification backend becomes temporarily unavailable. Callers
// discard the returned promise without a rejection handler, so an
Expand Down
9 changes: 9 additions & 0 deletions desktop/src/shared/lib/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ export function isLinuxPlatform(): boolean {
);
}

/** Returns true on Windows desktops (navigator.platform reports "Win32"/"Win64"). */
export function isWindowsPlatform(): boolean {
if (typeof navigator === "undefined") {
return false;
}

return /win/i.test(navigator.platform);
}

/**
* The platform's normal application-shortcut modifier:
* - macOS: Command (Meta)
Expand Down