diff --git a/web/src/components/dynamic/CameraFeatureToggle.tsx b/web/src/components/dynamic/CameraFeatureToggle.tsx new file mode 100644 index 000000000..666bfb33b --- /dev/null +++ b/web/src/components/dynamic/CameraFeatureToggle.tsx @@ -0,0 +1,60 @@ +import { IconType } from "react-icons"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { isDesktop } from "react-device-detect"; + +const variants = { + primary: { + active: "font-bold text-primary-foreground bg-primary", + inactive: "text-muted-foreground bg-muted", + }, + secondary: { + active: "font-bold text-primary", + inactive: "text-muted-foreground", + }, +}; + +type CameraFeatureToggleProps = { + className?: string; + variant?: "primary" | "secondary"; + isActive: boolean; + Icon: IconType; + title: string; + onClick?: () => void; +}; + +export default function CameraFeatureToggle({ + className = "", + variant = "primary", + isActive, + Icon, + title, + onClick, +}: CameraFeatureToggleProps) { + const content = ( +
+ +
+ ); + + if (isDesktop) { + return ( + + {content} + +

{title}

+
+
+ ); + } + + return content; +} diff --git a/web/src/views/live/LiveCameraView.tsx b/web/src/views/live/LiveCameraView.tsx index 951e16c76..b83a78935 100644 --- a/web/src/views/live/LiveCameraView.tsx +++ b/web/src/views/live/LiveCameraView.tsx @@ -1,4 +1,11 @@ -import { usePtzCommand } from "@/api/ws"; +import { + useAudioState, + useDetectState, + usePtzCommand, + useRecordingsState, + useSnapshotsState, +} from "@/api/ws"; +import CameraFeatureToggle from "@/components/dynamic/CameraFeatureToggle"; import LivePlayer from "@/components/player/LivePlayer"; import { Button } from "@/components/ui/button"; import { @@ -7,6 +14,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { TooltipProvider } from "@/components/ui/tooltip"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; import { CameraConfig } from "@/types/frigateConfig"; import { CameraPtzInfo } from "@/types/ptz"; @@ -20,7 +28,15 @@ import { FaAngleUp, } from "react-icons/fa"; import { IoMdArrowBack } from "react-icons/io"; -import { MdZoomIn, MdZoomOut } from "react-icons/md"; +import { LuEar, LuEarOff, LuVideo, LuVideoOff } from "react-icons/lu"; +import { + MdNoPhotography, + MdPersonOff, + MdPersonSearch, + MdPhotoCamera, + MdZoomIn, + MdZoomOut, +} from "react-icons/md"; import { useNavigate } from "react-router-dom"; import useSWR from "swr"; @@ -30,6 +46,19 @@ type LiveCameraViewProps = { export default function LiveCameraView({ camera }: LiveCameraViewProps) { const navigate = useNavigate(); + // camera features + + const { payload: detectState, send: sendDetect } = useDetectState( + camera.name, + ); + const { payload: recordState, send: sendRecord } = useRecordingsState( + camera.name, + ); + const { payload: snapshotState, send: sendSnapshot } = useSnapshotsState( + camera.name, + ); + const { payload: audioState, send: sendAudio } = useAudioState(camera.name); + const growClassName = useMemo(() => { if (camera.detect.width / camera.detect.height > 2) { return "absolute left-2 right-2 top-[50%] -translate-y-[50%]"; @@ -45,6 +74,36 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) { Back + +
+ sendDetect(detectState == "ON" ? "OFF" : "ON")} + /> + sendRecord(recordState == "ON" ? "OFF" : "ON")} + /> + sendSnapshot(snapshotState == "ON" ? "OFF" : "ON")} + /> + {camera.audio.enabled_in_config && ( + sendAudio(audioState == "ON" ? "OFF" : "ON")} + /> + )} +
+
@@ -70,7 +129,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) { function PtzControlPanel({ camera }: { camera: string }) { const { data: ptz } = useSWR(`${camera}/ptz/info`); - const { payload: _, send: sendPtz } = usePtzCommand(camera); + const { send: sendPtz } = usePtzCommand(camera); const onStop = useCallback( (e: React.SyntheticEvent) => {