import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { libraryService } from '../services/library'; import { usePlayer } from '../context/PlayerContext'; import { Play, Shuffle, Heart, Disc, Music } from 'lucide-react'; import { Track } from '../types'; import { GENERATED_CONTENT } from '../data/seed_data'; interface ArtistData { name: string; photo?: string; topSongs: Track[]; albums: any[]; // Extended type needed singles: any[]; } export default function Artist() { const { id } = useParams(); // Start with name or id const navigate = useNavigate(); const { playTrack, toggleLike, likedTracks, setIsFullScreenOpen } = usePlayer(); const [artist, setArtist] = useState(null); const [loading, setLoading] = useState(true); // YouTube Music uses name-based IDs or channel IDs. // Our 'id' might be a name if clicked from Home. // If it's a UUID (from DB), we might need to look up. // For now, assume ID = Name or handle both. const artistName = decodeURIComponent(id || ''); useEffect(() => { if (!artistName) return; // OPTIMISTIC LOADING START // 1. Try to find in Seed Data first for instant header // Seed data keys are names, but IDs are "artist-Name" const seedArtist = Object.values(GENERATED_CONTENT).find( item => item.id === id || item.title === artistName || item.id === `artist-${artistName.replace(/ /g, '-')}` ); if (seedArtist) { setArtist({ name: seedArtist.title, photo: seedArtist.cover_url, topSongs: [], // Will load albums: [], singles: [] }); setLoading(false); // Show UI immediately! } else { setLoading(true); // Only blocking load if we have ZERO data } const fetchData = async () => { // Fetch info (Background) // If we already have photo from seed, maybe skip or update? // libraryService.getArtistInfo might find a better photo or same. // Parallel Fetch for speed const [info, songs] = await Promise.allSettled([ !seedArtist?.cover_url ? libraryService.getArtistInfo(artistName) : Promise.resolve({ photo: seedArtist.cover_url }), libraryService.search(artistName) ]); const finalPhoto = (info.status === 'fulfilled' && info.value?.photo) ? info.value.photo : seedArtist?.cover_url; let topSongs = (songs.status === 'fulfilled') ? songs.value : []; if (topSongs.length > 5) topSongs = topSongs.slice(0, 5); setArtist({ name: artistName, photo: finalPhoto, topSongs, albums: [], singles: [] }); setLoading(false); }; fetchData(); }, [artistName, id]); if (loading) return (
); if (!artist) return
Artist not found
; return (
{/* Header / Banner */}
{artist.photo && (
{artist.name}
)}

{artist.name}

{/* Content */}
{/* Top Songs */}

Top Songs

{artist.topSongs.length === 0 ? ( // Skeleton Loading for Songs [...Array(5)].map((_, i) => (
)) ) : ( artist.topSongs.map((track, i) => (
playTrack(track, artist.topSongs)} > {i + 1} Cover
{track.title}
{track.artist} • {track.album || 'Single'}
{Math.floor((track.duration || 0) / 60)}:{((track.duration || 0) % 60).toString().padStart(2, '0')}
)))}
{/* Albums (Mock UI for now as strict album search is hard with yt-dlp only) */}

Albums

{/* Placeholder Logic: Show top song covers as "Albums" for visual parity if no real albums */}
{artist.topSongs.slice(0, 5).map((track) => (
{ playTrack(track, [track]); }} >

{track.album || track.title}

Album
))}
{/* Singles */}

Singles

{artist.topSongs.slice(0, 4).reverse().map((track) => (
{ playTrack(track, [track]); }} >

{track.title}

Single
))}
); }