Implement recordings fullscreen and rework recordings layout size calculation (#11318)

* Implement fullscreen button

* wrap items on mobile

* control based on width

* refresh

* Implement basic fullscreen

* Fix scrolling

* Add observer to detect of row overflows

* Use cn to simplify classnames

* dynamically respond to layout sizing

* Simplify listener

* Simplify layout

* Handle tall browser
This commit is contained in:
Nicolas Mowen
2024-05-09 15:06:29 -06:00
committed by GitHub
parent 021ffb2437
commit 8b344cea81
6 changed files with 180 additions and 19 deletions

View File

@@ -6,7 +6,7 @@ import {
useState,
} from "react";
import Hls from "hls.js";
import { isAndroid, isDesktop, isMobile } from "react-device-detect";
import { isAndroid, isDesktop, isIOS, isMobile } from "react-device-detect";
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
import VideoControls from "./VideoControls";
import { VideoResolutionType } from "@/types/live";
@@ -28,24 +28,28 @@ type HlsVideoPlayerProps = {
visible: boolean;
currentSource: string;
hotKeys: boolean;
fullscreen: boolean;
onClipEnded?: () => void;
onPlayerLoaded?: () => void;
onTimeUpdate?: (time: number) => void;
onPlaying?: () => void;
setFullResolution?: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
onUploadFrame?: (playTime: number) => Promise<AxiosResponse> | undefined;
setFullscreen?: (full: boolean) => void;
};
export default function HlsVideoPlayer({
videoRef,
visible,
currentSource,
hotKeys,
fullscreen,
onClipEnded,
onPlayerLoaded,
onTimeUpdate,
onPlaying,
setFullResolution,
onUploadFrame,
setFullscreen,
}: HlsVideoPlayerProps) {
const { data: config } = useSWR<FrigateConfig>("config");
@@ -153,6 +157,7 @@ export default function HlsVideoPlayer({
seek: true,
playbackRate: true,
plusUpload: config?.plus?.enabled == true,
fullscreen: !isIOS,
}}
setControlsOpen={setControlsOpen}
setMuted={setMuted}
@@ -196,6 +201,8 @@ export default function HlsVideoPlayer({
}
}
}}
fullscreen={fullscreen}
setFullscreen={setFullscreen}
/>
<TransformComponent
wrapperStyle={{

View File

@@ -30,12 +30,14 @@ import {
AlertDialogTrigger,
} from "../ui/alert-dialog";
import { cn } from "@/lib/utils";
import { FaCompress, FaExpand } from "react-icons/fa";
type VideoControls = {
volume?: boolean;
seek?: boolean;
playbackRate?: boolean;
plusUpload?: boolean;
fullscreen?: boolean;
};
const CONTROLS_DEFAULT: VideoControls = {
@@ -43,6 +45,7 @@ const CONTROLS_DEFAULT: VideoControls = {
seek: true,
playbackRate: true,
plusUpload: false,
fullscreen: false,
};
const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16];
@@ -57,12 +60,14 @@ type VideoControlsProps = {
playbackRates?: number[];
playbackRate: number;
hotKeys?: boolean;
fullscreen?: boolean;
setControlsOpen?: (open: boolean) => void;
setMuted?: (muted: boolean) => void;
onPlayPause: (play: boolean) => void;
onSeek: (diff: number) => void;
onSetPlaybackRate: (rate: number) => void;
onUploadFrame?: () => void;
setFullscreen?: (full: boolean) => void;
};
export default function VideoControls({
className,
@@ -75,12 +80,14 @@ export default function VideoControls({
playbackRates = PLAYBACK_RATE_DEFAULT,
playbackRate,
hotKeys = true,
fullscreen,
setControlsOpen,
setMuted,
onPlayPause,
onSeek,
onSetPlaybackRate,
onUploadFrame,
setFullscreen,
}: VideoControlsProps) {
const onReplay = useCallback(
(e: React.MouseEvent<SVGElement>) => {
@@ -163,7 +170,7 @@ export default function VideoControls({
return (
<div
className={cn(
"px-4 py-2 flex justify-between items-center gap-8 text-primary z-50 bg-background/60 rounded-lg",
"w-[96%] sm:w-auto px-4 py-2 flex flex-wrap sm:flex-nowrap justify-between items-center gap-4 sm:gap-8 text-primary z-50 bg-background/60 rounded-lg",
className,
)}
>
@@ -248,6 +255,14 @@ export default function VideoControls({
onUploadFrame={onUploadFrame}
/>
)}
{features.fullscreen && setFullscreen && (
<div
className="cursor-pointer"
onClick={() => setFullscreen(!fullscreen)}
>
{fullscreen ? <FaCompress /> : <FaExpand />}
</div>
)}
</div>
);
}

View File

@@ -24,10 +24,12 @@ type DynamicVideoPlayerProps = {
startTimestamp?: number;
isScrubbing: boolean;
hotKeys: boolean;
fullscreen: boolean;
onControllerReady: (controller: DynamicVideoController) => void;
onTimestampUpdate?: (timestamp: number) => void;
onClipEnded?: () => void;
setFullResolution: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
setFullscreen: (full: boolean) => void;
};
export default function DynamicVideoPlayer({
className,
@@ -37,10 +39,12 @@ export default function DynamicVideoPlayer({
startTimestamp,
isScrubbing,
hotKeys,
fullscreen,
onControllerReady,
onTimestampUpdate,
onClipEnded,
setFullResolution,
setFullscreen,
}: DynamicVideoPlayerProps) {
const apiHost = useApiHost();
const { data: config } = useSWR<FrigateConfig>("config");
@@ -184,6 +188,7 @@ export default function DynamicVideoPlayer({
visible={!(isScrubbing || isLoading)}
currentSource={source}
hotKeys={hotKeys}
fullscreen={fullscreen}
onTimeUpdate={onTimeUpdate}
onPlayerLoaded={onPlayerLoaded}
onClipEnded={onClipEnded}
@@ -201,6 +206,7 @@ export default function DynamicVideoPlayer({
}}
setFullResolution={setFullResolution}
onUploadFrame={onUploadFrameToPlus}
setFullscreen={setFullscreen}
/>
<PreviewPlayer
className={cn(