import React, { memo } from 'react'; import { useNavigate } from 'react-router-dom'; import { Play, ListPlus } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { SubsonicSong, buildCoverArtUrl, coverArtCacheKey } from '../api/subsonic'; import { usePlayerStore, songToTrack } from '../store/playerStore'; import CachedImage from './CachedImage'; import { enqueueAndPlay } from '../utils/playSong'; import { useDragDrop } from '../contexts/DragDropContext'; interface SongCardProps { song: SubsonicSong; } function SongCard({ song }: SongCardProps) { const { t } = useTranslation(); const navigate = useNavigate(); const openContextMenu = usePlayerStore(s => s.openContextMenu); const enqueue = usePlayerStore(s => s.enqueue); const coverUrl = song.coverArt ? buildCoverArtUrl(song.coverArt, 200) : ''; const psyDrag = useDragDrop(); const handleClick = () => enqueueAndPlay(song); const handleArtistClick = (e: React.MouseEvent) => { if (!song.artistId) return; e.stopPropagation(); navigate(`/artist/${song.artistId}`); }; return (
e.key === 'Enter' && handleClick()} onContextMenu={(e) => { e.preventDefault(); openContextMenu(e.clientX, e.clientY, song, 'song'); }} onMouseDown={e => { if (e.button !== 0) return; const sx = e.clientX, sy = e.clientY; const onMove = (me: MouseEvent) => { if (Math.abs(me.clientX - sx) > 5 || Math.abs(me.clientY - sy) > 5) { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); psyDrag.startDrag( { data: JSON.stringify({ type: 'song', id: song.id, name: song.title }), label: song.title, coverUrl: coverUrl || undefined }, me.clientX, me.clientY, ); } }; const onUp = () => { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); }; document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); }} >
{coverUrl ? ( ) : (
)}

{song.title}

{song.artist}

); } export default memo(SongCard);