From 8e24bd79dfce50c45e9f60851022480ce0b3be41 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 18 Apr 2024 08:07:13 -0500 Subject: [PATCH] tweaks --- web/src/api/ws.tsx | 4 +- web/src/components/settings/MasksAndZones.tsx | 105 +++++++++--------- web/src/components/settings/MotionTuner.tsx | 93 +++------------- web/src/components/settings/PolygonDrawer.tsx | 1 + .../settings/PolygonEditControls.tsx | 12 +- web/src/pages/Settings.tsx | 49 +++++++- 6 files changed, 123 insertions(+), 141 deletions(-) diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx index ae885d10a..2439dbb33 100644 --- a/web/src/api/ws.tsx +++ b/web/src/api/ws.tsx @@ -236,7 +236,7 @@ export function useMotionContourArea(camera: string): { } export function useImproveContrast(camera: string): { - payload: string; + payload: ToggleableSetting; send: (payload: string, retain?: boolean) => void; } { const { @@ -246,5 +246,5 @@ export function useImproveContrast(camera: string): { `${camera}/improve_contrast/state`, `${camera}/improve_contrast/set`, ); - return { payload: payload as string, send }; + return { payload: payload as ToggleableSetting, send }; } diff --git a/web/src/components/settings/MasksAndZones.tsx b/web/src/components/settings/MasksAndZones.tsx index 6e744ca38..955f9e625 100644 --- a/web/src/components/settings/MasksAndZones.tsx +++ b/web/src/components/settings/MasksAndZones.tsx @@ -24,6 +24,7 @@ import MotionMaskEditPane from "./MotionMaskEditPane"; import ObjectMaskEditPane from "./ObjectMaskEditPane"; import PolygonItem from "./PolygonItem"; import { Link } from "react-router-dom"; +import { isDesktop } from "react-device-detect"; type MasksAndZoneProps = { selectedCamera: string; @@ -87,8 +88,8 @@ export default function MasksAndZones({ }, [config, selectedCamera]); const stretch = true; - // TODO: mobile / portrait cams - const fitAspect = 16 / 9; + // may need tweaking for mobile + const fitAspect = isDesktop ? 16 / 9 : 3 / 4; const scaledHeight = useMemo(() => { if (containerRef.current && aspectRatio && detectHeight) { @@ -97,7 +98,9 @@ export default function MasksAndZones({ ? Math.floor( Math.min(containerHeight, containerRef.current?.clientHeight), ) - : Math.floor(containerWidth / aspectRatio); + : isDesktop || aspectRatio > fitAspect + ? Math.floor(containerWidth / aspectRatio) + : Math.floor(containerWidth / aspectRatio) / 1.5; const finalHeight = stretch ? scaledHeight : Math.min(scaledHeight, detectHeight); @@ -157,12 +160,14 @@ export default function MasksAndZones({ setEditingPolygons([...allPolygons]); setActivePolygonIndex(undefined); setHoveredPolygonIndex(null); - }, [allPolygons]); + setUnsavedChanges(false); + }, [allPolygons, setUnsavedChanges]); const handleSave = useCallback(() => { setAllPolygons([...(editingPolygons ?? [])]); setHoveredPolygonIndex(null); - }, [editingPolygons]); + setUnsavedChanges(false); + }, [editingPolygons, setUnsavedChanges]); useEffect(() => { if (isLoading) { @@ -218,62 +223,52 @@ export default function MasksAndZones({ let globalObjectMasks: Polygon[] = []; let objectMasks: Polygon[] = []; - if ( - cameraConfig.motion.mask !== null && - cameraConfig.motion.mask !== undefined - ) { - // this can be an array or a string - motionMasks = ( - Array.isArray(cameraConfig.motion.mask) - ? cameraConfig.motion.mask - : cameraConfig.motion.mask - ? [cameraConfig.motion.mask] - : [] - ).map((maskData, index) => ({ - type: "motion_mask" as PolygonType, - typeIndex: index, - camera: cameraConfig.name, - name: `Motion Mask ${index + 1}`, - objects: [], - points: interpolatePoints( - parseCoordinates(maskData), - 1, - 1, - scaledWidth, - scaledHeight, - ), - isFinished: true, - color: [0, 0, 255], - })); - } + // this can be an array or a string + motionMasks = ( + Array.isArray(cameraConfig.motion.mask) + ? cameraConfig.motion.mask + : cameraConfig.motion.mask + ? [cameraConfig.motion.mask] + : [] + ).map((maskData, index) => ({ + type: "motion_mask" as PolygonType, + typeIndex: index, + camera: cameraConfig.name, + name: `Motion Mask ${index + 1}`, + objects: [], + points: interpolatePoints( + parseCoordinates(maskData), + 1, + 1, + scaledWidth, + scaledHeight, + ), + isFinished: true, + color: [0, 0, 255], + })); const globalObjectMasksArray = Array.isArray(cameraConfig.objects.mask) ? cameraConfig.objects.mask : cameraConfig.objects.mask ? [cameraConfig.objects.mask] : []; - // TODO: check to see if this is necessary - if ( - cameraConfig.objects.mask !== null && - cameraConfig.objects.mask !== undefined - ) { - globalObjectMasks = globalObjectMasksArray.map((maskData, index) => ({ - type: "object_mask" as PolygonType, - typeIndex: index, - camera: cameraConfig.name, - name: `Object Mask ${index + 1} (all objects)`, - objects: [], - points: interpolatePoints( - parseCoordinates(maskData), - 1, - 1, - scaledWidth, - scaledHeight, - ), - isFinished: true, - color: [128, 128, 128], - })); - } + + globalObjectMasks = globalObjectMasksArray.map((maskData, index) => ({ + type: "object_mask" as PolygonType, + typeIndex: index, + camera: cameraConfig.name, + name: `Object Mask ${index + 1} (all objects)`, + objects: [], + points: interpolatePoints( + parseCoordinates(maskData), + 1, + 1, + scaledWidth, + scaledHeight, + ), + isFinished: true, + color: [128, 128, 128], + })); const globalObjectMasksCount = globalObjectMasks.length; let index = 0; diff --git a/web/src/components/settings/MotionTuner.tsx b/web/src/components/settings/MotionTuner.tsx index 52984a80d..a63651ee4 100644 --- a/web/src/components/settings/MotionTuner.tsx +++ b/web/src/components/settings/MotionTuner.tsx @@ -1,14 +1,4 @@ import Heading from "@/components/ui/heading"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "../ui/alert-dialog"; import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; import axios from "axios"; @@ -50,7 +40,6 @@ export default function MotionTuner({ useSWR("config"); const [changedValue, setChangedValue] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); const { send: sendMotionThreshold } = useMotionThreshold(selectedCamera); const { send: sendMotionContourArea } = useMotionContourArea(selectedCamera); @@ -87,43 +76,31 @@ export default function MotionTuner({ improve_contrast: cameraConfig.motion.improve_contrast, }); } - }, [cameraConfig]); + // we know that these deps are correct + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { - if (cameraConfig) { - const { threshold, contour_area, improve_contrast } = motionSettings; + if (!motionSettings.threshold) return; - if ( - threshold !== undefined && - cameraConfig.motion.threshold !== threshold - ) { - sendMotionThreshold(threshold); - } + sendMotionThreshold(motionSettings.threshold); + }, [motionSettings.threshold, sendMotionThreshold]); - if ( - contour_area !== undefined && - cameraConfig.motion.contour_area !== contour_area - ) { - sendMotionContourArea(contour_area); - } + useEffect(() => { + if (!motionSettings.contour_area) return; - if ( - improve_contrast !== undefined && - cameraConfig.motion.improve_contrast !== improve_contrast - ) { - sendImproveContrast(improve_contrast ? "ON" : "OFF"); - } - } - }, [ - cameraConfig, - motionSettings, - sendMotionThreshold, - sendMotionContourArea, - sendImproveContrast, - ]); + sendMotionContourArea(motionSettings.contour_area); + }, [motionSettings.contour_area, sendMotionContourArea]); + + useEffect(() => { + if (motionSettings.improve_contrast === undefined) return; + + sendImproveContrast(motionSettings.improve_contrast ? "ON" : "OFF"); + }, [motionSettings.improve_contrast, sendImproveContrast]); const handleMotionConfigChange = (newConfig: Partial) => { setMotionSettings((prevConfig) => ({ ...prevConfig, ...newConfig })); + setUnsavedChanges(true); setChangedValue(true); }; @@ -170,17 +147,6 @@ export default function MotionTuner({ setChangedValue(false); }, [origMotionSettings]); - const handleDialog = useCallback( - (save: boolean) => { - if (save) { - saveToConfig(); - } - setConfirmationDialogOpen(false); - setChangedValue(false); - }, - [saveToConfig], - ); - if (!cameraConfig && !selectedCamera) { return ; } @@ -315,31 +281,6 @@ export default function MotionTuner({ - {confirmationDialogOpen && ( - setConfirmationDialogOpen(false)} - > - - - - You have unsaved changes on this camera. - - - Do you want to save your changes before continuing? - - - - handleDialog(false)}> - Cancel - - handleDialog(true)}> - Save - - - - - )} {cameraConfig ? ( diff --git a/web/src/components/settings/PolygonDrawer.tsx b/web/src/components/settings/PolygonDrawer.tsx index 48a3516bc..861bae1d9 100644 --- a/web/src/components/settings/PolygonDrawer.tsx +++ b/web/src/components/settings/PolygonDrawer.tsx @@ -97,6 +97,7 @@ export default function PolygonDrawer({ onMouseOver={isActive ? handleGroupMouseOver : undefined} onTouchStart={isActive ? handleGroupMouseOver : undefined} onMouseOut={isActive ? handleGroupMouseOut : undefined} + // TODO: don't use zindex zIndex={isActive ? 999 : 100} > Undo @@ -66,12 +66,12 @@ export default function PolygonEditControls({ Reset diff --git a/web/src/pages/Settings.tsx b/web/src/pages/Settings.tsx index 4abb9db68..a5243c048 100644 --- a/web/src/pages/Settings.tsx +++ b/web/src/pages/Settings.tsx @@ -5,12 +5,22 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import MotionTuner from "@/components/settings/MotionTuner"; import MasksAndZones from "@/components/settings/MasksAndZones"; import { Button } from "@/components/ui/button"; -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import useOptimisticState from "@/hooks/use-optimistic-state"; import { isMobile } from "react-device-detect"; import { FaVideo } from "react-icons/fa"; @@ -38,6 +48,7 @@ export default function Settings() { // TODO: confirm leave page const [unsavedChanges, setUnsavedChanges] = useState(false); + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); const cameras = useMemo(() => { if (!config) { @@ -53,6 +64,17 @@ export default function Settings() { const [filterZoneMask, setFilterZoneMask] = useState(); + const handleDialog = useCallback( + (save: boolean) => { + if (unsavedChanges && save) { + // TODO + } + setConfirmationDialogOpen(false); + setUnsavedChanges(false); + }, + [unsavedChanges], + ); + useEffect(() => { if (cameras.length) { setSelectedCamera(cameras[0].name); @@ -123,6 +145,29 @@ export default function Settings() { /> )} + {confirmationDialogOpen && ( + setConfirmationDialogOpen(false)} + > + + + You have unsaved changes. + + Do you want to save your changes before continuing? + + + + handleDialog(false)}> + Cancel + + handleDialog(true)}> + Save + + + + + )} ); } @@ -165,7 +210,7 @@ function CameraSelectButton({ )} -
+
{allCameras.map((item) => (