mask/zone bugfixes

- fix websocket crash when creating a new mask or zone before a name is assigned
- fix deleted masks and zones not disappearing from the list until navigating away
- fix deleting profile override not reverting to the base mask in the list
- fix inertia defaulting to nan
This commit is contained in:
Josh Hawkins 2026-04-02 06:19:42 -05:00
parent a7f70e1334
commit 1365fbe16c
5 changed files with 36 additions and 15 deletions

View File

@ -74,9 +74,11 @@ export default function MotionMaskEditPane({
} }
}, [polygons, activePolygonIndex]); }, [polygons, activePolygonIndex]);
const maskCamera = polygon?.camera || "";
const maskName = polygon?.name || "";
const { send: sendMotionMaskState } = useMotionMaskState( const { send: sendMotionMaskState } = useMotionMaskState(
polygon?.camera || "", maskCamera,
polygon?.name || "", maskName,
); );
const cameraConfig = useMemo(() => { const cameraConfig = useMemo(() => {
@ -250,8 +252,8 @@ export default function MotionMaskEditPane({
}, },
); );
updateConfig(); updateConfig();
// Only publish WS state for base config // Only publish WS state for base config when mask has a name
if (!editingProfile) { if (!editingProfile && maskName) {
sendMotionMaskState(enabled ? "ON" : "OFF"); sendMotionMaskState(enabled ? "ON" : "OFF");
} }
} else { } else {
@ -291,6 +293,7 @@ export default function MotionMaskEditPane({
cameraConfig, cameraConfig,
t, t,
sendMotionMaskState, sendMotionMaskState,
maskName,
editingProfile, editingProfile,
], ],
); );

View File

@ -80,9 +80,10 @@ export default function ObjectMaskEditPane({
} }
}, [polygons, activePolygonIndex]); }, [polygons, activePolygonIndex]);
const maskName = polygon?.name || "";
const { send: sendObjectMaskState } = useObjectMaskState( const { send: sendObjectMaskState } = useObjectMaskState(
polygon?.camera || "", polygon?.camera || "",
polygon?.name || "", maskName,
); );
const cameraConfig = useMemo(() => { const cameraConfig = useMemo(() => {
@ -256,8 +257,8 @@ export default function ObjectMaskEditPane({
}, },
); );
updateConfig(); updateConfig();
// Only publish WS state for base config // Only publish WS state for base config when mask has a name
if (!editingProfile) { if (!editingProfile && maskName) {
sendObjectMaskState(enabled ? "ON" : "OFF"); sendObjectMaskState(enabled ? "ON" : "OFF");
} }
} else { } else {
@ -300,6 +301,7 @@ export default function ObjectMaskEditPane({
cameraConfig, cameraConfig,
t, t,
sendObjectMaskState, sendObjectMaskState,
maskName,
editingProfile, editingProfile,
], ],
); );

View File

@ -51,6 +51,7 @@ type PolygonItemProps = {
setLoadingPolygonIndex: (index: number | undefined) => void; setLoadingPolygonIndex: (index: number | undefined) => void;
editingProfile?: string | null; editingProfile?: string | null;
allProfileNames?: string[]; allProfileNames?: string[];
onDeleted?: () => void;
}; };
export default function PolygonItem({ export default function PolygonItem({
@ -67,6 +68,7 @@ export default function PolygonItem({
setLoadingPolygonIndex, setLoadingPolygonIndex,
editingProfile, editingProfile,
allProfileNames, allProfileNames,
onDeleted,
}: PolygonItemProps) { }: PolygonItemProps) {
const { t } = useTranslation("views/settings"); const { t } = useTranslation("views/settings");
const { data: config, mutate: updateConfig } = const { data: config, mutate: updateConfig } =
@ -169,6 +171,7 @@ export default function PolygonItem({
{ position: "top-center" }, { position: "top-center" },
); );
updateConfig(); updateConfig();
onDeleted?.();
} else { } else {
toast.error( toast.error(
t("toast.save.error.title", { t("toast.save.error.title", {
@ -234,6 +237,7 @@ export default function PolygonItem({
{ position: "top-center" }, { position: "top-center" },
); );
updateConfig(); updateConfig();
onDeleted?.();
} else { } else {
toast.error( toast.error(
t("toast.save.error.title", { t("toast.save.error.title", {
@ -267,6 +271,7 @@ export default function PolygonItem({
index, index,
setLoadingPolygonIndex, setLoadingPolygonIndex,
editingProfile, editingProfile,
onDeleted,
], ],
); );

View File

@ -91,10 +91,8 @@ export default function ZoneEditPane({
} }
}, [polygons, activePolygonIndex]); }, [polygons, activePolygonIndex]);
const { send: sendZoneState } = useZoneState( const zoneName = polygon?.name || "";
polygon?.camera || "", const { send: sendZoneState } = useZoneState(polygon?.camera || "", zoneName);
polygon?.name || "",
);
const cameraConfig = useMemo(() => { const cameraConfig = useMemo(() => {
if (polygon?.camera && config) { if (polygon?.camera && config) {
@ -303,8 +301,8 @@ export default function ZoneEditPane({
resolvedZoneData?.enabled !== undefined resolvedZoneData?.enabled !== undefined
? resolvedZoneData.enabled ? resolvedZoneData.enabled
: (polygon?.enabled ?? true), : (polygon?.enabled ?? true),
inertia: resolvedZoneData?.inertia, inertia: resolvedZoneData?.inertia ?? 3,
loitering_time: resolvedZoneData?.loitering_time, loitering_time: resolvedZoneData?.loitering_time ?? 0,
isFinished: polygon?.isFinished ?? false, isFinished: polygon?.isFinished ?? false,
objects: polygon?.objects ?? [], objects: polygon?.objects ?? [],
speedEstimation: !!(lineA || lineB || lineC || lineD), speedEstimation: !!(lineA || lineB || lineC || lineD),
@ -516,8 +514,8 @@ export default function ZoneEditPane({
}, },
); );
updateConfig(); updateConfig();
// Only publish WS state for base config (not profiles) // Only publish WS state for base config when zone has a name
if (!editingProfile) { if (!editingProfile && zoneName) {
sendZoneState(enabled ? "ON" : "OFF"); sendZoneState(enabled ? "ON" : "OFF");
} }
} else { } else {

View File

@ -201,6 +201,16 @@ export default function MasksAndZonesView({
setUnsavedChanges(false); setUnsavedChanges(false);
}, [editingPolygons, setUnsavedChanges]); }, [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(() => { useEffect(() => {
if (isLoading) { if (isLoading) {
return; return;
@ -843,6 +853,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex} setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile} editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames} allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/> />
))} ))}
</div> </div>
@ -919,6 +930,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex} setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile} editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames} allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/> />
))} ))}
</div> </div>
@ -995,6 +1007,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex} setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile} editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames} allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/> />
))} ))}
</div> </div>