2026-09-25 11:36:25 -03:00
|
|
|
import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
|
2026-09-18 12:55:54 -03:00
|
|
|
import { NotificationSessionContext } from "./notification-session-context";
|
|
|
|
|
|
2026-09-25 11:36:25 -03:00
|
|
|
function addAll<T>(current: ReadonlySet<T>, ids: T[]): ReadonlySet<T> {
|
2026-09-18 12:55:54 -03:00
|
|
|
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<ReadonlySet<string>>(() => new Set());
|
|
|
|
|
const [readIds, setReadIds] = useState<ReadonlySet<string>>(() => new Set());
|
2026-09-25 11:36:25 -03:00
|
|
|
const [slaBannerDismissedIds, setSlaBannerDismissedIds] = useState<ReadonlySet<number>>(
|
|
|
|
|
() => new Set(),
|
|
|
|
|
);
|
|
|
|
|
// A ref, not state: claiming must be synchronous so a re-run effect can never toast twice.
|
|
|
|
|
const toastedSlaIds = useRef(new Set<number>());
|
2026-09-18 12:55:54 -03:00
|
|
|
|
|
|
|
|
const dismiss = useCallback((ids: string[]) => {
|
|
|
|
|
setDismissedIds((current) => addAll(current, ids));
|
|
|
|
|
}, []);
|
|
|
|
|
const markRead = useCallback((ids: string[]) => {
|
|
|
|
|
setReadIds((current) => addAll(current, ids));
|
|
|
|
|
}, []);
|
2026-09-25 11:36:25 -03:00
|
|
|
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;
|
|
|
|
|
}, []);
|
2026-09-18 12:55:54 -03:00
|
|
|
|
|
|
|
|
const value = useMemo(
|
2026-09-25 11:36:25 -03:00
|
|
|
() => ({
|
|
|
|
|
dismissedIds,
|
|
|
|
|
readIds,
|
|
|
|
|
dismiss,
|
|
|
|
|
markRead,
|
|
|
|
|
slaBannerDismissedIds,
|
|
|
|
|
dismissSlaBanner,
|
|
|
|
|
claimSlaToasts,
|
|
|
|
|
}),
|
|
|
|
|
[
|
|
|
|
|
dismissedIds,
|
|
|
|
|
readIds,
|
|
|
|
|
dismiss,
|
|
|
|
|
markRead,
|
|
|
|
|
slaBannerDismissedIds,
|
|
|
|
|
dismissSlaBanner,
|
|
|
|
|
claimSlaToasts,
|
|
|
|
|
],
|
2026-09-18 12:55:54 -03:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<NotificationSessionContext.Provider value={value}>
|
|
|
|
|
{children}
|
|
|
|
|
</NotificationSessionContext.Provider>
|
|
|
|
|
);
|
|
|
|
|
}
|