diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 85d1991b1..cec8165af 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -39,7 +39,7 @@ type HlsVideoPlayerProps = { onPlaying?: () => void; setFullResolution?: React.Dispatch>; onUploadFrame?: (playTime: number) => Promise | undefined; - setFullscreen?: (full: boolean) => void; + toggleFullscreen?: () => void; }; export default function HlsVideoPlayer({ videoRef, @@ -53,7 +53,7 @@ export default function HlsVideoPlayer({ onPlaying, setFullResolution, onUploadFrame, - setFullscreen, + toggleFullscreen, }: HlsVideoPlayerProps) { const { data: config } = useSWR("config"); @@ -224,7 +224,7 @@ export default function HlsVideoPlayer({ } }} fullscreen={fullscreen} - setFullscreen={setFullscreen} + toggleFullscreen={toggleFullscreen} /> void; onSetPlaybackRate: (rate: number) => void; onUploadFrame?: () => void; - setFullscreen?: (full: boolean) => void; + toggleFullscreen?: () => void; }; export default function VideoControls({ className, @@ -88,7 +88,7 @@ export default function VideoControls({ onSeek, onSetPlaybackRate, onUploadFrame, - setFullscreen, + toggleFullscreen, }: VideoControlsProps) { // layout @@ -160,8 +160,8 @@ export default function VideoControls({ } break; case "f": - if (setFullscreen && down && !repeat) { - setFullscreen(!fullscreen); + if (toggleFullscreen && down && !repeat) { + toggleFullscreen(); } break; case "m": @@ -178,7 +178,7 @@ export default function VideoControls({ }, // only update when preview only changes // eslint-disable-next-line react-hooks/exhaustive-deps - [video, isPlaying, fullscreen, setFullscreen, onSeek], + [video, isPlaying, fullscreen, toggleFullscreen, onSeek], ); useKeyboardListener( hotKeys @@ -287,11 +287,8 @@ export default function VideoControls({ onUploadFrame={onUploadFrame} /> )} - {features.fullscreen && setFullscreen && ( -
setFullscreen(!fullscreen)} - > + {features.fullscreen && toggleFullscreen && ( +
{fullscreen ? : }
)} diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index 7241d475a..62f8a75d7 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -29,7 +29,7 @@ type DynamicVideoPlayerProps = { onTimestampUpdate?: (timestamp: number) => void; onClipEnded?: () => void; setFullResolution: React.Dispatch>; - setFullscreen: (full: boolean) => void; + toggleFullscreen: () => void; }; export default function DynamicVideoPlayer({ className, @@ -44,7 +44,7 @@ export default function DynamicVideoPlayer({ onTimestampUpdate, onClipEnded, setFullResolution, - setFullscreen, + toggleFullscreen, }: DynamicVideoPlayerProps) { const apiHost = useApiHost(); const { data: config } = useSWR("config"); @@ -207,7 +207,7 @@ export default function DynamicVideoPlayer({ }} setFullResolution={setFullResolution} onUploadFrame={onUploadFrameToPlus} - setFullscreen={setFullscreen} + toggleFullscreen={toggleFullscreen} /> (null); + + const { fullscreen, toggleFullscreen } = useFullscreen(mainRef); + // document title useEffect(() => { @@ -78,21 +85,31 @@ function Live() { [cameras, selectedCameraName], ); - if (selectedCameraName == "birdseye") { - return ; - } - - if (selectedCamera) { - return ; - } - return ( - +
+ {selectedCameraName === "birdseye" ? ( + + ) : selectedCamera ? ( + + ) : ( + + )} +
); } diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 2f3efa715..c4bd1857f 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -45,6 +45,7 @@ import { VideoResolutionType } from "@/types/live"; import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record"; import { useResizeObserver } from "@/hooks/resize-observer"; import { cn } from "@/lib/utils"; +import { useFullscreen } from "@/hooks/use-fullscreen"; const SEGMENT_DURATION = 30; @@ -227,32 +228,7 @@ export function RecordingView({ // fullscreen - const [fullscreen, setFullscreen] = useState(false); - - const onToggleFullscreen = useCallback( - (full: boolean) => { - if (full) { - mainLayoutRef.current?.requestFullscreen(); - } else { - document.exitFullscreen(); - } - }, - [mainLayoutRef], - ); - - useEffect(() => { - if (mainLayoutRef.current == null) { - return; - } - const fsListener = () => { - setFullscreen(document.fullscreenElement != null); - }; - document.addEventListener("fullscreenchange", fsListener); - - return () => { - document.removeEventListener("fullscreenchange", fsListener); - }; - }, [mainLayoutRef]); + const { fullscreen, toggleFullscreen } = useFullscreen(mainLayoutRef); // layout @@ -535,7 +511,7 @@ export function RecordingView({ }} isScrubbing={scrubbing || exportMode == "timeline"} setFullResolution={setFullResolution} - setFullscreen={onToggleFullscreen} + toggleFullscreen={toggleFullscreen} />
{isDesktop && ( diff --git a/web/src/views/live/DraggableGridLayout.tsx b/web/src/views/live/DraggableGridLayout.tsx index bfc644159..29130013a 100644 --- a/web/src/views/live/DraggableGridLayout.tsx +++ b/web/src/views/live/DraggableGridLayout.tsx @@ -40,8 +40,6 @@ import { TooltipTrigger, TooltipContent, } from "@/components/ui/tooltip"; -import { useFullscreen } from "@/hooks/use-fullscreen"; -import { toast } from "sonner"; import { Toaster } from "@/components/ui/sonner"; type DraggableGridLayoutProps = { @@ -55,6 +53,8 @@ type DraggableGridLayoutProps = { visibleCameras: string[]; isEditMode: boolean; setIsEditMode: React.Dispatch>; + fullscreen: boolean; + toggleFullscreen: () => void; }; export default function DraggableGridLayout({ cameras, @@ -67,6 +67,8 @@ export default function DraggableGridLayout({ visibleCameras, isEditMode, setIsEditMode, + fullscreen, + toggleFullscreen, }: DraggableGridLayoutProps) { const { data: config } = useSWR("config"); const birdseyeConfig = useMemo(() => config?.birdseye, [config]); @@ -289,20 +291,6 @@ export default function DraggableGridLayout({ } }, [containerRef, containerHeight]); - // fullscreen state - - const { fullscreen, toggleFullscreen, error, clearError } = - useFullscreen(gridContainerRef); - - useEffect(() => { - if (error !== null) { - toast.error(`Error attempting fullscreen mode: ${error}`, { - position: "top-center", - }); - clearError(); - } - }, [error, clearError]); - const cellHeight = useMemo(() => { const aspectRatio = 16 / 9; // subtract container margin, 1 camera takes up at least 4 rows @@ -463,21 +451,23 @@ export default function DraggableGridLayout({ {!isEditMode && ( <> - - -
- setEditGroup((prevEditGroup) => !prevEditGroup) - } - > - -
-
- - {isEditMode ? "Exit Editing" : "Edit Camera Group"} - -
+ {!fullscreen && ( + + +
+ setEditGroup((prevEditGroup) => !prevEditGroup) + } + > + +
+
+ + {isEditMode ? "Exit Editing" : "Edit Camera Group"} + +
+ )}
void; +}; + +export default function LiveBirdseyeView({ + fullscreen, + toggleFullscreen, +}: LiveBirdseyeViewProps) { const { data: config } = useSWR("config"); const navigate = useNavigate(); const { isPortrait } = useMobileOrientation(); @@ -28,10 +35,6 @@ export default function LiveBirdseyeView() { const [{ width: windowWidth, height: windowHeight }] = useResizeObserver(window); - // fullscreen state - - const { fullscreen, toggleFullscreen } = useFullscreen(mainRef); - // playback state const cameraAspectRatio = useMemo(() => { diff --git a/web/src/views/live/LiveCameraView.tsx b/web/src/views/live/LiveCameraView.tsx index f6d85a565..9e897cbd6 100644 --- a/web/src/views/live/LiveCameraView.tsx +++ b/web/src/views/live/LiveCameraView.tsx @@ -72,15 +72,18 @@ import { import { useNavigate } from "react-router-dom"; import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"; import useSWR from "swr"; -import { useFullscreen } from "@/hooks/use-fullscreen"; type LiveCameraViewProps = { config?: FrigateConfig; camera: CameraConfig; + fullscreen: boolean; + toggleFullscreen: () => void; }; export default function LiveCameraView({ config, camera, + fullscreen, + toggleFullscreen, }: LiveCameraViewProps) { const navigate = useNavigate(); const { isPortrait } = useMobileOrientation(); @@ -175,9 +178,7 @@ export default function LiveCameraView({ [clickOverlayRef, clickOverlay, sendPtz], ); - // fullscreen / pip state - - const { fullscreen, toggleFullscreen } = useFullscreen(mainRef); + // pip state useEffect(() => { setPip(document.pictureInPictureElement != null); @@ -314,6 +315,18 @@ export default function LiveCameraView({
+ {fullscreen && ( + + )} {!isIOS && ( void; + fullscreen: boolean; + toggleFullscreen: () => void; }; export default function LiveDashboardView({ cameras, cameraGroup, includeBirdseye, onSelectCamera, + fullscreen, + toggleFullscreen, }: LiveDashboardViewProps) { const { data: config } = useSWR("config"); @@ -214,7 +218,7 @@ export default function LiveDashboardView({
)} - {events && events.length > 0 && ( + {!fullscreen && events && events.length > 0 && (
@@ -281,6 +285,8 @@ export default function LiveDashboardView({ visibleCameras={visibleCameras} isEditMode={isEditMode} setIsEditMode={setIsEditMode} + fullscreen={fullscreen} + toggleFullscreen={toggleFullscreen} /> )}