Jarian Cottingham 404fe78748 Initial commit: mobile app
React Native app for the self-hosted music streaming project,
carved out from the music-app monorepo. Uses @music-app/shared
for shared types and utilities.
2026-08-21 18:44:05 +00:00

93 lines
3.4 KiB
TypeScript

import { View, Text, ScrollView, TouchableOpacity, TextInput, ActivityIndicator } from 'react-native'
import { useRouter } from 'expo-router'
import { BottomNavBar } from '../src/components/BottomNavBar'
import { useState, useEffect } from 'react'
export default function RadioScreen() {
const router = useRouter()
const [stations, setStations] = useState([])
const [loading, setLoading] = useState(true)
const [search, setSearch] = useState('')
useEffect(() => {
fetchStations()
}, [])
const fetchStations = async () => {
try {
const res = await fetch('/api/radio/stations?limit=20')
const data = await res.json()
setStations(data.items || data || [])
} catch {
setStations([])
} finally {
setLoading(false)
}
}
const filtered = stations.filter(s => s.name?.toLowerCase().includes(search.toLowerCase()))
return (
<View className="flex-1 bg-music-black">
<ScrollView className="flex-1 px-4 pt-4">
<View className="flex-row items-center justify-between mb-6">
<TouchableOpacity onPress={() => router.push('/account' as any)} className="w-10 h-10 rounded-full bg-music-card items-center justify-center">
<Text className="text-music-muted">👤</Text>
</TouchableOpacity>
<Text className="text-xl font-semibold text-music-text">Internet Radio</Text>
<View className="w-10" />
</View>
<View className="flex-row items-center bg-music-card rounded-full px-4 py-3 mb-6">
<Text className="text-music-muted">🔍</Text>
<TextInput
placeholder="Search stations..."
placeholderTextColor="#888"
value={search}
onChangeText={setSearch}
className="text-sm text-music-text ml-2 flex-1"
/>
</View>
{loading ? (
<View className="items-center py-8">
<ActivityIndicator size="large" color="#e94560" />
<Text className="text-music-muted mt-2">Loading stations...</Text>
</View>
) : (
<>
<View className="mb-6 p-4 rounded-2xl bg-music-card">
<Text className="text-sm font-semibold text-music-text mb-3">Browse Stations</Text>
<View className="flex-wrap flex-row gap-4">
{filtered.map((station: any) => (
<TouchableOpacity
key={station.id}
className="w-32 p-3 rounded-xl bg-music-dark items-center"
>
<View className="w-16 h-16 rounded-full bg-music-vinyl mb-2 items-center justify-center">
<Text>📻</Text>
</View>
<Text className="text-xs text-music-text text-center" numberOfLines={2}>
{station.name || 'Station'}
</Text>
<Text className="text-xs text-music-muted mt-1">
{station.country_code || 'Local'}
</Text>
</TouchableOpacity>
))}
</View>
</View>
{filtered.length === 0 && (
<View className="items-center py-8">
<Text className="text-3xl mb-2">📻</Text>
<Text className="text-music-muted">No stations found</Text>
</View>
)}
</>
)}
</ScrollView>
<BottomNavBar />
</View>
)
}