From fdca1ab461d600fd9b02df83ef3069afefbcc959 Mon Sep 17 00:00:00 2001 From: "Blake L." Date: Wed, 25 Feb 2026 01:22:42 -0600 Subject: [PATCH] feat: Enhance ArtistInfo component with album selection and download options (#493) --- frontend/src/components/ArtistInfo.tsx | 53 ++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/ArtistInfo.tsx b/frontend/src/components/ArtistInfo.tsx index f2aeda0..2398a3f 100644 --- a/frontend/src/components/ArtistInfo.tsx +++ b/frontend/src/components/ArtistInfo.tsx @@ -14,6 +14,7 @@ import { useState, useMemo } from "react"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { ScrollArea } from "@/components/ui/scroll-area"; + interface ArtistInfoProps { artistInfo: { name: string; @@ -93,12 +94,14 @@ interface ArtistInfoProps { onTrackClick?: (track: TrackMetadata) => void; onBack?: () => void; } + 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) { const [downloadingHeader, setDownloadingHeader] = useState(false); const [downloadingAvatar, setDownloadingAvatar] = useState(false); const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState(null); const [downloadingAllGallery, setDownloadingAllGallery] = useState(false); const [activeTab, setActiveTab] = useState<"albums" | "tracks" | "gallery">("albums"); + const filteredAlbumGroups = useMemo(() => { const albumTypeMap = new Map(albumList.map(a => [a.name, a.album_type])); const albumGroups = trackList.reduce((acc, track) => { @@ -125,6 +128,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort return dateB.localeCompare(dateA); }); }, [trackList, albumList]); + const handleDownloadHeader = async () => { if (!artistInfo.header) return; @@ -155,6 +159,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort setDownloadingHeader(false); } }; + const handleDownloadAvatar = async () => { if (!artistInfo.images) return; @@ -185,6 +190,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort setDownloadingAvatar(false); } }; + const handleDownloadGalleryImage = async (imageUrl: string, index: number) => { setDownloadingGalleryIndex(index); try { @@ -214,6 +220,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort setDownloadingGalleryIndex(null); } }; + const handleDownloadAllGallery = async () => { if (!artistInfo.gallery || artistInfo.gallery.length === 0) return; @@ -270,7 +277,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort setDownloadingAllGallery(false); } }; + const hasGallery = artistInfo.gallery && artistInfo.gallery.length > 0; + return (
{artistInfo.header ? (<> @@ -446,14 +455,49 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
)} {activeTab === "albums" && albumList.length > 0 && (
-

Discography

+
+

Discography

+
+ + {selectedTracks.length > 0 && ( + + )} +
+
- {albumList.map((album) => (
onAlbumClick({ + {albumList.map((album) => { + // Filter tracks for this specific album to handle selection + const albumTracks = trackList.filter(t => t.album_name === album.name); + const tracksWithId = albumTracks.filter(t => t.spotify_id); + // Check if all tracks in this album are currently selected + const isSelected = tracksWithId.length > 0 && tracksWithId.every(t => selectedTracks.includes(t.spotify_id!)); + const hasTracks = tracksWithId.length > 0; + + return (
onAlbumClick({ id: album.id, name: album.name, external_urls: album.external_urls, })}>
+ {/* Checkbox for Album Selection */} + {hasTracks && ( +
e.stopPropagation()} + > + onToggleSelectAll(albumTracks)} + className="bg-black/50 border-white/70 data-[state=checked]:bg-primary data-[state=checked]:border-primary" + /> +
+ )} {album.images && ({album.name})}
@@ -469,7 +513,8 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort {album.total_tracks} {album.total_tracks === 1 ? "track" : "tracks"} )}
-
))} +
); + })}
)} @@ -554,4 +599,4 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
)} ); -} +} \ No newline at end of file