Adjust Frigate config editor when in safe mode

This commit is contained in:
Nicolas Mowen 2025-05-24 10:16:53 -06:00
parent 35ece12f44
commit 081c9ec228
2 changed files with 29 additions and 16 deletions

View File

@ -1,6 +1,8 @@
{ {
"documentTitle": "Config Editor - Frigate", "documentTitle": "Config Editor - Frigate",
"configEditor": "Config Editor", "configEditor": "Config Editor",
"safeConfigEditor": "Config Editor (Safe Mode)",
"safeModeDescription": "Frigate is in safe mode due to a config validation error.",
"copyConfig": "Copy Config", "copyConfig": "Copy Config",
"saveAndRestart": "Save & Restart", "saveAndRestart": "Save & Restart",
"saveOnly": "Save Only", "saveOnly": "Save Only",

View File

@ -1,7 +1,7 @@
import useSWR from "swr"; import useSWR from "swr";
import * as monaco from "monaco-editor"; import * as monaco from "monaco-editor";
import { configureMonacoYaml } from "monaco-yaml"; import { configureMonacoYaml } from "monaco-yaml";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import ActivityIndicator from "@/components/indicators/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
@ -16,6 +16,7 @@ import { MdOutlineRestartAlt } from "react-icons/md";
import RestartDialog from "@/components/overlay/dialog/RestartDialog"; import RestartDialog from "@/components/overlay/dialog/RestartDialog";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useRestart } from "@/api/ws"; import { useRestart } from "@/api/ws";
import { FrigateConfig } from "@/types/frigateConfig";
type SaveOptions = "saveonly" | "restart"; type SaveOptions = "saveonly" | "restart";
@ -32,7 +33,10 @@ function ConfigEditor() {
document.title = t("documentTitle"); document.title = t("documentTitle");
}, [t]); }, [t]);
const { data: config } = useSWR<string>("config/raw"); const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
const { data: rawConfig } = useSWR<string>("config/raw");
const { theme, systemTheme } = useTheme(); const { theme, systemTheme } = useTheme();
const [error, setError] = useState<string | undefined>(); const [error, setError] = useState<string | undefined>();
@ -102,7 +106,7 @@ function ConfigEditor() {
}, [onHandleSaveConfig]); }, [onHandleSaveConfig]);
useEffect(() => { useEffect(() => {
if (!config) { if (!rawConfig) {
return; return;
} }
@ -129,9 +133,9 @@ function ConfigEditor() {
} }
if (!modelRef.current) { if (!modelRef.current) {
modelRef.current = monaco.editor.createModel(config, "yaml", modelUri); modelRef.current = monaco.editor.createModel(rawConfig, "yaml", modelUri);
} else { } else {
modelRef.current.setValue(config); modelRef.current.setValue(rawConfig);
} }
const container = configRef.current; const container = configRef.current;
@ -164,32 +168,32 @@ function ConfigEditor() {
} }
schemaConfiguredRef.current = false; schemaConfiguredRef.current = false;
}; };
}, [config, apiHost, systemTheme, theme, onHandleSaveConfig]); }, [rawConfig, apiHost, systemTheme, theme, onHandleSaveConfig]);
// monitoring state // monitoring state
const [hasChanges, setHasChanges] = useState(false); const [hasChanges, setHasChanges] = useState(false);
useEffect(() => { useEffect(() => {
if (!config || !modelRef.current) { if (!rawConfig || !modelRef.current) {
return; return;
} }
modelRef.current.onDidChangeContent(() => { modelRef.current.onDidChangeContent(() => {
if (modelRef.current?.getValue() != config) { if (modelRef.current?.getValue() != rawConfig) {
setHasChanges(true); setHasChanges(true);
} else { } else {
setHasChanges(false); setHasChanges(false);
} }
}); });
}, [config]); }, [rawConfig]);
useEffect(() => { useEffect(() => {
if (config && modelRef.current) { if (rawConfig && modelRef.current) {
modelRef.current.setValue(config); modelRef.current.setValue(rawConfig);
setHasChanges(false); setHasChanges(false);
} }
}, [config]); }, [rawConfig]);
useEffect(() => { useEffect(() => {
let listener: ((e: BeforeUnloadEvent) => void) | undefined; let listener: ((e: BeforeUnloadEvent) => void) | undefined;
@ -209,7 +213,7 @@ function ConfigEditor() {
}; };
}, [hasChanges, t]); }, [hasChanges, t]);
if (!config) { if (!rawConfig) {
return <ActivityIndicator />; return <ActivityIndicator />;
} }
@ -217,9 +221,16 @@ function ConfigEditor() {
<div className="absolute bottom-2 left-0 right-0 top-2 md:left-2"> <div className="absolute bottom-2 left-0 right-0 top-2 md:left-2">
<div className="relative h-full overflow-hidden"> <div className="relative h-full overflow-hidden">
<div className="mr-1 flex items-center justify-between"> <div className="mr-1 flex items-center justify-between">
<div>
<Heading as="h2" className="mb-0 ml-1 md:ml-0"> <Heading as="h2" className="mb-0 ml-1 md:ml-0">
{t("configEditor")} {t(config?.safe_mode ? "safeConfigEditor" : "configEditor")}
</Heading> </Heading>
{config?.safe_mode && (
<div className="text-sm text-secondary-foreground">
{t("safeModeDescription")}
</div>
)}
</div>
<div className="flex flex-row gap-1"> <div className="flex flex-row gap-1">
<Button <Button
size="sm" size="sm"