import { useCallback, useMemo, useRef, useState } from "react"; import { PlayerContext, type PlayerItem } from "./PlayerContext.ts"; export function PlayerProvider({ children }: { children: React.ReactNode }) { const [current, setCurrent] = useState(null); const [playing, setPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); // GlobalPlayer registers the active MediaPlayer's imperative handles here const seekRef = useRef<((t: number) => void) | null>(null); const toggleRef = useRef<(() => void) | null>(null); // Suppresses stale timeupdate callbacks that fire between play() and the old // MediaPlayer's unmount cleanup. Cleared when the new media fires onPlayStateChange(true). const suppressUpdates = useRef(false); const play = useCallback((item: PlayerItem) => { suppressUpdates.current = true; setCurrent(item); setCurrentTime(0); setDuration(0); setPlaying(false); }, []); const stop = useCallback(() => { setCurrent(null); setPlaying(false); setCurrentTime(0); setDuration(0); }, []); const seekTo = useCallback((t: number) => { seekRef.current?.(t); setCurrentTime(t); // optimistic — prevents waveform jitter before timeupdate fires }, []); const togglePlay = useCallback(() => { toggleRef.current?.(); }, []); const onPlayStateChange = useCallback((p: boolean) => { if (p) suppressUpdates.current = false; setPlaying(p); }, []); const onTimeUpdate = useCallback((t: number, d: number) => { if (suppressUpdates.current) return; setCurrentTime(t); setDuration(d); }, []); const value = useMemo(() => ({ current, playing, currentTime, duration, play, stop, seekTo, togglePlay, seekRef, toggleRef, onPlayStateChange, onTimeUpdate, }), [ current, playing, currentTime, duration, play, stop, seekTo, togglePlay, onPlayStateChange, onTimeUpdate, ]); return ( {children} ); }