mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 10:33:11 +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>(
|
||||
undefined,
|
||||
);
|
||||
const [baselineFormData, setBaselineFormData] =
|
||||
useState<ConfigSectionData | null>(null);
|
||||
const baselineByKeyRef = useRef<Record<string, ConfigSectionData>>({});
|
||||
|
||||
const pendingData =
|
||||
pendingDataBySection !== undefined
|
||||
@ -202,6 +201,7 @@ export function ConfigSection({
|
||||
const [restartDialogOpen, setRestartDialogOpen] = useState(false);
|
||||
const isResettingRef = useRef(false);
|
||||
const isInitializingRef = useRef(true);
|
||||
const lastPendingDataKeyRef = useRef<string | null>(null);
|
||||
|
||||
const updateTopic =
|
||||
level === "camera" && cameraName
|
||||
@ -268,6 +268,23 @@ export function ConfigSection({
|
||||
return sanitizeSectionData(baseData);
|
||||
}, [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(() => {
|
||||
if (!modifiedSchema) {
|
||||
return {};
|
||||
@ -297,21 +314,29 @@ export function ConfigSection({
|
||||
// This prevents RJSF's initial onChange call from being treated as a user edit
|
||||
// Only clear if pendingData is managed locally (not by parent)
|
||||
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;
|
||||
setPendingOverrides(undefined);
|
||||
setDirtyOverrides(undefined);
|
||||
setBaselineFormData(cloneDeep(formData as ConfigSectionData));
|
||||
}
|
||||
|
||||
if (onPendingDataChange === undefined) {
|
||||
setPendingData(null);
|
||||
}
|
||||
}, [
|
||||
formData,
|
||||
pendingData,
|
||||
setPendingData,
|
||||
setBaselineFormData,
|
||||
onPendingDataChange,
|
||||
pendingData,
|
||||
pendingDataKey,
|
||||
setPendingData,
|
||||
setDirtyOverrides,
|
||||
setPendingOverrides,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -344,7 +369,7 @@ export function ConfigSection({
|
||||
return;
|
||||
}
|
||||
const sanitizedData = sanitizeSectionData(data as ConfigSectionData);
|
||||
let nextBaselineFormData = baselineFormData ?? formData;
|
||||
const nextBaselineFormData = baselineSnapshot;
|
||||
const overrides = buildOverrides(
|
||||
sanitizedData,
|
||||
compareBaseData,
|
||||
@ -353,16 +378,6 @@ export function ConfigSection({
|
||||
setPendingOverrides(overrides as JsonValue | undefined);
|
||||
if (isInitializingRef.current && !pendingData) {
|
||||
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) {
|
||||
setPendingData(null);
|
||||
setPendingOverrides(undefined);
|
||||
@ -392,14 +407,12 @@ export function ConfigSection({
|
||||
setPendingData,
|
||||
setPendingOverrides,
|
||||
setDirtyOverrides,
|
||||
baselineFormData,
|
||||
setBaselineFormData,
|
||||
formData,
|
||||
baselineSnapshot,
|
||||
],
|
||||
);
|
||||
|
||||
const currentFormData = pendingData || formData;
|
||||
const effectiveBaselineFormData = baselineFormData ?? formData;
|
||||
const effectiveBaselineFormData = baselineSnapshot;
|
||||
|
||||
const currentOverrides = useMemo(() => {
|
||||
if (!currentFormData || typeof currentFormData !== "object") {
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { Children, useState, useEffect } from "react";
|
||||
import { Children, useState, useEffect, useRef } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@ -142,6 +142,10 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
|
||||
const hasModifiedDescendants = checkSubtreeModified(fieldPath);
|
||||
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
|
||||
useEffect(() => {
|
||||
@ -191,6 +195,14 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
setShowAdvanced(true);
|
||||
}
|
||||
}, [hasModifiedAdvanced]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastResetKeyRef.current !== resetKey) {
|
||||
lastResetKeyRef.current = resetKey;
|
||||
setIsOpen(hasModifiedDescendants);
|
||||
setShowAdvanced(hasModifiedAdvanced);
|
||||
}
|
||||
}, [resetKey, hasModifiedDescendants, hasModifiedAdvanced]);
|
||||
const { children } = props as ObjectFieldTemplateProps & {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user