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
+64 -36
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;
@@ -806,20 +816,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.zones.add")}
onClick={() => {
setEditPane("zone");
handleNewPolygon("zone");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.zones.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("zone");
handleNewPolygon("zone");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.zones.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.zones.add")}
</TooltipContent>
</Tooltip>
</div>
@@ -843,6 +858,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/>
))}
</div>
@@ -880,20 +896,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.motionMasks.add")}
onClick={() => {
setEditPane("motion_mask");
handleNewPolygon("motion_mask");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.motionMasks.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("motion_mask");
handleNewPolygon("motion_mask");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.motionMasks.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.motionMasks.add")}
</TooltipContent>
</Tooltip>
</div>
@@ -919,6 +940,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/>
))}
</div>
@@ -956,20 +978,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.objectMasks.add")}
onClick={() => {
setEditPane("object_mask");
handleNewPolygon("object_mask");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.objectMasks.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("object_mask");
handleNewPolygon("object_mask");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.objectMasks.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.objectMasks.add")}
</TooltipContent>
</Tooltip>
</div>
@@ -995,6 +1022,7 @@ export default function MasksAndZonesView({
setLoadingPolygonIndex={setLoadingPolygonIndex}
editingProfile={currentEditingProfile}
allProfileNames={profileState?.allProfileNames}
onDeleted={handlePolygonDeleted}
/>
))}
</div>