Dynamically detect if full screen is supported (#13197)

This commit is contained in:
Nicolas Mowen 2024-08-19 15:01:21 -06:00
parent 65ceadda2b
commit 4974defe6f
7 changed files with 57 additions and 15 deletions

View File

@ -6,7 +6,7 @@ import {
useState, useState,
} from "react"; } from "react";
import Hls from "hls.js"; import Hls from "hls.js";
import { isAndroid, isDesktop, isIOS, isMobile } from "react-device-detect"; import { isAndroid, isDesktop, isMobile } from "react-device-detect";
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
import VideoControls from "./VideoControls"; import VideoControls from "./VideoControls";
import { VideoResolutionType } from "@/types/live"; import { VideoResolutionType } from "@/types/live";
@ -33,6 +33,7 @@ type HlsVideoPlayerProps = {
visible: boolean; visible: boolean;
currentSource: string; currentSource: string;
hotKeys: boolean; hotKeys: boolean;
supportsFullscreen: boolean;
fullscreen: boolean; fullscreen: boolean;
onClipEnded?: () => void; onClipEnded?: () => void;
onPlayerLoaded?: () => void; onPlayerLoaded?: () => void;
@ -49,6 +50,7 @@ export default function HlsVideoPlayer({
visible, visible,
currentSource, currentSource,
hotKeys, hotKeys,
supportsFullscreen,
fullscreen, fullscreen,
onClipEnded, onClipEnded,
onPlayerLoaded, onPlayerLoaded,
@ -180,7 +182,7 @@ export default function HlsVideoPlayer({
seek: true, seek: true,
playbackRate: true, playbackRate: true,
plusUpload: config?.plus?.enabled == true, plusUpload: config?.plus?.enabled == true,
fullscreen: !isIOS, fullscreen: supportsFullscreen,
}} }}
setControlsOpen={setControlsOpen} setControlsOpen={setControlsOpen}
setMuted={(muted) => setMuted(muted, true)} setMuted={(muted) => setMuted(muted, true)}

View File

@ -24,6 +24,7 @@ type DynamicVideoPlayerProps = {
startTimestamp?: number; startTimestamp?: number;
isScrubbing: boolean; isScrubbing: boolean;
hotKeys: boolean; hotKeys: boolean;
supportsFullscreen: boolean;
fullscreen: boolean; fullscreen: boolean;
onControllerReady: (controller: DynamicVideoController) => void; onControllerReady: (controller: DynamicVideoController) => void;
onTimestampUpdate?: (timestamp: number) => void; onTimestampUpdate?: (timestamp: number) => void;
@ -40,6 +41,7 @@ export default function DynamicVideoPlayer({
startTimestamp, startTimestamp,
isScrubbing, isScrubbing,
hotKeys, hotKeys,
supportsFullscreen,
fullscreen, fullscreen,
onControllerReady, onControllerReady,
onTimestampUpdate, onTimestampUpdate,
@ -201,6 +203,7 @@ export default function DynamicVideoPlayer({
visible={!(isScrubbing || isLoading)} visible={!(isScrubbing || isLoading)}
currentSource={source} currentSource={source}
hotKeys={hotKeys} hotKeys={hotKeys}
supportsFullscreen={supportsFullscreen}
fullscreen={fullscreen} fullscreen={fullscreen}
onTimeUpdate={onTimeUpdate} onTimeUpdate={onTimeUpdate}
onPlayerLoaded={onPlayerLoaded} onPlayerLoaded={onPlayerLoaded}

View File

@ -1,4 +1,4 @@
import { RefObject, useCallback, useEffect, useState } from "react"; import { RefObject, useCallback, useEffect, useMemo, useState } from "react";
import nosleep from "nosleep.js"; import nosleep from "nosleep.js";
const NoSleep = new nosleep(); const NoSleep = new nosleep();
@ -147,5 +147,31 @@ export function useFullscreen<T extends HTMLElement = HTMLElement>(
} }
}, [elementRef, handleFullscreenChange, handleFullscreenError]); }, [elementRef, handleFullscreenChange, handleFullscreenError]);
return { fullscreen, toggleFullscreen, error, clearError }; // compatibility
const supportsFullScreen = useMemo<boolean>(() => {
// @ts-expect-error we need to check that fullscreen exists
if (document.exitFullscreen) return true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((document as any).msExitFullscreen)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((document as any).webkitExitFullscreen)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((document as any).mozCancelFullScreen)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return true;
return false;
}, []);
return {
fullscreen,
toggleFullscreen,
supportsFullScreen,
error,
clearError,
};
} }

View File

@ -36,7 +36,8 @@ function Live() {
const mainRef = useRef<HTMLDivElement | null>(null); const mainRef = useRef<HTMLDivElement | null>(null);
const { fullscreen, toggleFullscreen } = useFullscreen(mainRef); const { fullscreen, toggleFullscreen, supportsFullScreen } =
useFullscreen(mainRef);
// document title // document title
@ -100,6 +101,7 @@ function Live() {
<div className="size-full" ref={mainRef}> <div className="size-full" ref={mainRef}>
{selectedCameraName === "birdseye" ? ( {selectedCameraName === "birdseye" ? (
<LiveBirdseyeView <LiveBirdseyeView
supportsFullscreen={supportsFullScreen}
fullscreen={fullscreen} fullscreen={fullscreen}
toggleFullscreen={toggleFullscreen} toggleFullscreen={toggleFullscreen}
/> />
@ -107,6 +109,7 @@ function Live() {
<LiveCameraView <LiveCameraView
config={config} config={config}
camera={selectedCamera} camera={selectedCamera}
supportsFullscreen={supportsFullScreen}
fullscreen={fullscreen} fullscreen={fullscreen}
toggleFullscreen={toggleFullscreen} toggleFullscreen={toggleFullscreen}
/> />

View File

@ -257,7 +257,8 @@ export function RecordingView({
// fullscreen // fullscreen
const { fullscreen, toggleFullscreen } = useFullscreen(mainLayoutRef); const { fullscreen, toggleFullscreen, supportsFullScreen } =
useFullscreen(mainLayoutRef);
// layout // layout
@ -549,6 +550,7 @@ export function RecordingView({
mainControllerRef.current = controller; mainControllerRef.current = controller;
}} }}
isScrubbing={scrubbing || exportMode == "timeline"} isScrubbing={scrubbing || exportMode == "timeline"}
supportsFullscreen={supportsFullScreen}
setFullResolution={setFullResolution} setFullResolution={setFullResolution}
toggleFullscreen={toggleFullscreen} toggleFullscreen={toggleFullscreen}
containerRef={mainLayoutRef} containerRef={mainLayoutRef}

View File

@ -22,11 +22,13 @@ import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import useSWR from "swr"; import useSWR from "swr";
type LiveBirdseyeViewProps = { type LiveBirdseyeViewProps = {
supportsFullscreen: boolean;
fullscreen: boolean; fullscreen: boolean;
toggleFullscreen: () => void; toggleFullscreen: () => void;
}; };
export default function LiveBirdseyeView({ export default function LiveBirdseyeView({
supportsFullscreen,
fullscreen, fullscreen,
toggleFullscreen, toggleFullscreen,
}: LiveBirdseyeViewProps) { }: LiveBirdseyeViewProps) {
@ -155,14 +157,16 @@ export default function LiveBirdseyeView({
<div <div
className={`mr-1 flex flex-row items-center gap-2 *:rounded-lg ${isMobile ? "landscape:flex-col" : ""}`} className={`mr-1 flex flex-row items-center gap-2 *:rounded-lg ${isMobile ? "landscape:flex-col" : ""}`}
> >
<CameraFeatureToggle {supportsFullscreen && (
className="p-2 md:p-0" <CameraFeatureToggle
variant={fullscreen ? "overlay" : "primary"} className="p-2 md:p-0"
Icon={fullscreen ? FaCompress : FaExpand} variant={fullscreen ? "overlay" : "primary"}
isActive={fullscreen} Icon={fullscreen ? FaCompress : FaExpand}
title={fullscreen ? "Close" : "Fullscreen"} isActive={fullscreen}
onClick={toggleFullscreen} title={fullscreen ? "Close" : "Fullscreen"}
/> onClick={toggleFullscreen}
/>
)}
{!isIOS && !isFirefox && config.birdseye.restream && ( {!isIOS && !isFirefox && config.birdseye.restream && (
<CameraFeatureToggle <CameraFeatureToggle
className="p-2 md:p-0" className="p-2 md:p-0"

View File

@ -83,12 +83,14 @@ import { useSessionPersistence } from "@/hooks/use-session-persistence";
type LiveCameraViewProps = { type LiveCameraViewProps = {
config?: FrigateConfig; config?: FrigateConfig;
camera: CameraConfig; camera: CameraConfig;
supportsFullscreen: boolean;
fullscreen: boolean; fullscreen: boolean;
toggleFullscreen: () => void; toggleFullscreen: () => void;
}; };
export default function LiveCameraView({ export default function LiveCameraView({
config, config,
camera, camera,
supportsFullscreen,
fullscreen, fullscreen,
toggleFullscreen, toggleFullscreen,
}: LiveCameraViewProps) { }: LiveCameraViewProps) {
@ -376,7 +378,7 @@ export default function LiveCameraView({
)} )}
</Button> </Button>
)} )}
{!isIOS && ( {supportsFullscreen && (
<CameraFeatureToggle <CameraFeatureToggle
className="p-2 md:p-0" className="p-2 md:p-0"
variant={fullscreen ? "overlay" : "primary"} variant={fullscreen ? "overlay" : "primary"}