import { Mic, Sparkles, User2, Users, Volume2 } from 'lucide-react-native'; import { useMemo, useState } from 'react'; import { ActivityIndicator, FlatList, Pressable, RefreshControl, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { type VoiceProfileResponse } from '@/lib/api'; import { colors } from '@/lib/colors'; import { useProfiles } from '@/lib/hooks'; export default function VoicesTab() { const profiles = useProfiles(); const [query, setQuery] = useState(''); const [expandedId, setExpandedId] = useState(null); const filtered = useMemo(() => { const items = profiles.data ?? []; if (!query.trim()) return items; const q = query.trim().toLowerCase(); return items.filter( (p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.language.toLowerCase().includes(q), ); }, [profiles.data, query]); return ( Voices {profiles.data ? ( {profiles.data.length} {profiles.data.length === 1 ? 'voice' : 'voices'} ) : null} p.id} contentContainerStyle={{ paddingHorizontal: 24, paddingBottom: 80 }} ItemSeparatorComponent={() => } refreshControl={ profiles.refetch()} tintColor={colors.accent} /> } ListEmptyComponent={ profiles.isLoading ? ( ) : query ? ( No voices match "{query}". ) : ( ) } renderItem={({ item }) => ( setExpandedId(expandedId === item.id ? null : item.id)} /> )} /> ); } function ProfileRow({ profile, expanded, onPress, }: { profile: VoiceProfileResponse; expanded: boolean; onPress: () => void; }) { const kindIcon = profile.voice_type === 'preset' ? Sparkles : profile.voice_type === 'designed' ? User2 : Mic; const KindIcon = kindIcon; return ( {profile.name} {profile.language} {profile.generation_count} gens {profile.voice_type === 'cloned' ? ` · ${profile.sample_count} samples` : ''} {expanded && profile.description ? ( {profile.description} {profile.default_engine ? ( engine · {profile.default_engine} ) : null} ) : null} ); } function KindBadge({ kind }: { kind: VoiceProfileResponse['voice_type'] }) { const label = kind === 'preset' ? 'PRESET' : kind === 'designed' ? 'DESIGNED' : 'CLONED'; return ( {label} ); } function EmptyState() { return ( No voices yet Create voice profiles in your Voicebox desktop — they'll appear here automatically. ); }