music-app/mobile/app/playlist.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

86 lines
3.4 KiB
TypeScript

import { View, Text, ScrollView, TouchableOpacity, FlatList, ActivityIndicator } from 'react-native'
import { useRouter } from 'expo-router'
import { usePlayerStore } from '../src/store/playerStore'
import { BottomNavBar } from '../src/components/BottomNavBar'
import { useState, useEffect } from 'react'
export default function PlaylistScreen() {
const router = useRouter()
const isPlaying = usePlayerStore(s => s.isPlaying)
const togglePlay = usePlayerStore(s => s.togglePlay)
const [songs, setSongs] = useState([])
const [playlists, setPlaylists] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchData()
}, [])
const fetchData = async () => {
try {
const [plRes, songRes] = await Promise.all([
fetch('/api/playlists'),
fetch('/api/songs?page=1&per_page=50'),
])
const plData = await plRes.json()
const songData = await songRes.json()
setPlaylists(plData.items || plData || [])
setSongs(songData.items || [])
} catch {
setPlaylists([])
setSongs([])
} finally {
setLoading(false)
}
}
return (
<View className="flex-1 bg-music-black flex-row">
<View className="w-20 bg-music-dark border-r border-music-border items-center py-4 gap-3">
<TouchableOpacity onPress={() => router.push('/library' as any)}>
<Text className="text-music-muted text-xl"></Text>
</TouchableOpacity>
{playlists.map((pl: any, i: number) => (
<TouchableOpacity key={pl.id || i} className={`w-14 h-14 rounded-lg bg-music-card ${i === 0 ? 'border-2 border-music-accent' : ''}`} />
))}
<View className="flex-1 w-1 bg-music-border rounded-full relative">
<View className="absolute bottom-0 w-full bg-music-accent rounded-full" style={{ height: '35%' }} />
</View>
<Text className="text-music-muted text-sm"></Text>
<TouchableOpacity onPress={togglePlay}>
<Text className="text-music-accent text-xl">{isPlaying ? '⏸' : '▶️'}</Text>
</TouchableOpacity>
</View>
<ScrollView className="flex-1 px-6 py-4">
<View className="flex-row items-center justify-between mb-4">
<Text className="text-2xl font-semibold text-music-text">{playlists[0]?.name || 'Playlist'}</Text>
<Text className="text-music-muted">📢</Text>
</View>
<View className="w-48 h-48 rounded-xl bg-music-card mb-6" />
{loading ? (
<ActivityIndicator size="large" color="#e94560" />
) : (
songs.map((song: any) => (
<TouchableOpacity
key={song.id}
className="flex-row items-center gap-3 py-2"
onPress={() => usePlayerStore.getState().setSong(song)}
>
<View className="w-8 h-8 rounded-full bg-music-vinyl items-center justify-center">
<Text>🎵</Text>
</View>
<Text className="flex-1 text-sm text-music-text">{song.title}</Text>
<Text className="text-xs text-music-muted">{song.artist}</Text>
<Text className="text-xs text-music-muted">
{song.duration_sec ? `${Math.floor(song.duration_sec / 60)}:${(song.duration_sec % 60).toString().padStart(2, '0')}` : '--:--'}
</Text>
</TouchableOpacity>
))
)}
</ScrollView>
</View>
)
}