Skip to content

Commit fdca1ab

Browse files
authored
feat: Enhance ArtistInfo component with album selection and download options (#493)
1 parent 3d8ff2c commit fdca1ab

File tree

1 file changed

+49
-4
lines changed

1 file changed

+49
-4
lines changed

frontend/src/components/ArtistInfo.tsx

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useState, useMemo } from "react";
1414
import { Checkbox } from "@/components/ui/checkbox";
1515
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
1616
import { ScrollArea } from "@/components/ui/scroll-area";
17+
1718
interface ArtistInfoProps {
1819
artistInfo: {
1920
name: string;
@@ -93,12 +94,14 @@ interface ArtistInfoProps {
9394
onTrackClick?: (track: TrackMetadata) => void;
9495
onBack?: () => void;
9596
}
97+
9698
export 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

Comments
 (0)