sudo-archive/src/video/components/__old/VideoPlayer.tsx

134 lines
3.8 KiB
TypeScript
Raw Normal View History

2023-01-15 15:51:55 +00:00
import { useGoBack } from "@/hooks/useGoBack";
import { useVolumeControl } from "@/hooks/useVolumeToggle";
2023-01-10 18:53:55 +00:00
import { forwardRef, useContext, useEffect, useRef } from "react";
import { VideoErrorBoundary } from "../../components/video/parts/VideoErrorBoundary";
import {
useVideoPlayerState,
VideoPlayerContext,
VideoPlayerContextProvider,
} from "../../video/components./../components/video/VideoContext";
2023-01-08 12:15:32 +00:00
2023-01-08 21:29:38 +00:00
export interface VideoPlayerProps {
autoPlay?: boolean;
2023-01-08 12:15:32 +00:00
children?: React.ReactNode;
}
const VideoPlayerInternals = forwardRef<
HTMLVideoElement,
{ autoPlay: boolean }
>((props, ref) => {
2023-01-08 12:15:32 +00:00
const video = useContext(VideoPlayerContext);
const didInitialize = useRef<{ source: string | null } | null>(null);
const { videoState } = useVideoPlayerState();
const { toggleVolume } = useVolumeControl();
2023-01-08 12:15:32 +00:00
2023-01-10 18:53:55 +00:00
useEffect(() => {
const value = { source: video.source };
const hasChanged = value.source !== didInitialize.current?.source;
if (!hasChanged) return;
2023-01-10 18:53:55 +00:00
if (!video.state.hasInitialized || !video.source) return;
video.state.initPlayer(video.source, video.sourceType);
didInitialize.current = value;
2023-01-10 18:53:55 +00:00
}, [didInitialize, video]);
useEffect(() => {
let isRolling = false;
const onKeyDown = (evt: KeyboardEvent) => {
if (!videoState.isFocused) return;
if (!ref || !(ref as any)?.current) return;
const el = (ref as any).current as HTMLVideoElement;
switch (evt.key.toLowerCase()) {
// Toggle fullscreen
case "f":
if (videoState.isFullscreen) {
videoState.exitFullscreen();
} else {
videoState.enterFullscreen();
}
break;
// Skip backwards
case "arrowleft":
videoState.setTime(videoState.time - 5);
break;
// Skip forward
case "arrowright":
videoState.setTime(videoState.time + 5);
break;
// Pause / play
case " ":
if (videoState.isPaused) {
videoState.play();
} else {
videoState.pause();
}
break;
// Mute
case "m":
toggleVolume();
break;
// Do a barrel Roll!
case "r":
if (isRolling) return;
isRolling = true;
el.classList.add("roll");
setTimeout(() => {
isRolling = false;
el.classList.remove("roll");
}, 1000);
break;
default:
break;
}
};
window.addEventListener("keydown", onKeyDown);
return () => {
window.removeEventListener("keydown", onKeyDown);
};
}, [videoState, toggleVolume, ref]);
2023-01-10 18:53:55 +00:00
// muted attribute is required for safari, as they cant change the volume itself
2023-01-08 12:15:32 +00:00
return (
<video
ref={ref}
autoPlay={props.autoPlay}
2023-01-10 18:53:55 +00:00
muted={video.state.volume === 0}
playsInline
className="h-full w-full"
2023-01-10 18:53:55 +00:00
/>
2023-01-08 12:15:32 +00:00
);
});
export function VideoPlayer(props: VideoPlayerProps) {
const playerRef = useRef<HTMLVideoElement | null>(null);
2023-01-08 15:23:42 +00:00
const playerWrapperRef = useRef<HTMLDivElement | null>(null);
2023-01-15 15:51:55 +00:00
const goBack = useGoBack();
// TODO move error boundary to only decorated, <VideoPlayer /> shouldn't have styling
2023-01-08 12:15:32 +00:00
return (
2023-01-08 15:23:42 +00:00
<VideoPlayerContextProvider player={playerRef} wrapper={playerWrapperRef}>
<div
className="is-video-player relative h-full w-full select-none overflow-hidden bg-black [border-left:env(safe-area-inset-left)_solid_transparent] [border-right:env(safe-area-inset-right)_solid_transparent]"
2023-01-08 15:23:42 +00:00
ref={playerWrapperRef}
>
2023-01-15 15:51:55 +00:00
<VideoErrorBoundary onGoBack={goBack}>
<VideoPlayerInternals
autoPlay={props.autoPlay ?? false}
ref={playerRef}
/>
<div className="absolute inset-0">{props.children}</div>
</VideoErrorBoundary>
2023-01-08 15:23:42 +00:00
</div>
2023-01-08 12:15:32 +00:00
</VideoPlayerContextProvider>
);
}