mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-18 22:28:23 +03:00
preserve form data when changing cameras
This commit is contained in:
parent
a48304d3a2
commit
c90a547ec9
@ -178,8 +178,7 @@ export function ConfigSection({
|
|||||||
const [dirtyOverrides, setDirtyOverrides] = useState<JsonValue | undefined>(
|
const [dirtyOverrides, setDirtyOverrides] = useState<JsonValue | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
const [baselineFormData, setBaselineFormData] =
|
const baselineByKeyRef = useRef<Record<string, ConfigSectionData>>({});
|
||||||
useState<ConfigSectionData | null>(null);
|
|
||||||
|
|
||||||
const pendingData =
|
const pendingData =
|
||||||
pendingDataBySection !== undefined
|
pendingDataBySection !== undefined
|
||||||
@ -202,6 +201,7 @@ export function ConfigSection({
|
|||||||
const [restartDialogOpen, setRestartDialogOpen] = useState(false);
|
const [restartDialogOpen, setRestartDialogOpen] = useState(false);
|
||||||
const isResettingRef = useRef(false);
|
const isResettingRef = useRef(false);
|
||||||
const isInitializingRef = useRef(true);
|
const isInitializingRef = useRef(true);
|
||||||
|
const lastPendingDataKeyRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const updateTopic =
|
const updateTopic =
|
||||||
level === "camera" && cameraName
|
level === "camera" && cameraName
|
||||||
@ -268,6 +268,23 @@ export function ConfigSection({
|
|||||||
return sanitizeSectionData(baseData);
|
return sanitizeSectionData(baseData);
|
||||||
}, [rawFormData, modifiedSchema, sanitizeSectionData]);
|
}, [rawFormData, modifiedSchema, sanitizeSectionData]);
|
||||||
|
|
||||||
|
const baselineSnapshot = useMemo(() => {
|
||||||
|
if (!pendingData) {
|
||||||
|
const snapshot = cloneDeep(formData as ConfigSectionData);
|
||||||
|
baselineByKeyRef.current[pendingDataKey] = snapshot;
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cached = baselineByKeyRef.current[pendingDataKey];
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = cloneDeep(formData as ConfigSectionData);
|
||||||
|
baselineByKeyRef.current[pendingDataKey] = snapshot;
|
||||||
|
return snapshot;
|
||||||
|
}, [formData, pendingData, pendingDataKey]);
|
||||||
|
|
||||||
const schemaDefaults = useMemo(() => {
|
const schemaDefaults = useMemo(() => {
|
||||||
if (!modifiedSchema) {
|
if (!modifiedSchema) {
|
||||||
return {};
|
return {};
|
||||||
@ -297,21 +314,29 @@ export function ConfigSection({
|
|||||||
// This prevents RJSF's initial onChange call from being treated as a user edit
|
// This prevents RJSF's initial onChange call from being treated as a user edit
|
||||||
// Only clear if pendingData is managed locally (not by parent)
|
// Only clear if pendingData is managed locally (not by parent)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pendingData) {
|
const pendingKeyChanged = lastPendingDataKeyRef.current !== pendingDataKey;
|
||||||
|
|
||||||
|
if (pendingKeyChanged) {
|
||||||
|
lastPendingDataKeyRef.current = pendingDataKey;
|
||||||
|
isInitializingRef.current = true;
|
||||||
|
setPendingOverrides(undefined);
|
||||||
|
setDirtyOverrides(undefined);
|
||||||
|
} else if (!pendingData) {
|
||||||
isInitializingRef.current = true;
|
isInitializingRef.current = true;
|
||||||
setPendingOverrides(undefined);
|
setPendingOverrides(undefined);
|
||||||
setDirtyOverrides(undefined);
|
setDirtyOverrides(undefined);
|
||||||
setBaselineFormData(cloneDeep(formData as ConfigSectionData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onPendingDataChange === undefined) {
|
if (onPendingDataChange === undefined) {
|
||||||
setPendingData(null);
|
setPendingData(null);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
formData,
|
|
||||||
pendingData,
|
|
||||||
setPendingData,
|
|
||||||
setBaselineFormData,
|
|
||||||
onPendingDataChange,
|
onPendingDataChange,
|
||||||
|
pendingData,
|
||||||
|
pendingDataKey,
|
||||||
|
setPendingData,
|
||||||
|
setDirtyOverrides,
|
||||||
|
setPendingOverrides,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -344,7 +369,7 @@ export function ConfigSection({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sanitizedData = sanitizeSectionData(data as ConfigSectionData);
|
const sanitizedData = sanitizeSectionData(data as ConfigSectionData);
|
||||||
let nextBaselineFormData = baselineFormData ?? formData;
|
const nextBaselineFormData = baselineSnapshot;
|
||||||
const overrides = buildOverrides(
|
const overrides = buildOverrides(
|
||||||
sanitizedData,
|
sanitizedData,
|
||||||
compareBaseData,
|
compareBaseData,
|
||||||
@ -353,16 +378,6 @@ export function ConfigSection({
|
|||||||
setPendingOverrides(overrides as JsonValue | undefined);
|
setPendingOverrides(overrides as JsonValue | undefined);
|
||||||
if (isInitializingRef.current && !pendingData) {
|
if (isInitializingRef.current && !pendingData) {
|
||||||
isInitializingRef.current = false;
|
isInitializingRef.current = false;
|
||||||
if (!baselineFormData) {
|
|
||||||
// Always use formData (server data + schema defaults) for the
|
|
||||||
// baseline snapshot, NOT sanitizedData from the onChange callback.
|
|
||||||
// If a custom component (e.g., zone checkboxes) triggers onChange
|
|
||||||
// before RJSF's initial onChange, sanitizedData would include the
|
|
||||||
// user's modification, corrupting the baseline.
|
|
||||||
const baselineSnapshot = cloneDeep(formData as ConfigSectionData);
|
|
||||||
setBaselineFormData(baselineSnapshot);
|
|
||||||
nextBaselineFormData = baselineSnapshot;
|
|
||||||
}
|
|
||||||
if (overrides === undefined) {
|
if (overrides === undefined) {
|
||||||
setPendingData(null);
|
setPendingData(null);
|
||||||
setPendingOverrides(undefined);
|
setPendingOverrides(undefined);
|
||||||
@ -392,14 +407,12 @@ export function ConfigSection({
|
|||||||
setPendingData,
|
setPendingData,
|
||||||
setPendingOverrides,
|
setPendingOverrides,
|
||||||
setDirtyOverrides,
|
setDirtyOverrides,
|
||||||
baselineFormData,
|
baselineSnapshot,
|
||||||
setBaselineFormData,
|
|
||||||
formData,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentFormData = pendingData || formData;
|
const currentFormData = pendingData || formData;
|
||||||
const effectiveBaselineFormData = baselineFormData ?? formData;
|
const effectiveBaselineFormData = baselineSnapshot;
|
||||||
|
|
||||||
const currentOverrides = useMemo(() => {
|
const currentOverrides = useMemo(() => {
|
||||||
if (!currentFormData || typeof currentFormData !== "object") {
|
if (!currentFormData || typeof currentFormData !== "object") {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import { Children, useState, useEffect } from "react";
|
import { Children, useState, useEffect, useRef } from "react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -142,6 +142,10 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
|||||||
|
|
||||||
const hasModifiedDescendants = checkSubtreeModified(fieldPath);
|
const hasModifiedDescendants = checkSubtreeModified(fieldPath);
|
||||||
const [isOpen, setIsOpen] = useState(hasModifiedDescendants);
|
const [isOpen, setIsOpen] = useState(hasModifiedDescendants);
|
||||||
|
const resetKey = `${formContext?.level ?? "global"}::${
|
||||||
|
formContext?.cameraName ?? "global"
|
||||||
|
}`;
|
||||||
|
const lastResetKeyRef = useRef<string | null>(null);
|
||||||
|
|
||||||
// Auto-expand collapsible when modifications are detected
|
// Auto-expand collapsible when modifications are detected
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -191,6 +195,14 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
|||||||
setShowAdvanced(true);
|
setShowAdvanced(true);
|
||||||
}
|
}
|
||||||
}, [hasModifiedAdvanced]);
|
}, [hasModifiedAdvanced]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lastResetKeyRef.current !== resetKey) {
|
||||||
|
lastResetKeyRef.current = resetKey;
|
||||||
|
setIsOpen(hasModifiedDescendants);
|
||||||
|
setShowAdvanced(hasModifiedAdvanced);
|
||||||
|
}
|
||||||
|
}, [resetKey, hasModifiedDescendants, hasModifiedAdvanced]);
|
||||||
const { children } = props as ObjectFieldTemplateProps & {
|
const { children } = props as ObjectFieldTemplateProps & {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user