Mask/zone editor fixes (#22732)

* add guards to reject missing sub commands

* 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

* disable save button on invalid form state

* fix validation for speed estimation

* ensure polygon is closed before allowing save

* require all masks and zones to be on the base config

* clarify dialog message and tooltip when removing an override

* clarify docs
This commit is contained in:
Josh Hawkins
2026-04-02 08:15:51 -06:00
committed by GitHub
parent adc8c2a6e8
commit 5059311c9d
9 changed files with 235 additions and 86 deletions
@@ -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(() => {
@@ -154,7 +156,7 @@ export default function MotionMaskEditPane({
message: t("masksAndZones.form.name.error.mustNotBeEmpty"),
}),
enabled: z.boolean(),
isFinished: z.boolean().refine(() => polygon?.isFinished === true, {
isFinished: z.boolean().refine((val) => val === true, {
message: t("masksAndZones.form.polygonDrawing.error.mustBeFinished"),
}),
});
@@ -170,6 +172,12 @@ export default function MotionMaskEditPane({
},
});
useEffect(() => {
if (polygon?.isFinished !== undefined) {
form.setValue("isFinished", polygon.isFinished, { shouldValidate: true });
}
}, [polygon?.isFinished, form]);
const saveToConfig = useCallback(
async ({
name: maskId,
@@ -250,8 +258,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 +299,7 @@ export default function MotionMaskEditPane({
cameraConfig,
t,
sendMotionMaskState,
maskName,
editingProfile,
],
);
@@ -449,7 +458,7 @@ export default function MotionMaskEditPane({
<Button
variant="select"
aria-label={t("button.save", { ns: "common" })}
disabled={isLoading}
disabled={isLoading || !form.formState.isValid}
className="flex flex-1"
type="submit"
>