Deduplicate desktop notifications

This commit is contained in:
Bohdan Triapitsyn
2026-06-15 14:02:16 +03:00
parent f321562abd
commit a73090f396
2 changed files with 37 additions and 2 deletions
+35
View File
@@ -892,6 +892,37 @@ const focusForegroundWindow = () => {
// click events to silently stop firing after ~1 min.
// See https://blog.bloomca.me/2025/02/22/electron-mac-notifications
const activeNotifications = new Set();
const nativeNotificationClaims = new Map();
const NATIVE_NOTIFICATION_DEDUPE_TTL_MS = 5000;
const getNativeNotificationClaimKey = (payload) => {
const tag = typeof payload?.tag === 'string' ? payload.tag.trim() : '';
if (tag) return tag;
return [payload?.sessionId, payload?.kind, payload?.title, payload?.body]
.filter((value) => typeof value === 'string' && value.trim())
.map((value) => value.trim())
.join('|');
};
const claimNativeNotification = (payload) => {
const key = getNativeNotificationClaimKey(payload);
if (!key) return true;
const now = Date.now();
for (const [claimKey, claimedAt] of nativeNotificationClaims) {
if (now - claimedAt > NATIVE_NOTIFICATION_DEDUPE_TTL_MS) {
nativeNotificationClaims.delete(claimKey);
}
}
const claimedAt = nativeNotificationClaims.get(key) ?? 0;
if (now - claimedAt < NATIVE_NOTIFICATION_DEDUPE_TTL_MS) {
return false;
}
nativeNotificationClaims.set(key, now);
return true;
};
const maybeShowNativeNotification = (rawInput) => {
const payload = normalizeNotificationInput(rawInput);
@@ -905,6 +936,10 @@ const maybeShowNativeNotification = (rawInput) => {
return;
}
if (!claimNativeNotification(payload)) {
return;
}
const title = typeof payload.title === 'string' && payload.title.trim()
? payload.title.trim()
: 'OpenChamber';
@@ -12,7 +12,7 @@ import { OpenChamberLogo } from '@/components/ui/OpenChamberLogo';
import { Icon } from "@/components/icon/Icon";
import { useI18n } from '@/lib/i18n';
import { runtimeFetch } from '@/lib/runtime-fetch';
import { getRuntimeApiBaseUrl, getRuntimeKey, subscribeRuntimeEndpointChanged, switchRuntimeEndpoint } from '@/lib/runtime-switch';
import { getRuntimeApiBaseUrl, subscribeRuntimeEndpointChanged, switchRuntimeEndpoint } from '@/lib/runtime-switch';
import { desktopHostsGet, desktopHostsSet, getDesktopHostApiUrl, normalizeHostUrl } from '@/lib/desktopHosts';
import {
authenticateWithPasskey,
@@ -181,7 +181,7 @@ const applyDesktopClientToken = async (clientToken: string): Promise<void> => {
if (!clientToken) return;
const apiBaseUrl = getRuntimeApiBaseUrl();
await persistDesktopClientToken(apiBaseUrl, clientToken);
switchRuntimeEndpoint({ apiBaseUrl, clientToken, runtimeKey: getRuntimeKey() });
switchRuntimeEndpoint({ apiBaseUrl, clientToken });
};
const AuthShell: React.FC<{ children: React.ReactNode }> = ({ children }) => {