import { useCallback, useMemo, useRef, useState, type ReactNode } from "react"; import { NotificationSessionContext } from "./notification-session-context"; function addAll(current: ReadonlySet, ids: T[]): ReadonlySet { if (ids.every((id) => current.has(id))) { return current; } const next = new Set(current); ids.forEach((id) => next.add(id)); return next; } /** * Dismissed and read notification ids for this app session only. Nothing is persisted, so a * reload starts over and a condition that still holds shows up again. */ export function NotificationSessionProvider({ children }: { children: ReactNode }) { const [dismissedIds, setDismissedIds] = useState>(() => new Set()); const [readIds, setReadIds] = useState>(() => new Set()); const [slaBannerDismissedIds, setSlaBannerDismissedIds] = useState>( () => new Set(), ); // A ref, not state: claiming must be synchronous so a re-run effect can never toast twice. const toastedSlaIds = useRef(new Set()); const dismiss = useCallback((ids: string[]) => { setDismissedIds((current) => addAll(current, ids)); }, []); const markRead = useCallback((ids: string[]) => { setReadIds((current) => addAll(current, ids)); }, []); const dismissSlaBanner = useCallback((workOrderIds: number[]) => { setSlaBannerDismissedIds((current) => addAll(current, workOrderIds)); }, []); const claimSlaToasts = useCallback((workOrderIds: number[]) => { const claimed = workOrderIds.filter((id) => !toastedSlaIds.current.has(id)); claimed.forEach((id) => toastedSlaIds.current.add(id)); return claimed; }, []); const value = useMemo( () => ({ dismissedIds, readIds, dismiss, markRead, slaBannerDismissedIds, dismissSlaBanner, claimSlaToasts, }), [ dismissedIds, readIds, dismiss, markRead, slaBannerDismissedIds, dismissSlaBanner, claimSlaToasts, ], ); return ( {children} ); }