import { useRef, useState, useCallback } from 'react' import { Play, Pause, Volume2, VolumeX, SkipBack, SkipForward } from 'lucide-react' import { usePlayerStore } from '@/stores/player' import { formatDuration } from '@/lib/utils' import { publicUrl } from '@/lib/storage' import { Avatar } from '@/components/ui/Avatar' export function PlayerBar() { const { current, isPlaying, isExternal, progress, duration, volume, toggle, seek, setVolume } = usePlayerStore() const seekRef = useRef(null) const [isDragging, setIsDragging] = useState(false) const [dragPct, setDragPct] = useState(0) const calcPct = useCallback((clientX: number) => { if (!seekRef.current) return 0 const rect = seekRef.current.getBoundingClientRect() return Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100)) }, []) if (!current) return null const pct = duration > 0 ? (progress / duration) * 100 : 0 const displayPct = isDragging ? dragPct : pct // Allow seeking for native audio and YouTube (via IFrame API) const canSeek = duration > 0 const showBar = true function startDrag(clientX: number) { if (!canSeek) return setIsDragging(true) setDragPct(calcPct(clientX)) } function handleMouseDown(e: React.MouseEvent) { if (!canSeek) return e.preventDefault() startDrag(e.clientX) const onMove = (ev: MouseEvent) => setDragPct(calcPct(ev.clientX)) const onUp = (ev: MouseEvent) => { setIsDragging(false) const p = calcPct(ev.clientX) seek((p / 100) * duration) window.removeEventListener('mousemove', onMove) window.removeEventListener('mouseup', onUp) } window.addEventListener('mousemove', onMove) window.addEventListener('mouseup', onUp) } function handleTouchStart(e: React.TouchEvent) { if (!canSeek) return startDrag(e.touches[0].clientX) const el = seekRef.current! const onMove = (ev: TouchEvent) => { ev.preventDefault(); setDragPct(calcPct(ev.touches[0].clientX)) } const onEnd = (ev: TouchEvent) => { setIsDragging(false) seek((calcPct(ev.changedTouches[0].clientX) / 100) * duration) el.removeEventListener('touchmove', onMove) el.removeEventListener('touchend', onEnd) } el.addEventListener('touchmove', onMove, { passive: false }) el.addEventListener('touchend', onEnd) } function handleClick(e: React.MouseEvent) { if (!canSeek || isDragging) return seek((calcPct(e.clientX) / 100) * duration) } const currentTime = isDragging ? (dragPct / 100) * duration : progress return (
{/* Main row */}
{/* Cover + info */}
{current.cover_url ? ( ) : ( )} {isPlaying && (
)}

{current.title}

{current.creator?.username}

{/* Controls */}
{canSeek && ( )} {canSeek && ( )}
{/* Time + Volume (desktop) */}
{duration > 0 ? `${formatDuration(currentTime)} / ${formatDuration(duration)}` : ''} setVolume(parseFloat(e.target.value))} className="w-20 accent-white" />
{/* Seek bar */} {showBar &&
{/* Track background — centered vertically */}
{/* Track filled */}
{/* Thumb — centered on track */} {canSeek && (
)}
}
) }