mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-08 20:25:26 +03:00
Add camera feature buttons
This commit is contained in:
parent
027337a865
commit
b9d4a3d51a
60
web/src/components/dynamic/CameraFeatureToggle.tsx
Normal file
60
web/src/components/dynamic/CameraFeatureToggle.tsx
Normal file
@ -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 = (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={`${className} flex flex-col justify-center items-center rounded-lg ${
|
||||
variants[variant][isActive ? "active" : "inactive"]
|
||||
}`}
|
||||
>
|
||||
<Icon className="size-5 md:m-[6px]" />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isDesktop) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>{content}</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<p>{title}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
@ -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) {
|
||||
<IoMdArrowBack className="size-5 mr-[10px]" />
|
||||
Back
|
||||
</Button>
|
||||
<TooltipProvider>
|
||||
<div className="flex items-center gap-1 mr-1 *:rounded-lg">
|
||||
<CameraFeatureToggle
|
||||
Icon={detectState == "ON" ? MdPersonSearch : MdPersonOff}
|
||||
isActive={detectState == "ON"}
|
||||
title={`${detectState == "ON" ? "Disable" : "Enable"} Detect`}
|
||||
onClick={() => sendDetect(detectState == "ON" ? "OFF" : "ON")}
|
||||
/>
|
||||
<CameraFeatureToggle
|
||||
Icon={recordState == "ON" ? LuVideo : LuVideoOff}
|
||||
isActive={recordState == "ON"}
|
||||
title={`${recordState == "ON" ? "Disable" : "Enable"} Recording`}
|
||||
onClick={() => sendRecord(recordState == "ON" ? "OFF" : "ON")}
|
||||
/>
|
||||
<CameraFeatureToggle
|
||||
Icon={snapshotState == "ON" ? MdPhotoCamera : MdNoPhotography}
|
||||
isActive={snapshotState == "ON"}
|
||||
title={`${snapshotState == "ON" ? "Disable" : "Enable"} Snapshots`}
|
||||
onClick={() => sendSnapshot(snapshotState == "ON" ? "OFF" : "ON")}
|
||||
/>
|
||||
{camera.audio.enabled_in_config && (
|
||||
<CameraFeatureToggle
|
||||
Icon={audioState == "ON" ? LuEar : LuEarOff}
|
||||
isActive={audioState == "ON"}
|
||||
title={`${audioState == "ON" ? "Disable" : "Enable"} Audio Detect`}
|
||||
onClick={() => sendAudio(audioState == "ON" ? "OFF" : "ON")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
<div className="relative size-full">
|
||||
@ -70,7 +129,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
||||
function PtzControlPanel({ camera }: { camera: string }) {
|
||||
const { data: ptz } = useSWR<CameraPtzInfo>(`${camera}/ptz/info`);
|
||||
|
||||
const { payload: _, send: sendPtz } = usePtzCommand(camera);
|
||||
const { send: sendPtz } = usePtzCommand(camera);
|
||||
|
||||
const onStop = useCallback(
|
||||
(e: React.SyntheticEvent) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user