jefflix-website/components/music/search-results.tsx

124 lines
3.9 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useMusicPlayer, type Track } from './music-provider'
import { DownloadButton } from './download-button'
import { SwipeableRow } from './swipeable-row'
import { Play, Pause, ListPlus } from 'lucide-react'
function formatDuration(secs: number) {
if (!secs) return ''
const m = Math.floor(secs / 60)
const s = Math.floor(secs % 60)
return `${m}:${s.toString().padStart(2, '0')}`
}
export function SongRow({
song,
songs,
index,
showDownload = false,
}: {
song: Track
songs: Track[]
index: number
showDownload?: boolean
}) {
const { state, playTrack, togglePlay, addToQueue } = useMusicPlayer()
const isActive = state.currentTrack?.id === song.id
const isPlaying = isActive && state.isPlaying
const [confirmSwitch, setConfirmSwitch] = useState(false)
const handlePlay = () => {
if (isActive) {
togglePlay()
} else if (state.currentTrack && state.isPlaying) {
setConfirmSwitch(true)
} else {
playTrack(song, songs, index)
}
}
return (
<SwipeableRow onSwipeRight={() => addToQueue(song)}>
<div
className={`relative flex items-center gap-3 px-4 py-2.5 hover:bg-muted/50 transition-colors group ${
isActive ? 'bg-primary/5' : ''
}`}
>
{/* Confirm switch overlay */}
{confirmSwitch && (
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-background/95 backdrop-blur-sm rounded px-4">
<span className="text-sm font-medium truncate mr-auto">Switch song, DJ Cutoff?</span>
<button
onClick={() => { playTrack(song, songs, index); setConfirmSwitch(false) }}
className="px-3 py-1.5 text-sm font-medium bg-purple-600 text-white rounded-md hover:bg-purple-700 transition-colors"
>
Switch
</button>
<button
onClick={() => setConfirmSwitch(false)}
className="px-3 py-1.5 text-sm font-medium bg-muted hover:bg-muted/80 rounded-md transition-colors"
>
Cancel
</button>
</div>
)}
{/* Play button / track number */}
<button
onClick={handlePlay}
className="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-full hover:bg-muted transition-colors"
>
{isPlaying ? (
<Pause className="h-4 w-4 text-primary" />
) : (
<Play className="h-4 w-4 text-primary ml-0.5" />
)}
</button>
{/* Cover art */}
<div className="flex-shrink-0 w-10 h-10 rounded overflow-hidden bg-muted">
{song.coverArt ? (
<img
src={`/api/music/cover/${song.coverArt}?size=80`}
alt={song.album}
className="w-full h-full object-cover"
loading="lazy"
/>
) : (
<div className="w-full h-full bg-muted" />
)}
</div>
{/* Info */}
<div className="flex-1 min-w-0">
<div className={`text-sm font-medium truncate ${isActive ? 'text-primary' : ''}`}>
{song.title}
</div>
<div className="text-xs text-muted-foreground truncate">
{song.artist} &middot; {song.album}
</div>
</div>
{/* Duration */}
<span className="text-xs text-muted-foreground tabular-nums hidden sm:block">
{formatDuration(song.duration)}
</span>
{/* Download */}
{showDownload && <DownloadButton track={song} />}
{/* Add to queue (desktop hover) */}
<button
onClick={(e) => { e.stopPropagation(); addToQueue(song) }}
className="p-1.5 opacity-0 group-hover:opacity-100 hover:bg-muted/50 rounded transition-all"
title="Add to queue"
>
<ListPlus className="h-4 w-4 text-muted-foreground" />
</button>
</div>
</SwipeableRow>
)
}