A small web app for watching movies and shows easily
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

97 lines
3.1 KiB

import { ReactNode, RefObject, useEffect, useRef } from "react";
import { OverlayDisplay } from "@/components/overlays/OverlayDisplay";
import { CastingInternal } from "@/components/player/internals/CastingInternal";
import { HeadUpdater } from "@/components/player/internals/HeadUpdater";
import { KeyboardEvents } from "@/components/player/internals/KeyboardEvents";
import { ProgressSaver } from "@/components/player/internals/ProgressSaver";
import { VideoClickTarget } from "@/components/player/internals/VideoClickTarget";
import { VideoContainer } from "@/components/player/internals/VideoContainer";
import { PlayerHoverState } from "@/stores/player/slices/interface";
import { usePlayerStore } from "@/stores/player/store";
export interface PlayerProps {
children?: ReactNode;
onLoad?: () => void;
}
function useHovering(containerEl: RefObject<HTMLDivElement>) {
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const updateInterfaceHovering = usePlayerStore(
(s) => s.updateInterfaceHovering
);
const hovering = usePlayerStore((s) => s.interface.hovering);
useEffect(() => {
if (!containerEl.current) return;
const el = containerEl.current;
function pointerMove(e: PointerEvent) {
if (e.pointerType !== "mouse") return;
updateInterfaceHovering(PlayerHoverState.MOUSE_HOVER);
if (timeoutRef.current) clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => {
updateInterfaceHovering(PlayerHoverState.NOT_HOVERING);
timeoutRef.current = null;
}, 3000);
}
function pointerLeave(e: PointerEvent) {
if (e.pointerType !== "mouse") return;
updateInterfaceHovering(PlayerHoverState.NOT_HOVERING);
if (timeoutRef.current) clearTimeout(timeoutRef.current);
}
el.addEventListener("pointermove", pointerMove);
el.addEventListener("pointerleave", pointerLeave);
return () => {
el.removeEventListener("pointermove", pointerMove);
el.removeEventListener("pointerleave", pointerLeave);
};
}, [containerEl, hovering, updateInterfaceHovering]);
}
function BaseContainer(props: { children?: ReactNode }) {
const containerEl = useRef<HTMLDivElement | null>(null);
const display = usePlayerStore((s) => s.display);
useHovering(containerEl);
// report container element to display interface
useEffect(() => {
if (display && containerEl.current) {
display.processContainerElement(containerEl.current);
}
}, [display, containerEl]);
return (
<div ref={containerEl}>
<OverlayDisplay>
<div className="h-screen select-none">{props.children}</div>
</OverlayDisplay>
</div>
);
}
export function Container(props: PlayerProps) {
const propRef = useRef(props.onLoad);
useEffect(() => {
propRef.current?.();
}, []);
return (
<div className="relative">
<BaseContainer>
<CastingInternal />
<VideoContainer />
<ProgressSaver />
<KeyboardEvents />
<div className="relative h-screen overflow-hidden">
<VideoClickTarget />
<HeadUpdater />
{props.children}
</div>
</BaseContainer>
</div>
);
}