music-app/mobile/app/library.tsx
Jarian Cottingham 7e09799859 fix: batch fix all issues
- Add auth middleware (API key) to protect API routes (#5, #7)
- Add WebSocket handlers and cleanup on disconnect (#3, #8)
- Add web Dockerfile (#1)
- Fix memory upload with streaming chunks (#10)
- Move lofi seed to startup, remove per-request seeding (#9)
- Document ffmpeg dependency in README and .env.example (#6)
- Fill in mobile app with API-connected UI (#4)
- Set GENIUS_API_KEY from env var with documentation (#2)
2026-07-05 21:36:59 +00:00

69 lines
2.5 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 LibraryScreen() {
const router = useRouter()
const [search, setSearch] = useState('')
const [playlists, setPlaylists] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchPlaylists()
}, [])
const fetchPlaylists = async () => {
try {
const res = await fetch('/api/playlists')
const data = await res.json()
setPlaylists(data.items || data || [])
} catch {
setPlaylists([])
} finally {
setLoading(false)
}
}
const filtered = playlists.filter(p => p.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">Library</Text>
<View className="flex-row items-center bg-music-card rounded-full px-4 py-2">
<Text className="text-music-muted">🔍</Text>
<TextInput
placeholder="Search playlists..."
placeholderTextColor="#888"
value={search}
onChangeText={setSearch}
className="text-sm text-music-text ml-2 w-48"
/>
</View>
</View>
{loading ? (
<View className="items-center py-8">
<ActivityIndicator size="large" color="#e94560" />
</View>
) : (
<View className="flex-wrap flex-row gap-6 justify-center">
{filtered.map((playlist: any) => (
<TouchableOpacity key={playlist.id} onPress={() => router.push(`/playlist?id=${playlist.id}` as any)} className="items-center">
<View className="w-24 h-24 rounded-full bg-music-vinyl mb-2" />
<Text className="text-sm text-music-text">{playlist.name}</Text>
</TouchableOpacity>
))}
</View>
)}
</ScrollView>
<BottomNavBar />
</View>
)
}