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.
119 lines
3.7 KiB
119 lines
3.7 KiB
import { useCallback, useEffect, useRef, useState } from "react"; |
|
|
|
import { |
|
MouseActivity, |
|
makePercentage, |
|
makePercentageString, |
|
useProgressBar, |
|
} from "@/hooks/useProgressBar"; |
|
import { getPlayerState } from "@/video/state/cache"; |
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks"; |
|
import { useControls } from "@/video/state/logic/controls"; |
|
import { useProgress } from "@/video/state/logic/progress"; |
|
|
|
import ThumbnailAction from "./ThumbnailAction"; |
|
|
|
export function ProgressAction() { |
|
const descriptor = useVideoPlayerDescriptor(); |
|
const controls = useControls(descriptor); |
|
const videoTime = useProgress(descriptor); |
|
const ref = useRef<HTMLDivElement>(null); |
|
const dragRef = useRef<boolean>(false); |
|
const controlRef = useRef<typeof controls>(controls); |
|
const [hoverPosition, setHoverPosition] = useState<number>(0); |
|
const [isThumbnailVisible, setIsThumbnailVisible] = useState<boolean>(false); |
|
const isCasting = getPlayerState(descriptor).casting.isCasting; |
|
const onMouseOver = useCallback((e: MouseActivity) => { |
|
setHoverPosition(e.clientX); |
|
setIsThumbnailVisible(true); |
|
}, []); |
|
const onMouseLeave = useCallback(() => { |
|
setIsThumbnailVisible(false); |
|
}, []); |
|
useEffect(() => { |
|
controlRef.current = controls; |
|
}, [controls]); |
|
|
|
const commitTime = useCallback( |
|
(percentage) => { |
|
controls.setTime(percentage * videoTime.duration); |
|
}, |
|
[controls, videoTime] |
|
); |
|
const { dragging, dragPercentage, dragMouseDown } = useProgressBar( |
|
ref, |
|
commitTime |
|
); |
|
|
|
useEffect(() => { |
|
if (dragRef.current === dragging) return; |
|
dragRef.current = dragging; |
|
controls.setSeeking(dragging); |
|
}, [dragRef, dragging, controls]); |
|
|
|
useEffect(() => { |
|
if (dragging) { |
|
const state = getPlayerState(descriptor); |
|
controlRef.current.setDraggingTime( |
|
state.progress.duration * (dragPercentage / 100) |
|
); |
|
} |
|
}, [descriptor, dragging, dragPercentage]); |
|
|
|
let watchProgress = makePercentageString( |
|
makePercentage((videoTime.time / videoTime.duration) * 100) |
|
); |
|
if (dragging) |
|
watchProgress = makePercentageString(makePercentage(dragPercentage)); |
|
|
|
const bufferProgress = makePercentageString( |
|
makePercentage((videoTime.buffered / videoTime.duration) * 100) |
|
); |
|
|
|
return ( |
|
<div |
|
ref={ref} |
|
className="group pointer-events-auto w-full cursor-pointer rounded-full px-2" |
|
> |
|
<div |
|
className="-my-3 flex h-8 items-center" |
|
onMouseDown={dragMouseDown} |
|
onTouchStart={dragMouseDown} |
|
onMouseMove={onMouseOver} |
|
onMouseLeave={onMouseLeave} |
|
> |
|
<div |
|
className={`relative h-1 flex-1 rounded-full bg-gray-500 bg-opacity-50 transition-[height] duration-100 group-hover:h-2 ${ |
|
dragging ? "!h-2" : "" |
|
}`} |
|
> |
|
<div |
|
className="absolute inset-y-0 left-0 flex items-center justify-end rounded-full bg-gray-300 bg-opacity-20" |
|
style={{ |
|
width: bufferProgress, |
|
}} |
|
/> |
|
<div |
|
className="absolute inset-y-0 left-0 flex items-center justify-end rounded-full bg-bink-600" |
|
style={{ |
|
width: watchProgress, |
|
}} |
|
> |
|
<div |
|
className={`absolute h-1 w-1 translate-x-1/2 rounded-full bg-white opacity-0 transition-[transform,opacity] group-hover:scale-[400%] group-hover:opacity-100 ${ |
|
dragging ? "!scale-[400%] !opacity-100" : "" |
|
}`} |
|
/> |
|
</div> |
|
</div> |
|
</div> |
|
{isThumbnailVisible && !isCasting ? ( |
|
<ThumbnailAction |
|
parentRef={ref} |
|
videoTime={videoTime} |
|
hoverPosition={hoverPosition} |
|
/> |
|
) : null} |
|
</div> |
|
); |
|
}
|
|
|