diff --git a/web/src/components/settings/MotionMaskEditPane.tsx b/web/src/components/settings/MotionMaskEditPane.tsx index 1f4aaf76fd..8be32eef8c 100644 --- a/web/src/components/settings/MotionMaskEditPane.tsx +++ b/web/src/components/settings/MotionMaskEditPane.tsx @@ -74,9 +74,11 @@ export default function MotionMaskEditPane({ } }, [polygons, activePolygonIndex]); + const maskCamera = polygon?.camera || ""; + const maskName = polygon?.name || ""; const { send: sendMotionMaskState } = useMotionMaskState( - polygon?.camera || "", - polygon?.name || "", + maskCamera, + maskName, ); const cameraConfig = useMemo(() => { @@ -250,8 +252,8 @@ export default function MotionMaskEditPane({ }, ); updateConfig(); - // Only publish WS state for base config - if (!editingProfile) { + // Only publish WS state for base config when mask has a name + if (!editingProfile && maskName) { sendMotionMaskState(enabled ? "ON" : "OFF"); } } else { @@ -291,6 +293,7 @@ export default function MotionMaskEditPane({ cameraConfig, t, sendMotionMaskState, + maskName, editingProfile, ], ); diff --git a/web/src/components/settings/ObjectMaskEditPane.tsx b/web/src/components/settings/ObjectMaskEditPane.tsx index a2c174de52..0ec44b27a4 100644 --- a/web/src/components/settings/ObjectMaskEditPane.tsx +++ b/web/src/components/settings/ObjectMaskEditPane.tsx @@ -80,9 +80,10 @@ export default function ObjectMaskEditPane({ } }, [polygons, activePolygonIndex]); + const maskName = polygon?.name || ""; const { send: sendObjectMaskState } = useObjectMaskState( polygon?.camera || "", - polygon?.name || "", + maskName, ); const cameraConfig = useMemo(() => { @@ -256,8 +257,8 @@ export default function ObjectMaskEditPane({ }, ); updateConfig(); - // Only publish WS state for base config - if (!editingProfile) { + // Only publish WS state for base config when mask has a name + if (!editingProfile && maskName) { sendObjectMaskState(enabled ? "ON" : "OFF"); } } else { @@ -300,6 +301,7 @@ export default function ObjectMaskEditPane({ cameraConfig, t, sendObjectMaskState, + maskName, editingProfile, ], ); diff --git a/web/src/components/settings/PolygonItem.tsx b/web/src/components/settings/PolygonItem.tsx index c3f1b7742b..04030656d8 100644 --- a/web/src/components/settings/PolygonItem.tsx +++ b/web/src/components/settings/PolygonItem.tsx @@ -51,6 +51,7 @@ type PolygonItemProps = { setLoadingPolygonIndex: (index: number | undefined) => void; editingProfile?: string | null; allProfileNames?: string[]; + onDeleted?: () => void; }; export default function PolygonItem({ @@ -67,6 +68,7 @@ export default function PolygonItem({ setLoadingPolygonIndex, editingProfile, allProfileNames, + onDeleted, }: PolygonItemProps) { const { t } = useTranslation("views/settings"); const { data: config, mutate: updateConfig } = @@ -169,6 +171,7 @@ export default function PolygonItem({ { position: "top-center" }, ); updateConfig(); + onDeleted?.(); } else { toast.error( t("toast.save.error.title", { @@ -234,6 +237,7 @@ export default function PolygonItem({ { position: "top-center" }, ); updateConfig(); + onDeleted?.(); } else { toast.error( t("toast.save.error.title", { @@ -267,6 +271,7 @@ export default function PolygonItem({ index, setLoadingPolygonIndex, editingProfile, + onDeleted, ], ); diff --git a/web/src/components/settings/ZoneEditPane.tsx b/web/src/components/settings/ZoneEditPane.tsx index e4131e7634..454a726f54 100644 --- a/web/src/components/settings/ZoneEditPane.tsx +++ b/web/src/components/settings/ZoneEditPane.tsx @@ -91,10 +91,8 @@ export default function ZoneEditPane({ } }, [polygons, activePolygonIndex]); - const { send: sendZoneState } = useZoneState( - polygon?.camera || "", - polygon?.name || "", - ); + const zoneName = polygon?.name || ""; + const { send: sendZoneState } = useZoneState(polygon?.camera || "", zoneName); const cameraConfig = useMemo(() => { if (polygon?.camera && config) { @@ -303,8 +301,8 @@ export default function ZoneEditPane({ resolvedZoneData?.enabled !== undefined ? resolvedZoneData.enabled : (polygon?.enabled ?? true), - inertia: resolvedZoneData?.inertia, - loitering_time: resolvedZoneData?.loitering_time, + inertia: resolvedZoneData?.inertia ?? 3, + loitering_time: resolvedZoneData?.loitering_time ?? 0, isFinished: polygon?.isFinished ?? false, objects: polygon?.objects ?? [], speedEstimation: !!(lineA || lineB || lineC || lineD), @@ -516,8 +514,8 @@ export default function ZoneEditPane({ }, ); updateConfig(); - // Only publish WS state for base config (not profiles) - if (!editingProfile) { + // Only publish WS state for base config when zone has a name + if (!editingProfile && zoneName) { sendZoneState(enabled ? "ON" : "OFF"); } } else { diff --git a/web/src/views/settings/MasksAndZonesView.tsx b/web/src/views/settings/MasksAndZonesView.tsx index 6bb2fde9a6..cab607c474 100644 --- a/web/src/views/settings/MasksAndZonesView.tsx +++ b/web/src/views/settings/MasksAndZonesView.tsx @@ -201,6 +201,16 @@ export default function MasksAndZonesView({ setUnsavedChanges(false); }, [editingPolygons, setUnsavedChanges]); + const handlePolygonDeleted = useCallback(() => { + // Temporarily clear the edit pane guard so the useEffect that + // rebuilds editingPolygons from config will run when the fresh + // config arrives via updateConfig(). This handles all cases: + // base deletes, profile override deletes (which revert to base), + // and profile-only deletes. + setEditPane(undefined); + setActivePolygonIndex(undefined); + }, [setEditPane, setActivePolygonIndex]); + useEffect(() => { if (isLoading) { return; @@ -843,6 +853,7 @@ export default function MasksAndZonesView({ setLoadingPolygonIndex={setLoadingPolygonIndex} editingProfile={currentEditingProfile} allProfileNames={profileState?.allProfileNames} + onDeleted={handlePolygonDeleted} /> ))} @@ -919,6 +930,7 @@ export default function MasksAndZonesView({ setLoadingPolygonIndex={setLoadingPolygonIndex} editingProfile={currentEditingProfile} allProfileNames={profileState?.allProfileNames} + onDeleted={handlePolygonDeleted} /> ))} @@ -995,6 +1007,7 @@ export default function MasksAndZonesView({ setLoadingPolygonIndex={setLoadingPolygonIndex} editingProfile={currentEditingProfile} allProfileNames={profileState?.allProfileNames} + onDeleted={handlePolygonDeleted} /> ))}