import React, { useEffect, useState } from 'react'; import { View, Text, FlatList, TouchableOpacity, ActivityIndicator, Alert, KeyboardAvoidingView, Platform, Linking, } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import type { RootState, AppDispatch } from '../../store'; import { searchHuggingFaceModels, saveHuggingFaceApiKey, loadHuggingFaceApiKey, setSearchQuery, clearHFError, } from '../../store/modelsSlice'; import HFModelItem from './HFModelItem'; import ApiKeyEditor from './ApiKeyEditor'; import SearchSection from './SearchSection'; import { handleModelDownload } from './downloadHelper'; import { createStyles } from './styles'; import { useTheme } from '../../theme/ThemeProvider'; export default function HuggingFaceModelsScreen() { const { colors } = useTheme(); const styles = createStyles(colors); const dispatch = useDispatch(); const { huggingFaceModels, huggingFaceApiKey, searchQuery, isLoadingHF, hfError, modelsDirectory, downloadProgress, } = useSelector((state: RootState) => state.models); const [showApiKeyInput, setShowApiKeyInput] = useState(false); const [tempApiKey, setTempApiKey] = useState(''); const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery); useEffect(() => { // Load saved API key on mount dispatch(loadHuggingFaceApiKey()); }, [dispatch]); useEffect(() => { setTempApiKey(huggingFaceApiKey); }, [huggingFaceApiKey]); useEffect(() => { if (hfError) { Alert.alert('Erreur', hfError, [ { text: 'OK', onPress: () => dispatch(clearHFError()) }, ]); } }, [hfError, dispatch]); const handleSearch = () => { if (localSearchQuery.trim()) { dispatch(setSearchQuery(localSearchQuery.trim())); dispatch(searchHuggingFaceModels({ query: localSearchQuery.trim(), apiKey: huggingFaceApiKey || undefined, })); } }; const handleSaveApiKey = () => { dispatch(saveHuggingFaceApiKey(tempApiKey.trim())); setShowApiKeyInput(false); Alert.alert('Succès', 'Clé API enregistrée'); }; const handleOpenHuggingFace = () => { Linking.openURL('https://huggingface.co/settings/tokens'); }; const handleModelPress = (modelId: string) => { const url = `https://huggingface.co/${modelId}`; Linking.openURL(url); }; const handleDownload = (modelId: string) => { handleModelDownload(modelId, modelsDirectory, dispatch); }; const renderModelItem = ({ item }: { item: typeof huggingFaceModels[0] }) => { const modelProgress = downloadProgress[item.id]; const isDownloading = !!modelProgress; const progressPercent = modelProgress ? (modelProgress.bytesWritten / modelProgress.contentLength) * 100 : 0; return ( ); }; const renderEmptyList = () => { if (isLoadingHF) { return null; } return ( {searchQuery ? ( <> Aucun modèle trouvé Essayez une autre recherche ) : ( <> Recherchez des modèles GGUF Entrez un terme de recherche ci-dessus )} ); }; return ( {/* API Key Section */} Clé API HuggingFace ℹ️ Obtenir {showApiKeyInput ? ( { setTempApiKey(huggingFaceApiKey); setShowApiKeyInput(false); }} onSave={handleSaveApiKey} /> ) : ( {huggingFaceApiKey ? '•••••••••••••' : 'Non configurée'} setShowApiKeyInput(true)} > {huggingFaceApiKey ? 'Modifier' : 'Configurer'} )} Optionnel - Permet d'augmenter les limites de recherche {/* Search Section */} {/* Results Section */} Résultats ({huggingFaceModels.length}) {isLoadingHF && huggingFaceModels.length === 0 ? ( Recherche en cours... ) : ( item.id} ListEmptyComponent={renderEmptyList} contentContainerStyle={styles.listContent} /> )} ); }