Files
frigate/web/src/context/statusbar-provider.tsx
T
Josh HawkinsandGitHub 9664d9ceae Notices and status bar improvements (#24459)
* Notice and status bar improvements

Status bar problems added in the same pass got the same `Date.now()` id and overwrote each other, so usually only one showed. Messages now fall back to their text as the id. The desktop status bar shows the most severe message with a count of the rest that opens a popover listing all of them, and the mobile drawer stacks them vertically instead of placing them side by side.

Dismissing a notice hid it for good, so a detector that restarted again after a dismissal was never shown. Dismiss is replaced by acknowledge, which hides a notice until it happens again, and mute, which hides it permanently. Kinds that never repeat (config and stream checks, the update notice) can only be muted. `reopen_at_count` is removed since acknowledge covers the failed login case.

* move camera CPU warnings to notices

High ffmpeg and detect CPU warnings sat in the status bar with no way to dismiss them. They're now `ffmpeg_high_cpu` and `detect_high_cpu` notices, raised per episode by the same tracker as skipped detections. Also stop failed login attempts held from before an acknowledgement from reopening the notice.

* fix mypy and handle missing cpu stats in notices
2026-09-24 15:39:18 -06:00

89 lines
2.4 KiB
TypeScript

import { useState, ReactNode, useCallback, useMemo } from "react";
import {
StatusBarMessagesContext,
StatusMessagesState,
} from "@/context/statusbar-context";
import { ProblemSeverity } from "@/types/stats";
type StatusBarMessagesProviderProps = {
children: ReactNode;
};
export function StatusBarMessagesProvider({
children,
}: StatusBarMessagesProviderProps) {
const [messagesState, setMessagesState] = useState<StatusMessagesState>({});
const messages = useMemo(() => messagesState, [messagesState]);
const addMessage = useCallback(
(
key: string,
message: string,
severity: ProblemSeverity = "error",
messageId?: string,
link?: string,
) => {
if (!key || !message) return;
// the text is the fallback id, so repeating a message replaces it
const id = messageId ?? message;
setMessagesState((prevMessages) => {
const existingMessages = prevMessages[key] || [];
// Check if a message with the same ID already exists
const messageIndex = existingMessages.findIndex((msg) => msg.id === id);
const newMessage = { id, text: message, severity, link };
// If the message exists, replace it, otherwise add the new message
let updatedMessages;
if (messageIndex > -1) {
updatedMessages = [
...existingMessages.slice(0, messageIndex),
newMessage,
...existingMessages.slice(messageIndex + 1),
];
} else {
updatedMessages = [...existingMessages, newMessage];
}
return {
...prevMessages,
[key]: updatedMessages,
};
});
return id;
},
[],
);
const removeMessage = useCallback(
(key: string, messageId: string) => {
if (!messages || !key || !messages[key]) return;
setMessagesState((prevMessages) => ({
...prevMessages,
[key]: prevMessages[key].filter((msg) => msg.id !== messageId),
}));
},
[messages],
);
const clearMessages = useCallback((key: string) => {
setMessagesState((prevMessages) => {
const updatedMessages = { ...prevMessages };
delete updatedMessages[key];
return updatedMessages;
});
}, []);
return (
<StatusBarMessagesContext
value={{ messages, addMessage, removeMessage, clearMessages }}
>
{children}
</StatusBarMessagesContext>
);
}