import React, { memo, useCallback } from "react"; import { Card } from "@/components/ui/Card"; import { Play, Pause, Volume2, ListPlus, Plus } from "lucide-react"; import { cn } from "@/utils/cn"; import type { Track, Album, AlbumSource } from "../types"; interface TrackListProps { tracks: Track[]; album: Album; source: AlbumSource; currentTrackId: string | undefined; colors: any; onPlayTrack: (track: Track, index: number) => void; onAddToQueue: (track: Track) => void; onAddToPlaylist: (trackId: string) => void; previewTrack: string | null; previewPlaying: boolean; onPreview: (track: Track, e: React.MouseEvent) => void; } interface TrackRowProps { track: Track; index: number; album: Album; isOwned: boolean; isPlaying: boolean; isPreviewPlaying: boolean; colors: any; onPlayTrack: (track: Track, index: number) => void; onAddToQueue: (track: Track) => void; onAddToPlaylist: (trackId: string) => void; onPreview: (track: Track, e: React.MouseEvent) => void; } const formatDuration = (seconds: number) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, "0")}`; }; const formatNumber = (num: number) => { if (num >= 1000000) { return `${(num / 1000000).toFixed(1)}M`; } else if (num >= 1000) { return `${(num / 1000).toFixed(1)}K`; } return num.toString(); }; const TrackRow = memo( function TrackRow({ track, index, album, isOwned, isPlaying, isPreviewPlaying, colors, onPlayTrack, onAddToQueue, onAddToPlaylist, onPreview, }: TrackRowProps) { const isPreviewOnly = !isOwned; const handleAddToQueue = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); onAddToQueue(track); }, [track, onAddToQueue] ); const handleAddToPlaylist = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); onAddToPlaylist(track.id); }, [track.id, onAddToPlaylist] ); const handlePreview = useCallback( (e: React.MouseEvent) => { onPreview(track, e); }, [track, onPreview] ); const handlePlayTrack = useCallback(() => { onPlayTrack(track, index); }, [track, index, onPlayTrack]); const handleRowClick = useCallback( (e: React.MouseEvent) => { // For unowned tracks, play preview instead of local file if (isPreviewOnly) { onPreview(track, e); } else { onPlayTrack(track, index); } }, [isPreviewOnly, track, index, onPlayTrack, onPreview] ); return (
{ if (e.key === "Enter") { e.preventDefault(); if (isPreviewOnly) { onPreview(track, e as unknown as React.MouseEvent); } else { handlePlayTrack(); } } }} >
{index + 1}
{track.displayTitle ?? track.title} {isPreviewOnly && ( PREVIEW )}
{track.artist?.name && track.artist.name !== album.artist?.name && (
{track.artist.name}
)}
{isOwned && track.playCount !== undefined && track.playCount > 0 && (
{formatNumber(track.playCount)}
)} {isOwned && ( <> )} {isPreviewOnly && ( )} {track.duration && (
{formatDuration(track.duration)}
)}
); }, (prevProps, nextProps) => { return ( prevProps.track.id === nextProps.track.id && prevProps.isPlaying === nextProps.isPlaying && prevProps.isPreviewPlaying === nextProps.isPreviewPlaying && prevProps.index === nextProps.index && prevProps.isOwned === nextProps.isOwned ); } ); export const TrackList = memo(function TrackList({ tracks, album, source, currentTrackId, colors, onPlayTrack, onAddToQueue, onAddToPlaylist, previewTrack, previewPlaying, onPreview, }: TrackListProps) { const isOwned = source === "library"; return (
{tracks.map((track, index) => { const isPlaying = currentTrackId === track.id; const isPreviewPlaying = previewTrack === track.id && previewPlaying; return ( ); })}
); });