import React from 'react'; import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import type { ColDef } from '@/utils/useTracklistColumns'; import type { SubsonicSong } from '@/lib/api/subsonicTypes'; import type { Track } from '@/features/playback/store/playerStoreTypes'; import { songToTrack } from '@/features/playback/utils/playback/songToTrack'; import { useSelectionStore } from '@/store/selectionStore'; import { useThemeStore } from '@/store/themeStore'; import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore'; import StarRating from '@/components/StarRating'; import { codecLabel, type ColKey } from '@/features/album/utils/albumTrackListHelpers'; import { formatLongDuration } from '@/lib/format/formatDuration'; import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers'; import i18n from '@/lib/i18n'; import { offlineActionPolicy, type OfflineActionPolicy } from '@/features/offline'; import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs'; type ContextMenuFn = ( x: number, y: number, track: Track, type: 'song' | 'album' | 'artist' | 'queue-item' | 'album-song', ) => void; interface TrackRowProps { song: SubsonicSong; globalIdx: number; visibleCols: readonly ColDef[]; gridStyle: React.CSSProperties; currentTrackId: string | null; isPlaying: boolean; ratingValue: number; isStarred: boolean; inSelectMode: boolean; isContextMenuSong: boolean; onPlaySong: (song: SubsonicSong) => void; onDoubleClickSong?: (song: SubsonicSong) => void; onRate: (songId: string, rating: number) => void; onToggleSongStar: (song: SubsonicSong, e: React.MouseEvent) => void; onContextMenu: ContextMenuFn; onToggleSelect: (id: string, globalIdx: number, shift: boolean) => void; onDragStart: (song: SubsonicSong, me: MouseEvent) => void; setContextMenuSongId: (id: string | null) => void; actionPolicy?: OfflineActionPolicy; } /** * Memoised tracklist row. Subscribes to its own selection + preview state * via primitive selectors so only this row re-renders when the user * toggles selection or starts/stops a preview. */ export const TrackRow = React.memo(function TrackRow({ song, globalIdx, visibleCols, gridStyle, currentTrackId, isPlaying, ratingValue, isStarred, inSelectMode, isContextMenuSong, onPlaySong, onDoubleClickSong, onRate, onToggleSongStar, onContextMenu, onToggleSelect, onDragStart, setContextMenuSongId, actionPolicy, }: TrackRowProps) { const policy = actionPolicy ?? offlineActionPolicy('trackRow', false); const { t } = useTranslation(); const navigate = useNavigate(); const showBitrate = useThemeStore(s => s.showBitrate); const isSelected = useSelectionStore(s => s.selectedIds.has(song.id)); const isActive = currentTrackId === song.id; const isPreviewing = usePreviewStore(s => s.previewingId === song.id); const isPreviewAudioStarted = usePreviewStore(s => s.previewingId === song.id && s.audioStarted); const renderCell = (colDef: ColDef) => { const key = colDef.key as ColKey; switch (key) { case 'num': return (