Files
next-tjwater-frontend/src/features/workbench/hooks/use-scada-alert-notices.ts
T

65 lines
2.6 KiB
TypeScript

import { useEffect, useRef } from "react";
import { dismissMapNotice, showMapNotice } from "@/features/map/core";
import type { WorkbenchAlert } from "../types";
import { useScadaEventAtmosphere, type ScadaEventTask } from "./use-scada-event-atmosphere";
export type { ScadaAlertAtmosphereState } from "./use-scada-event-atmosphere";
/** Announce newly persisted alerts, including while the peripheral panel is closed. */
export function useScadaAlertNotices(
projectId: string | null,
alerts: WorkbenchAlert[] | null,
onOpen: (alert: WorkbenchAlert) => void,
tasks: readonly ScadaEventTask[] | null = null
) {
const atmosphere = useScadaEventAtmosphere(projectId, alerts, tasks);
const openRef = useRef(onOpen);
openRef.current = onOpen;
const state = useRef<{ initialized: boolean; seen: Set<string>; visible: Set<string>; linked: Set<string> }>({
initialized: false, seen: new Set(), visible: new Set(), linked: new Set()
});
useEffect(() => {
const current = { initialized: false, seen: new Set<string>(), visible: new Set<string>(), linked: new Set<string>() };
state.current = current;
return () => { current.visible.forEach((id) => dismissMapNotice(id)); };
}, [projectId]);
useEffect(() => {
if (!alerts) return;
const current = state.current;
const incoming = alerts.filter((alert) => alert.source && "kind" in alert.source
&& alert.source.kind === "scada_replay");
const noticeId = (id: string) => `scada-alert:${projectId}:${id}`;
const activeIds = new Set(incoming.map((alert) => noticeId(alert.id)));
for (const id of current.visible) {
if (!activeIds.has(id) && !current.linked.has(id)) {
dismissMapNotice(id);
current.visible.delete(id);
}
}
for (const alert of incoming) {
if (current.seen.has(alert.id)) continue;
current.seen.add(alert.id);
// Opening a project restores its notification center without replaying old popups.
if (!current.initialized) continue;
const id = noticeId(alert.id);
current.visible.add(id);
if (alert.conditionId) current.linked.add(id);
showMapNotice({
id,
title: alert.title,
message: alert.description,
tone: alert.severity === "critical" ? "error" : "warning",
position: "top-center",
duration: alert.conditionId ? Infinity : 10_000,
actionLabel: alert.conditionId ? "查看调查" : undefined,
onAction: alert.conditionId ? () => openRef.current(alert) : undefined
});
}
current.initialized = true;
}, [alerts, projectId]);
return atmosphere;
}