import { Row } from '@olly/modern-sk'; import { useTranslation } from 'react-i18next'; import { TrackContextMenu } from './TrackContextMenu'; import { AvailabilityBadge } from './AvailabilityBadge'; import { MetadataStatusBadge } from './MetadataStatusBadge'; import { Icon } from '../common/Icon'; import { PlayingIndicator } from '../common/PlayingIndicator'; import { formatDuration } from '../../lib/format'; import { useAppDispatch, useAppSelector } from '../../hooks/useAppDispatch'; import { useIsOffline } from '../../hooks/useConnectionStatus'; import { useStreamCached } from '../../hooks/useStreamCached'; import { playNow } from '../../store/slices/queue'; import type { Track } from '../../api/types'; import { getCoverUrl, getTrackCoverUrl } from '../../api/endpoints/streaming'; interface Props { track: Track; index?: number; showAlbum?: boolean; /** Hide cover art and show the track's album position instead — used on * the album detail page, where the album cover is already shown once in * the header and per-track art would be redundant. */ hideArt?: boolean; onAddToPlaylist?: (track: Track) => void; onEditMetadata?: (track: Track) => void; onDelete?: (track: Track) => void; } export function TrackRow({ track, index, showAlbum = false, hideArt = false, onAddToPlaylist, onEditMetadata, onDelete, }: Props) { const { t } = useTranslation(); const dispatch = useAppDispatch(); const currentTrackId = useAppSelector((s) => s.player.currentTrackId); const isPlaying = useAppSelector((s) => s.player.isPlaying); const token = useAppSelector((s) => s.auth.accessToken); const isActive = currentTrackId === track.id; // Prefer an explicit album art URL; otherwise serve the track's own cover // (needs the token in the query string — `` can't send a header). const artUrl = getCoverUrl(track.albumArtUrl) ?? (token ? getTrackCoverUrl(track.id, token, track.hasCover) : undefined); // The backend reports `server`, but if it's unreachable and this track's // audio is already in the offline cache, show "Local" instead. const offline = useIsOffline(); const cached = useStreamCached(offline ? track.id : undefined); const displayAvailability = track.availability === 'server' && offline && cached ? 'local' : track.availability; const handlePlayNow = () => { dispatch( playNow({ trackId: track.id, title: track.title, artistName: track.artistName, albumTitle: track.albumTitle, durationMs: track.durationMs, albumArtUrl: track.albumArtUrl, }), ); }; return ( {!hideArt && ( {isActive && isPlaying ? '▶' : index !== undefined ? index + 1 : ''} )}
{hideArt ? (
{track.trackNumber ?? (index !== undefined ? index + 1 : '')}
) : artUrl ? ( ) : (
)} {isActive && (
)}
{track.title}
{track.artistName} {showAlbum && ` · ${track.albumTitle}`}
{formatDuration(track.durationMs)}
); }