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
This commit is contained in:
Josh Hawkins
2026-09-24 15:39:18 -06:00
committed by GitHub
parent c959df32c9
commit 9664d9ceae
30 changed files with 1307 additions and 707 deletions
+74
View File
@@ -0,0 +1,74 @@
import { useEmbeddingsReindexProgress } from "@/api/ws";
import {
StatusBarMessagesContext,
StatusMessage,
} from "@/context/statusbar-context";
import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
import { ProblemSeverity } from "@/types/stats";
import { useContext, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
const SEVERITY_ORDER: Record<ProblemSeverity, number> = {
error: 0,
warning: 1,
info: 2,
};
/**
* Publishes the stats problems and reindex progress to the status bar, then
* returns every status bar message, most severe first.
*/
export default function useStatusMessages(): StatusMessage[] {
const { t } = useTranslation(["views/system"]);
const { messages, addMessage, clearMessages } = useContext(
StatusBarMessagesContext,
)!;
const stats = useAutoFrigateStats();
const { potentialProblems } = useStats(stats);
useEffect(() => {
clearMessages("stats");
potentialProblems.forEach((problem) => {
addMessage(
"stats",
problem.text,
problem.severity,
undefined,
problem.relevantLink,
);
});
}, [potentialProblems, addMessage, clearMessages]);
const { payload: reindexState } = useEmbeddingsReindexProgress();
useEffect(() => {
if (reindexState) {
if (reindexState.status == "indexing") {
clearMessages("embeddings-reindex");
addMessage(
"embeddings-reindex",
t("stats.reindexingEmbeddings", {
processed: Math.floor(
(reindexState.processed_objects / reindexState.total_objects) *
100,
),
}),
);
}
if (reindexState.status === "completed") {
clearMessages("embeddings-reindex");
}
}
}, [reindexState, addMessage, clearMessages, t]);
return useMemo(
() =>
Object.values(messages)
.flat()
.sort(
(a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity],
),
[messages],
);
}