import React, { useState } from 'react'; import { Modal, View, Text, TextInput, ScrollView, Pressable, KeyboardAvoidingView, Platform, Alert, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Ionicons } from '@expo/vector-icons'; import { Button } from '../Button'; import { useBatchStore, BatchPrompt } from '~/store/batchStore'; import { useModelSelection } from '~/store/modelStore'; import { aspectRatios } from '~/hooks/useImageGeneration'; interface BatchGenerationModalProps { isOpen: boolean; onClose: () => void; onSuccess?: (batchId: string) => void; } export function BatchGenerationModal({ isOpen, onClose, onSuccess }: BatchGenerationModalProps) { const [batchName, setBatchName] = useState(''); const [prompts, setPrompts] = useState([{ text: '' }]); const [selectedAspectRatio, setSelectedAspectRatio] = useState(aspectRatios[0]); const [steps, setSteps] = useState(30); const [guidanceScale, setGuidanceScale] = useState(7.5); const { createBatch, isCreatingBatch } = useBatchStore(); const { models, selectedModel, setSelectedModel, loadModels, isLoading: modelsLoading } = useModelSelection(); // Models are preloaded at app start, only load if error occurred React.useEffect(() => { if (isOpen && models.length === 0 && !modelsLoading) { loadModels(); } }, [isOpen, models.length, modelsLoading]); const addPrompt = () => { if (prompts.length < 10) { setPrompts([...prompts, { text: '' }]); } }; const removePrompt = (index: number) => { if (prompts.length > 1) { setPrompts(prompts.filter((_, i) => i !== index)); } }; const updatePrompt = (index: number, text: string) => { const updated = [...prompts]; updated[index] = { ...updated[index], text }; setPrompts(updated); }; const handleSubmit = async () => { // Validate const validPrompts = prompts.filter(p => p.text.trim().length > 0); if (validPrompts.length === 0) { Alert.alert('Fehler', 'Mindestens ein Prompt ist erforderlich'); return; } if (!selectedModel) { Alert.alert('Fehler', 'Bitte wähle ein Modell aus'); return; } try { const batchId = await createBatch( validPrompts, { model_id: selectedModel.id, model_version: selectedModel.version, width: selectedAspectRatio.width, height: selectedAspectRatio.height, steps: steps, guidance_scale: guidanceScale }, batchName || undefined ); // Reset form setBatchName(''); setPrompts([{ text: '' }]); onSuccess?.(batchId); onClose(); } catch (err) { Alert.alert('Fehler', 'Batch konnte nicht erstellt werden'); console.error('Batch creation error:', err); } }; const validPromptCount = prompts.filter(p => p.text.trim().length > 0).length; return ( {/* Header */} Batch Generation ({validPromptCount}/10) {/* Batch Name */} Batch Name (optional) {/* Model Selection */} Modell {modelsLoading ? ( ) : ( {models.map((model) => ( setSelectedModel(model)} className={`mr-2 px-3 py-2 rounded-lg border ${ selectedModel?.id === model.id ? 'bg-indigo-600 border-indigo-600' : 'bg-dark-surface border-dark-border' }`} > {model.display_name} ))} )} {/* Aspect Ratio */} Seitenverhältnis {aspectRatios.map((ratio) => ( setSelectedAspectRatio(ratio)} className={`mr-2 px-3 py-2 rounded-lg border ${ selectedAspectRatio.value === ratio.value ? 'bg-indigo-600 border-indigo-600' : 'bg-dark-surface border-dark-border' }`} > {ratio.icon} {ratio.label} ))} {/* Settings */} Gemeinsame Einstellungen Größe {selectedAspectRatio.width} x {selectedAspectRatio.height} Steps {steps} Guidance Scale {guidanceScale} {/* Prompts */} Prompts {prompts.map((prompt, index) => ( {index + 1}. updatePrompt(index, text)} multiline textAlignVertical="top" /> {prompts.length > 1 && ( removePrompt(index)} className="ml-2 mt-3 p-2" > )} ))} {prompts.length < 10 && ( Prompt hinzufügen )} {/* Submit Buttons */}