import { MouseEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState, } from "react"; import { useProgressBar } from "@/hooks/useProgressBar"; import { nearestImageAt } from "@/stores/player/slices/thumbnails"; import { usePlayerStore } from "@/stores/player/store"; import { durationExceedsHour, formatSeconds } from "@/utils/formatSeconds"; function ThumbnailDisplay(props: { at: number; show: boolean }) { const thumbnailImages = usePlayerStore((s) => s.thumbnails.images); const currentThumbnail = useMemo(() => { return nearestImageAt(thumbnailImages, props.at)?.image; }, [thumbnailImages, props.at]); const [offsets, setOffsets] = useState({ offscreenLeft: 0, offscreenRight: 0, }); const ref = useRef(null); useEffect(() => { if (!ref.current) return; const rect = ref.current.getBoundingClientRect(); const padding = 32; const left = Math.max(0, (rect.left - padding) * -1); const right = Math.max(0, rect.right + padding - window.innerWidth); setOffsets({ offscreenLeft: left, offscreenRight: right, }); }, [props.at]); if (!props.show || !currentThumbnail) return null; return (
0 ? offsets.offscreenLeft : -offsets.offscreenRight }px)`, }} >

{formatSeconds( Math.max(props.at, 0), durationExceedsHour(props.at), )}

); } function useMouseHoverPosition(barRef: RefObject) { const [mousePos, setMousePos] = useState(-1); const mouseMove = useCallback( (e: MouseEvent) => { const bar = barRef.current; if (!bar) return; const rect = barRef.current.getBoundingClientRect(); const pos = (e.pageX - rect.left) / barRef.current.offsetWidth; setMousePos(pos * 100); }, [setMousePos, barRef], ); const mouseLeave = useCallback(() => { setMousePos(-1); }, [setMousePos]); return { mousePos, mouseMove, mouseLeave }; } export function ProgressBar() { const { duration, time, buffered } = usePlayerStore((s) => s.progress); const display = usePlayerStore((s) => s.display); const setDraggingTime = usePlayerStore((s) => s.setDraggingTime); const setSeeking = usePlayerStore((s) => s.setSeeking); const { isSeeking } = usePlayerStore((s) => s.interface); const commitTime = useCallback( (percentage: number) => { display?.setTime(percentage * duration); }, [duration, display], ); const ref = useRef(null); const { mouseMove, mouseLeave, mousePos } = useMouseHoverPosition(ref); const { dragging, dragPercentage, dragMouseDown } = useProgressBar( ref, commitTime, ); useEffect(() => { setSeeking(dragging); }, [setSeeking, dragging]); useEffect(() => { setDraggingTime((dragPercentage / 100) * duration); }, [setDraggingTime, duration, dragPercentage]); return (
-1} />
{/* Pre-loaded content bar */}
{/* Actual progress bar */}
); }