import { ReactNode, useCallback, useEffect, useMemo, useRef, useState, } from "react"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, } from "@/components/ui/context-menu"; import { MdVolumeDown, MdVolumeMute, MdVolumeOff, MdVolumeUp, } from "react-icons/md"; import { Dialog } from "@/components/ui/dialog"; import { VolumeSlider } from "@/components/ui/slider"; import { CameraStreamingDialog } from "../settings/CameraStreamingDialog"; import { AllGroupsStreamingSettings, GroupStreamingSettings, } from "@/types/frigateConfig"; import { useStreamingSettings } from "@/context/streaming-settings-provider"; import { IoIosWarning } from "react-icons/io"; import { cn } from "@/lib/utils"; import { useNavigate } from "react-router-dom"; type LiveContextMenuProps = { className?: string; camera: string; streamName: string; cameraGroup?: string; preferredLiveMode: string; isRestreamed: boolean; supportsAudio: boolean; audioState: boolean; toggleAudio: () => void; volumeState?: number; setVolumeState: (volumeState: number) => void; muteAll: () => void; unmuteAll: () => void; statsState: boolean; toggleStats: () => void; resetPreferredLiveMode: () => void; children?: ReactNode; }; export default function LiveContextMenu({ className, camera, streamName, cameraGroup, preferredLiveMode, isRestreamed, supportsAudio, audioState, toggleAudio, volumeState, setVolumeState, muteAll, unmuteAll, statsState, toggleStats, resetPreferredLiveMode, children, }: LiveContextMenuProps) { const [showSettings, setShowSettings] = useState(false); // streaming settings const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } = useStreamingSettings(); const [groupStreamingSettings, setGroupStreamingSettings] = useState( allGroupsStreamingSettings[cameraGroup ?? ""], ); useEffect(() => { if (cameraGroup && cameraGroup != "default") { setGroupStreamingSettings(allGroupsStreamingSettings[cameraGroup]); } // set individual group when all groups changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [allGroupsStreamingSettings]); const onSave = useCallback( (settings: GroupStreamingSettings) => { if ( !cameraGroup || !allGroupsStreamingSettings || cameraGroup == "default" || !settings ) { return; } const updatedSettings: AllGroupsStreamingSettings = { ...Object.fromEntries( Object.entries(allGroupsStreamingSettings || {}).filter( ([key]) => key !== cameraGroup, ), ), [cameraGroup]: { ...Object.fromEntries( Object.entries(settings).map(([cameraName, cameraSettings]) => [ cameraName, cameraName === camera ? { ...cameraSettings, playAudio: audioState ?? cameraSettings.playAudio ?? false, volume: volumeState ?? cameraSettings.volume ?? 1, } : cameraSettings, ]), ), // Add the current camera if it doesn't exist ...(!settings[camera] ? { [camera]: { streamName: streamName, streamType: "smart", compatibilityMode: false, playAudio: audioState, volume: volumeState ?? 1, }, } : {}), }, }; setAllGroupsStreamingSettings?.(updatedSettings); }, [ camera, streamName, cameraGroup, allGroupsStreamingSettings, setAllGroupsStreamingSettings, audioState, volumeState, ], ); // ui const audioControlsUsed = useRef(false); const VolumeIcon = useMemo(() => { if (!volumeState || volumeState == 0.0 || !audioState) { return MdVolumeOff; } else if (volumeState <= 0.33) { return MdVolumeMute; } else if (volumeState <= 0.67) { return MdVolumeDown; } else { return MdVolumeUp; } // only update when specific fields change // eslint-disable-next-line react-hooks/exhaustive-deps }, [volumeState, audioState]); const handleVolumeIconClick = (e: React.MouseEvent) => { e.stopPropagation(); audioControlsUsed.current = true; toggleAudio(); }; const handleVolumeChange = (value: number[]) => { audioControlsUsed.current = true; setVolumeState(value[0]); }; const handleOpenChange = (open: boolean) => { if (!open && audioControlsUsed.current) { onSave(groupStreamingSettings); audioControlsUsed.current = false; } }; // navigate for debug view const navigate = useNavigate(); return (
{children}
{camera.replaceAll("_", " ")}
{preferredLiveMode == "jsmpeg" && isRestreamed && (

Low-bandwidth mode

)}
{preferredLiveMode != "jsmpeg" && isRestreamed && supportsAudio && ( <>

Audio

)}
Mute All Cameras
Unmute All Cameras
{statsState ? "Hide" : "Show"} Stream Stats
navigate(`/settings?page=debug&camera=${camera}`)} >
Debug View
{cameraGroup && cameraGroup !== "default" && ( <>
setShowSettings(true)} >
Streaming Settings
)} {preferredLiveMode == "jsmpeg" && isRestreamed && ( <>
Reset
)}
); }