@@ -14,6 +14,7 @@ import { useState, useMemo } from "react";
1414import { Checkbox } from "@/components/ui/checkbox" ;
1515import { Dialog , DialogContent , DialogHeader , DialogTitle , DialogTrigger } from "@/components/ui/dialog" ;
1616import { ScrollArea } from "@/components/ui/scroll-area" ;
17+
1718interface ArtistInfoProps {
1819 artistInfo : {
1920 name : string ;
@@ -93,12 +94,14 @@ interface ArtistInfoProps {
9394 onTrackClick ?: ( track : TrackMetadata ) => void ;
9495 onBack ?: ( ) => void ;
9596}
97+
9698export function ArtistInfo ( { artistInfo, albumList, trackList, searchQuery, sortBy, selectedTracks, downloadedTracks, failedTracks, skippedTracks, downloadingTrack, isDownloading, bulkDownloadType, downloadProgress, currentDownloadInfo, currentPage, itemsPerPage, downloadedLyrics, failedLyrics, skippedLyrics, downloadingLyricsTrack, checkingAvailabilityTrack, availabilityMap, downloadedCovers, failedCovers, skippedCovers, downloadingCoverTrack, isBulkDownloadingCovers, isBulkDownloadingLyrics, onSearchChange, onSortChange, onToggleTrack, onToggleSelectAll, onDownloadTrack, onDownloadLyrics, onDownloadCover, onCheckAvailability, onDownloadAllLyrics, onDownloadAllCovers, onDownloadAll, onDownloadSelected, onStopDownload, onOpenFolder, onAlbumClick, onArtistClick, onPageChange, onTrackClick, onBack, } : ArtistInfoProps ) {
9799 const [ downloadingHeader , setDownloadingHeader ] = useState ( false ) ;
98100 const [ downloadingAvatar , setDownloadingAvatar ] = useState ( false ) ;
99101 const [ downloadingGalleryIndex , setDownloadingGalleryIndex ] = useState < number | null > ( null ) ;
100102 const [ downloadingAllGallery , setDownloadingAllGallery ] = useState ( false ) ;
101103 const [ activeTab , setActiveTab ] = useState < "albums" | "tracks" | "gallery" > ( "albums" ) ;
104+
102105 const filteredAlbumGroups = useMemo ( ( ) => {
103106 const albumTypeMap = new Map ( albumList . map ( a => [ a . name , a . album_type ] ) ) ;
104107 const albumGroups = trackList . reduce ( ( acc , track ) => {
@@ -125,6 +128,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
125128 return dateB . localeCompare ( dateA ) ;
126129 } ) ;
127130 } , [ trackList , albumList ] ) ;
131+
128132 const handleDownloadHeader = async ( ) => {
129133 if ( ! artistInfo . header )
130134 return ;
@@ -155,6 +159,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
155159 setDownloadingHeader ( false ) ;
156160 }
157161 } ;
162+
158163 const handleDownloadAvatar = async ( ) => {
159164 if ( ! artistInfo . images )
160165 return ;
@@ -185,6 +190,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
185190 setDownloadingAvatar ( false ) ;
186191 }
187192 } ;
193+
188194 const handleDownloadGalleryImage = async ( imageUrl : string , index : number ) => {
189195 setDownloadingGalleryIndex ( index ) ;
190196 try {
@@ -214,6 +220,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
214220 setDownloadingGalleryIndex ( null ) ;
215221 }
216222 } ;
223+
217224 const handleDownloadAllGallery = async ( ) => {
218225 if ( ! artistInfo . gallery || artistInfo . gallery . length === 0 )
219226 return ;
@@ -270,7 +277,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
270277 setDownloadingAllGallery ( false ) ;
271278 }
272279 } ;
280+
273281 const hasGallery = artistInfo . gallery && artistInfo . gallery . length > 0 ;
282+
274283 return ( < div className = "space-y-6" >
275284 < Card className = "overflow-hidden p-0 relative" >
276285 { artistInfo . header ? ( < >
@@ -446,14 +455,49 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
446455 </ div > ) }
447456
448457 { activeTab === "albums" && albumList . length > 0 && ( < div className = "space-y-4" >
449- < h3 className = "text-2xl font-bold" > Discography</ h3 >
458+ < div className = "flex items-center justify-between flex-wrap gap-2" >
459+ < h3 className = "text-2xl font-bold" > Discography</ h3 >
460+ < div className = "flex gap-2" >
461+ < Button onClick = { onDownloadAll } size = "sm" disabled = { isDownloading } >
462+ { isDownloading && bulkDownloadType === "all" ? ( < Spinner /> ) : ( < Download className = "h-4 w-4" /> ) }
463+ Download Discography
464+ </ Button >
465+ { selectedTracks . length > 0 && (
466+ < Button onClick = { onDownloadSelected } size = "sm" variant = "secondary" disabled = { isDownloading } >
467+ { isDownloading && bulkDownloadType === "selected" ? ( < Spinner /> ) : ( < Download className = "h-4 w-4" /> ) }
468+ Download Selected ({ selectedTracks . length } )
469+ </ Button >
470+ ) }
471+ </ div >
472+ </ div >
450473 < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4" >
451- { albumList . map ( ( album ) => ( < div key = { album . id } className = "group cursor-pointer" onClick = { ( ) => onAlbumClick ( {
474+ { albumList . map ( ( album ) => {
475+ // Filter tracks for this specific album to handle selection
476+ const albumTracks = trackList . filter ( t => t . album_name === album . name ) ;
477+ const tracksWithId = albumTracks . filter ( t => t . spotify_id ) ;
478+ // Check if all tracks in this album are currently selected
479+ const isSelected = tracksWithId . length > 0 && tracksWithId . every ( t => selectedTracks . includes ( t . spotify_id ! ) ) ;
480+ const hasTracks = tracksWithId . length > 0 ;
481+
482+ return ( < div key = { album . id } className = "group cursor-pointer relative" onClick = { ( ) => onAlbumClick ( {
452483 id : album . id ,
453484 name : album . name ,
454485 external_urls : album . external_urls ,
455486 } ) } >
456487 < div className = "relative mb-2" >
488+ { /* Checkbox for Album Selection */ }
489+ { hasTracks && (
490+ < div
491+ className = { `absolute top-2 left-2 z-20 ${ isSelected ? 'opacity-100' : 'opacity-0 group-hover:opacity-100' } transition-opacity` }
492+ onClick = { ( e ) => e . stopPropagation ( ) }
493+ >
494+ < Checkbox
495+ checked = { isSelected }
496+ onCheckedChange = { ( ) => onToggleSelectAll ( albumTracks ) }
497+ className = "bg-black/50 border-white/70 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
498+ />
499+ </ div >
500+ ) }
457501 { album . images && ( < img src = { album . images } alt = { album . name } className = "w-full aspect-square object-cover rounded-md shadow-md transition-shadow group-hover:shadow-xl" /> ) }
458502 < div className = "absolute bottom-2 right-2" >
459503 < span className = "text-[10px] uppercase font-bold px-1.5 py-0.5 rounded bg-black/60 text-white backdrop-blur-[2px]" >
@@ -469,7 +513,8 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
469513 < span > { album . total_tracks } { album . total_tracks === 1 ? "track" : "tracks" } </ span >
470514 </ > ) }
471515 </ div >
472- </ div > ) ) }
516+ </ div > ) ;
517+ } ) }
473518 </ div >
474519 </ div > ) }
475520
@@ -554,4 +599,4 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
554599 < TrackList tracks = { trackList } searchQuery = { searchQuery } sortBy = { sortBy } selectedTracks = { selectedTracks } downloadedTracks = { downloadedTracks } failedTracks = { failedTracks } skippedTracks = { skippedTracks } downloadingTrack = { downloadingTrack } isDownloading = { isDownloading } currentPage = { currentPage } itemsPerPage = { itemsPerPage } showCheckboxes = { true } hideAlbumColumn = { false } folderName = { artistInfo . name } isArtistDiscography = { true } downloadedLyrics = { downloadedLyrics } failedLyrics = { failedLyrics } skippedLyrics = { skippedLyrics } downloadingLyricsTrack = { downloadingLyricsTrack } checkingAvailabilityTrack = { checkingAvailabilityTrack } availabilityMap = { availabilityMap } onToggleTrack = { onToggleTrack } onToggleSelectAll = { onToggleSelectAll } onDownloadTrack = { onDownloadTrack } onDownloadLyrics = { onDownloadLyrics } onDownloadCover = { onDownloadCover } downloadedCovers = { downloadedCovers } failedCovers = { failedCovers } skippedCovers = { skippedCovers } downloadingCoverTrack = { downloadingCoverTrack } onCheckAvailability = { onCheckAvailability } onPageChange = { onPageChange } onAlbumClick = { onAlbumClick } onArtistClick = { onArtistClick } onTrackClick = { onTrackClick } />
555600 </ div > ) }
556601 </ div > ) ;
557- }
602+ }
0 commit comments