Deduplicate desktop notifications
This commit is contained in:
@@ -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 }) => {
|
||||
|
||||
Reference in New Issue
Block a user