import * as React from "react"; import { setDesktopAppBadge } from "@/features/notifications/lib/desktop"; import { relayClient } from "@/shared/api/relayClient"; type AppShellLifecycleEffectsOptions = { homeBadgeCountExcludingHighPriority: number; unreadChannelIds: ReadonlySet; unreadChannelNotificationCount: number; }; export function useAppShellLifecycleEffects({ homeBadgeCountExcludingHighPriority, unreadChannelIds, unreadChannelNotificationCount, }: AppShellLifecycleEffectsOptions) { // Prevent webview file:/// navigation on file drop outside the composer. // Scoped to file drags only (text drag-and-drop into inputs still works). // Composer's onDrop fires first (React synthetic before window bubble). React.useEffect(() => { function preventNavigation(e: DragEvent) { if (e.dataTransfer?.types.includes("Files")) { e.preventDefault(); } } window.addEventListener("dragover", preventNavigation); window.addEventListener("drop", preventNavigation); return () => { window.removeEventListener("dragover", preventNavigation); window.removeEventListener("drop", preventNavigation); }; }, []); React.useEffect(() => { let isCancelled = false; const startPreconnect = () => { if (isCancelled) { return; } void relayClient.preconnect().catch((error) => { if (!isCancelled) { console.error("Failed to preconnect to relay", error); } }); }; if ("requestIdleCallback" in window) { const idleId = window.requestIdleCallback(startPreconnect, { timeout: 1_500, }); return () => { isCancelled = true; window.cancelIdleCallback(idleId); }; } const timeoutId = globalThis.setTimeout(startPreconnect, 250); return () => { isCancelled = true; globalThis.clearTimeout(timeoutId); }; }, []); React.useEffect(() => { const count = unreadChannelNotificationCount + homeBadgeCountExcludingHighPriority; void setDesktopAppBadge( count ? { kind: "count", count } : { kind: unreadChannelIds.size ? "dot" : "none" }, ); }, [ homeBadgeCountExcludingHighPriority, unreadChannelIds, unreadChannelNotificationCount, ]); }