19242b4152
- EventToastBridge checks notification_toast_level and notification_mute_categories - toast-rules.ts: event-to-category/severity mapping functions - Settings page: notification preferences section (toast level dropdown, mute checkboxes) - Settings API types extended with notification preference fields - 17 frontend tests (toast-rules + bridge) - Preference hierarchy: mute categories → toast level → show/hide Quality gates: vitest 17 passed, tsc clean, eslint clean
78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import { useEventContext } from "../state/events";
|
|
import {
|
|
handleEventToast,
|
|
mapEventToCategory,
|
|
mapEventToSeverity,
|
|
} from "./toast-rules";
|
|
import { getUserConfig } from "../api/settings";
|
|
import type { UserConfig } from "../api/settings";
|
|
|
|
interface ToastConfig {
|
|
notification_toast_level: string;
|
|
notification_mute_categories: string[];
|
|
}
|
|
|
|
export function EventToastBridge(): JSX.Element | null {
|
|
const { events } = useEventContext();
|
|
const processedRef = useRef<Set<string>>(new Set());
|
|
const [config, setConfig] = useState<ToastConfig | null>(null);
|
|
|
|
useEffect(() => {
|
|
getUserConfig()
|
|
.then((c) => {
|
|
setConfig({
|
|
notification_toast_level: c.notification_toast_level ?? "all",
|
|
notification_mute_categories: c.notification_mute_categories ?? [],
|
|
});
|
|
})
|
|
.catch(() => {
|
|
setConfig({
|
|
notification_toast_level: "all",
|
|
notification_mute_categories: [],
|
|
});
|
|
});
|
|
|
|
const handler = (e: Event) => {
|
|
const detail = (e as CustomEvent<Partial<UserConfig>>).detail;
|
|
if (detail) {
|
|
setConfig((prev) => ({
|
|
notification_toast_level:
|
|
detail.notification_toast_level ??
|
|
prev?.notification_toast_level ??
|
|
"all",
|
|
notification_mute_categories:
|
|
detail.notification_mute_categories ??
|
|
prev?.notification_mute_categories ??
|
|
[],
|
|
}));
|
|
}
|
|
};
|
|
|
|
window.addEventListener("userconfig:updated", handler);
|
|
return () => window.removeEventListener("userconfig:updated", handler);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!config) return;
|
|
|
|
for (const event of events) {
|
|
const key = `${event.correlation_id}:${event.timestamp}`;
|
|
if (processedRef.current.has(key)) continue;
|
|
processedRef.current.add(key);
|
|
|
|
const category = mapEventToCategory(event);
|
|
const severity = mapEventToSeverity(event);
|
|
|
|
if (config.notification_toast_level === "none") continue;
|
|
if (config.notification_toast_level === "errors" && severity !== "error")
|
|
continue;
|
|
if (config.notification_mute_categories.includes(category)) continue;
|
|
|
|
handleEventToast(event);
|
|
}
|
|
}, [events, config]);
|
|
|
|
return null;
|
|
}
|