import { useState, useEffect, useRef } from 'react' import { useSearchParams } from 'react-router-dom' import { Compass, Sparkles, Loader2, ListMusic, Search, Users, Clock, Disc3, TrendingUp } from 'lucide-react' import { useAuth } from '../lib/auth' import { getPlaylists, generateRecommendations, toggleSaveRecommendation, dislikeRecommendation, type PlaylistResponse, type RecommendationItem } from '../lib/api' import RecommendationCard from '../components/RecommendationCard' const DISCOVERY_MODES = [ { id: 'discover', label: 'Discover', icon: Compass, description: 'General recommendations' }, { id: 'sonic_twin', label: 'Sonic Twin', icon: Users, description: 'Underground artists who sound like your favorites' }, { id: 'era_bridge', label: 'Era Bridge', icon: Clock, description: 'Classic artists who inspired your favorites' }, { id: 'deep_cuts', label: 'Deep Cuts', icon: Disc3, description: 'B-sides and rarities from artists you know' }, { id: 'rising', label: 'Rising', icon: TrendingUp, description: 'Under 50K listeners who fit your profile' }, ] as const const ADVENTUROUSNESS_LABELS: Record = { 1: 'Safe', 2: 'Familiar', 3: 'Balanced', 4: 'Exploring', 5: 'Adventurous', } export default function Discover() { const { user } = useAuth() const [searchParams] = useSearchParams() const [playlists, setPlaylists] = useState([]) const [selectedPlaylist, setSelectedPlaylist] = useState('') const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [remaining, setRemaining] = useState(null) const [discovering, setDiscovering] = useState(false) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [bandcampMode, setBandcampMode] = useState(false) const [savingIds, setSavingIds] = useState>(new Set()) const [dislikingIds, setDislikingIds] = useState>(new Set()) const [mode, setMode] = useState('discover') const [adventurousness, setAdventurousness] = useState(3) const [excludeGenres, setExcludeGenres] = useState('') const [count, setCount] = useState(5) const resultsRef = useRef(null) useEffect(() => { const load = async () => { try { const data = await getPlaylists() setPlaylists(data) const preselected = searchParams.get('playlist') if (preselected && data.some((p) => p.id === preselected)) { setSelectedPlaylist(preselected) } } catch { // silent } finally { setLoading(false) } } load() }, [searchParams]) const handleDiscover = async () => { if (!selectedPlaylist && !query.trim()) return setDiscovering(true) setError('') setResults([]) try { const response = await generateRecommendations( selectedPlaylist || undefined, query.trim() || undefined, bandcampMode, mode, adventurousness, excludeGenres.trim() || undefined, count, ) const recs = response.recommendations || [] setResults(recs) setRemaining(response.remaining_this_week ?? null) // Scroll to results after render if (recs.length > 0) { setTimeout(() => resultsRef.current?.scrollIntoView({ behavior: 'smooth' }), 100) } } catch (err: any) { setError( err.response?.data?.detail || 'Failed to generate recommendations. Please try again.' ) } finally { setDiscovering(false) } } const handleToggleSave = async (id: string) => { const sid = String(id) setSavingIds((prev) => new Set(prev).add(sid)) try { const { saved } = await toggleSaveRecommendation(sid) setResults((prev) => prev.map((r) => (String(r.id) === sid ? { ...r, saved } : r)) ) } catch { // silent } finally { setSavingIds((prev) => { const next = new Set(prev) next.delete(sid) return next }) } } const handleDislike = async (id: string) => { const sid = String(id) setDislikingIds((prev) => new Set(prev).add(sid)) try { const { disliked } = await dislikeRecommendation(sid) setResults((prev) => prev.map((r) => (String(r.id) === sid ? { ...r, disliked } : r)) ) } catch { // silent } finally { setDislikingIds((prev) => { const next = new Set(prev) next.delete(sid) return next }) } } if (loading) { return (
) } return (

Discover

Find new music based on your taste or a specific vibe

{/* Discovery Modes */}
{DISCOVERY_MODES.map(({ id, label, icon: Icon, description }) => ( ))}
{/* Discovery Form */}
{/* Playlist Selector */}
{/* Custom Query */}