From 8fd9726220df44d18fddf83322496473485af8f8 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 16 May 2026 16:24:29 -0500 Subject: [PATCH] Sync state back to snapshot when child form un-modifies and remount on undo --- .../DetectorsAndModelSettingsView.tsx | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/web/src/views/settings/DetectorsAndModelSettingsView.tsx b/web/src/views/settings/DetectorsAndModelSettingsView.tsx index e8ba9d869d..ee024ca5ce 100644 --- a/web/src/views/settings/DetectorsAndModelSettingsView.tsx +++ b/web/src/views/settings/DetectorsAndModelSettingsView.tsx @@ -120,6 +120,7 @@ export default function DetectorsAndModelSettingsView({ const [snapshot, setSnapshot] = useState(null); const [state, setState] = useState(null); const [isSaving, setIsSaving] = useState(false); + const [resetKey, setResetKey] = useState(0); const [restartDialogOpen, setRestartDialogOpen] = useState(false); const { send: sendRestart } = useRestart(); const [childPending, setChildPending] = useState< @@ -225,21 +226,28 @@ export default function DetectorsAndModelSettingsView({ useEffect(() => { const detectorsPending = childPending["detectors"]; - if (detectorsPending) { - setState((prev) => - prev ? { ...prev, detectors: detectorsPending } : prev, - ); - } + setState((prev) => { + if (!prev || !snapshot) return prev; + // When the embedded form un-modifies (data returns to baseline) it clears + // its entry from childPending — fall back to snapshot so state.detectors + // doesn't keep a stale value the user has visually reverted. + return { + ...prev, + detectors: detectorsPending ?? snapshot.detectors, + }; + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [childPending["detectors"]]); useEffect(() => { const modelPending = childPending["model"]; - if (modelPending) { - setState((prev) => - prev ? { ...prev, customModel: modelPending } : prev, - ); - } + setState((prev) => { + if (!prev || !snapshot) return prev; + return { + ...prev, + customModel: modelPending ?? snapshot.customModel, + }; + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [childPending["model"]]); @@ -308,6 +316,7 @@ export default function DetectorsAndModelSettingsView({ // Re-derive snapshot from the freshly saved state so isDirty resets. setSnapshot({ ...state }); setChildPending({}); + setResetKey((k) => k + 1); if (detectorChanged) { toast.success(t("detectorsAndModel.toast.saveSuccessRestart"), { @@ -344,6 +353,10 @@ export default function DetectorsAndModelSettingsView({ if (snapshot) { setState(snapshot); setChildPending({}); + // Force the embedded forms to re-mount so their internal dirty/baseline + // state is rebuilt from the current config — clearing childPending alone + // doesn't reset BaseSection's internal tracking. + setResetKey((k) => k + 1); } }, [snapshot]); @@ -393,6 +406,7 @@ export default function DetectorsAndModelSettingsView({
) : (