import type { SubsonicSong } from '../api/subsonicTypes'; import { songToTrack } from '../utils/playback/songToTrack'; import React, { memo } from 'react'; import { useNavigateToAlbum } from '../hooks/useNavigateToAlbum'; import { useNavigateToArtist } from '../hooks/useNavigateToArtist'; import { Play, ListPlus } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '../store/playerStore'; import { enqueueAndPlay } from '../utils/playback/playSong'; import { useDragDrop } from '../contexts/DragDropContext'; import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior'; import { formatTrackTime } from '../utils/format/formatDuration'; import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs'; import { tooltipAttrs } from '@/ui/tooltipAttrs'; interface Props { song: SubsonicSong; showBpm?: boolean; } function SongRow({ song, showBpm }: Props) { const navigateToAlbum = useNavigateToAlbum(); const navigateToArtist = useNavigateToArtist(); const { t } = useTranslation(); const enqueue = usePlayerStore(s => s.enqueue); const openContextMenu = usePlayerStore(s => s.openContextMenu); const isCurrent = usePlayerStore(s => s.currentTrack?.id === song.id); const psyDrag = useDragDrop(); const { orbitActive, addTrackToOrbit } = useOrbitSongRowBehavior(); // In an orbit session both buttons collapse into the orbit-suggest / host-enqueue // path so we don't ship a queue replacement to every guest. const handlePlay = () => { if (orbitActive) { addTrackToOrbit(song.id); return; } enqueueAndPlay(song); }; const handleEnqueue = () => { if (orbitActive) { addTrackToOrbit(song.id); return; } enqueue([songToTrack(song)]); }; const artistRefs = resolveTrackArtistRefs(song); const bpmTooltip = song.localBpmSource === 'analysis' ? t('search.bpmSourceAnalysis') : song.localBpmSource === 'tag' ? t('search.bpmSourceTag') : undefined; return (