668bdaad7d
Web fixes from the Wave-2 bug-hunt (findings in LOTUS_TODO): - F1 (security): wipe the decrypted-plaintext search index on SERVER-FORCED logout too (token expiry / remote sign-out) — only manual logout did before. F4: the delete no longer reports success while onblocked (waits, 3s cap). - M1/M2 (data-loss): useBookmarks + useUserNotes account-data writes are now serialized at MODULE scope (single queue + latestRef per client, echo-driven), fixing the cross-instance lost-update clobber (useBookmarks mounts per message row, so a per-instance queue was insufficient — caught in review). - M6: room-history export gets a 200-page cap + Cancel + unmount-abort + correct date-range early-break (raw paginated ts). M4: image compression skips PNG (was flattening transparency to black), bakes EXIF orientation via createImageBitmap, .jpg-renames, and falls back to the original on decode failure instead of dropping the file. M5: MediaGallery lightbox opens the right item (shared thumb guard). M8: audio speed survives async decrypt. - Desktop web wiring: D2 badge sums leaf rooms only (space double-count, like the favicon fix); D3 useTauriDnd re-hydrates from get_tray_dnd on mount; D5 updater has a terminal state. Reviewed; M7 reverted (past-time clamp is an intentional, tested contract). tsc/eslint/prettier clean, build OK, 678 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.7 KiB
TypeScript
40 lines
1.7 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
import { useAtomValue } from 'jotai';
|
|
import { roomToUnreadAtom } from '../state/room/roomToUnread';
|
|
|
|
// Tauri v2 injects __TAURI_INTERNALS__ into the webview at runtime.
|
|
// We use it directly so cinny doesn't need @tauri-apps/api as a dependency.
|
|
type TauriInternals = { invoke: (cmd: string, args?: Record<string, unknown>) => Promise<unknown> };
|
|
const tauriInvoke = (): TauriInternals['invoke'] | undefined =>
|
|
(window as unknown as { __TAURI_INTERNALS__?: TauriInternals }).__TAURI_INTERNALS__?.invoke;
|
|
|
|
export function useTauriNotificationBadge() {
|
|
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
|
const prevHighlightsRef = useRef(0);
|
|
|
|
useEffect(() => {
|
|
const invoke = tauriInvoke();
|
|
if (!invoke) return;
|
|
|
|
let totalHighlights = 0;
|
|
roomToUnread.forEach((unread) => {
|
|
// Sum only leaf rooms (from === null); roomToUnread also holds per-ancestor
|
|
// space aggregates (from = Set), so counting all entries double-counts a
|
|
// space-nested room. Mirrors the favicon fix in ClientNonUIFeatures.
|
|
if (unread.from !== null) return;
|
|
totalHighlights += unread.highlight;
|
|
});
|
|
|
|
invoke('set_badge_count', { count: totalHighlights }).catch(() => {});
|
|
// Mirror the unread state onto the tray icon (visible when minimized to tray).
|
|
invoke('set_tray_unread', { unread: totalHighlights > 0 }).catch(() => {});
|
|
|
|
// Flash the taskbar button when new mentions arrive and the window is not
|
|
// focused, then reset the baseline.
|
|
if (totalHighlights > prevHighlightsRef.current && !document.hasFocus()) {
|
|
invoke('flash_window').catch(() => {});
|
|
}
|
|
prevHighlightsRef.current = totalHighlights;
|
|
}, [roomToUnread]);
|
|
}
|