65 lines
2.6 KiB
TypeScript
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;
|
|
}
|