'use client' import { useState, useEffect, useRef, useCallback } from 'react' import { useMusicPlayer, type RadioTrack } from '@/components/music/music-provider' import { GlobeLoader, type GlobePoint } from '@/components/globe/globe-loader' import { JefflixLogo } from '@/components/jefflix-logo' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { ScrollArea } from '@/components/ui/scroll-area' import { ArrowLeft, Search, Radio, Loader2, Play, MapPin, Globe2, X, } from 'lucide-react' import Link from 'next/link' import type { RadioPlace, RadioChannel } from '@/lib/radio' import { getPlaces, getChannels, resolveStreamUrl, searchRadio } from '@/lib/radio' export default function RadioPage() { const { state, playTrack } = useMusicPlayer() // Globe data const [places, setPlaces] = useState([]) const [loadingPlaces, setLoadingPlaces] = useState(true) const [placesError, setPlacesError] = useState('') // Selected place & channels const [selectedPlace, setSelectedPlace] = useState(null) const [channels, setChannels] = useState([]) const [loadingChannels, setLoadingChannels] = useState(false) // Search const [query, setQuery] = useState('') const [debouncedQuery, setDebouncedQuery] = useState('') const [searchResults, setSearchResults] = useState<{ stations: Array<{ id: string; title: string; placeId: string; placeTitle: string; country: string }> places: Array<{ id: string; title: string; country: string }> } | null>(null) const [searching, setSearching] = useState(false) const debounceRef = useRef(null) // Playing state const [resolvingId, setResolvingId] = useState(null) // Globe focus const [focusLat, setFocusLat] = useState(undefined) const [focusLng, setFocusLng] = useState(undefined) // Load places on mount useEffect(() => { getPlaces() .then((data) => { setPlaces(data) setLoadingPlaces(false) }) .catch((err) => { setPlacesError(err.message) setLoadingPlaces(false) }) }, []) // Debounce search useEffect(() => { if (debounceRef.current) clearTimeout(debounceRef.current) debounceRef.current = setTimeout(() => setDebouncedQuery(query), 300) return () => { if (debounceRef.current) clearTimeout(debounceRef.current) } }, [query]) // Execute search useEffect(() => { if (debouncedQuery.length < 2) { setSearchResults(null) return } setSearching(true) searchRadio(debouncedQuery) .then(setSearchResults) .catch(() => setSearchResults(null)) .finally(() => setSearching(false)) }, [debouncedQuery]) // Convert places to globe points const globePoints: GlobePoint[] = places.map((p) => ({ lat: p.lat, lng: p.lng, id: p.id, label: `${p.title}, ${p.country}`, color: '#f43f5e', // rose-500 size: p.size, })) // Handle globe point click const handlePointClick = useCallback((point: GlobePoint) => { const place = places.find((p) => p.id === point.id) if (!place) return setSelectedPlace(place) setFocusLat(place.lat) setFocusLng(place.lng) setLoadingChannels(true) getChannels(place.id) .then(setChannels) .catch(() => setChannels([])) .finally(() => setLoadingChannels(false)) }, [places]) // Handle search place click const handleSearchPlaceClick = useCallback((placeId: string, placeTitle: string, country: string) => { // Find in places array for geo data, or just load channels const place = places.find((p) => p.id === placeId) if (place) { setSelectedPlace(place) setFocusLat(place.lat) setFocusLng(place.lng) } else { setSelectedPlace({ id: placeId, title: placeTitle, country, lat: 0, lng: 0, size: 1 }) } setQuery('') setSearchResults(null) setLoadingChannels(true) getChannels(placeId) .then(setChannels) .catch(() => setChannels([])) .finally(() => setLoadingChannels(false)) }, [places]) // Play a station const handlePlayStation = useCallback(async (channel: RadioChannel) => { setResolvingId(channel.id) try { const streamUrl = await resolveStreamUrl(channel.id) const radioTrack: RadioTrack = { type: 'radio', id: `radio:${channel.id}`, title: channel.title, artist: `${channel.placeTitle}, ${channel.country}`, album: '', albumId: '', duration: 0, coverArt: '', streamUrl, } playTrack(radioTrack) } catch (err) { console.error('Failed to play station:', err) } finally { setResolvingId(null) } }, [playTrack]) // Play search station directly const handlePlaySearchStation = useCallback(async (station: { id: string; title: string; placeTitle: string; country: string }) => { setResolvingId(station.id) try { const streamUrl = await resolveStreamUrl(station.id) const radioTrack: RadioTrack = { type: 'radio', id: `radio:${station.id}`, title: station.title, artist: `${station.placeTitle}, ${station.country}`, album: '', albumId: '', duration: 0, coverArt: '', streamUrl, } playTrack(radioTrack) } catch (err) { console.error('Failed to play station:', err) } finally { setResolvingId(null) } }, [playTrack]) const isPlaying = (channelId: string) => state.currentTrack?.id === `radio:${channelId}` && state.isPlaying return (
{/* Header */}
{/* Hero */}

World Radio

Explore live radio stations from around the globe. Click a point on the globe or search for stations.

{/* Main content */}
{/* Globe */}
{loadingPlaces ? (
Loading {places.length > 0 ? `${places.length.toLocaleString()} stations` : 'stations'}...
) : placesError ? (

Failed to load stations: {placesError}

) : ( = 1024 ? 550 : 400} autoRotate={!selectedPlace} focusLat={focusLat} focusLng={focusLng} /> )}
{places.length > 0 && (

{places.length.toLocaleString()} locations worldwide

)}
{/* Station panel */}
{/* Search */}
setQuery(e.target.value)} placeholder="Search stations, cities, countries..." className="w-full pl-10 pr-10 py-2.5 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-rose-500/50" /> {query && ( )}
{/* Search results */} {searchResults && (

{searching ? 'Searching...' : `${searchResults.stations.length} stations, ${searchResults.places.length} places`}

{/* Station results */} {searchResults.stations.map((station) => ( ))} {/* Place results */} {searchResults.places.map((place) => ( ))}
)} {/* Selected place channels */} {selectedPlace && !searchResults && (

{selectedPlace.title}

{selectedPlace.country}

{loadingChannels ? (
) : channels.length === 0 ? (
No stations found in this area
) : ( {channels.map((channel) => ( ))} )}
)} {/* Empty state */} {!selectedPlace && !searchResults && (

Explore World Radio

Click a point on the globe to discover radio stations, or use the search bar to find specific stations and cities.

)}
) }