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]);
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,
],
);

View File

@ -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,
],
);

View File

@ -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,
],
);

View File

@ -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 {

View File

@ -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}
/>
))}
</div>
@ -919,6 +930,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/>
))}
</div>
@ -995,6 +1007,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/>
))}
</div>