import React, { useState } from 'react'; import { View, Pressable, ActivityIndicator, ScrollView, Platform, Alert } from 'react-native'; import * as DocumentPicker from 'expo-document-picker'; import DateTimePicker from '@react-native-community/datetimepicker'; import { useTranslation } from 'react-i18next'; import { useTheme } from '~/features/theme/ThemeProvider'; import BaseModal from '~/components/atoms/BaseModal'; import Text from '~/components/atoms/Text'; import Icon from '~/components/atoms/Icon'; import Button from '~/components/atoms/Button'; import BlueprintModal from '~/components/organisms/BlueprintModal'; import { useRecordingLanguage } from '~/features/audioRecordingV2'; import { createClient } from '@supabase/supabase-js'; import colors from '~/tailwind.config.js'; import { useInsufficientCreditsStore } from '~/features/credits/store/insufficientCreditsStore'; import { extractMediaDurationWithFormat, isSupportedMediaFile } from '~/utils/mediaUtils'; interface UploadModalProps { isVisible: boolean; onClose: () => void; onFileUpload: ( file: any, language?: string, blueprint?: any, recordingDate?: Date, duration?: number ) => Promise; currentLanguage: string; isUploading?: boolean; } const UploadModal: React.FC = ({ isVisible, onClose, onFileUpload, currentLanguage, isUploading = false, }) => { const { t } = useTranslation(); const { isDark, themeVariant } = useTheme(); const { recordingLanguages, toggleRecordingLanguage, supportedAzureLanguages } = useRecordingLanguage(); const isInsufficientCreditsModalVisible = useInsufficientCreditsStore( (state) => state.isModalVisible ); const [isLanguageSelectorVisible, setIsLanguageSelectorVisible] = useState(false); const [isBlueprintModalVisible, setIsBlueprintModalVisible] = useState(false); const [selectedBlueprint, setSelectedBlueprint] = useState(null); const [modalMode, setModalMode] = useState<'upload' | 'language' | 'blueprint' | 'date' | 'time'>( 'upload' ); const [blueprints, setBlueprints] = useState([]); const [blueprintsLoading, setBlueprintsLoading] = useState(false); const [selectedBlueprintForUpload, setSelectedBlueprintForUpload] = useState(null); const [selectedLanguageForUpload, setSelectedLanguageForUpload] = useState('auto'); const [selectedFile, setSelectedFile] = useState(null); const [selectedDateTime, setSelectedDateTime] = useState(new Date()); const [showDatePicker, setShowDatePicker] = useState(false); const [showTimePicker, setShowTimePicker] = useState(false); const [hasCustomTime, setHasCustomTime] = useState(false); const [fileDuration, setFileDuration] = useState<{ milliseconds: number; formatted: string; } | null>(null); const [isExtractingDuration, setIsExtractingDuration] = useState(false); // Get theme colors const getThemeColors = () => { const themeColors = isDark ? colors.theme.extend.colors.dark[themeVariant] : colors.theme.extend.colors[themeVariant]; return { text: themeColors?.text || (isDark ? '#FFFFFF' : '#000000'), contentBackground: themeColors?.contentBackground || (isDark ? '#1E1E1E' : '#FFFFFF'), contentBackgroundHover: themeColors?.contentBackgroundHover || (isDark ? '#333333' : '#F5F5F5'), border: themeColors?.border || (isDark ? '#374151' : '#E5E7EB'), primary: themeColors?.primary || '#f8d62b', }; }; const themeColors = getThemeColors(); const handleFileSelect = async () => { try { const result = await DocumentPicker.getDocumentAsync({ type: [ // Audio formats 'audio/mpeg', 'audio/mp4', 'audio/x-m4a', 'audio/m4a', // Video formats 'video/mp4', 'video/quicktime', 'video/x-m4v', 'video/*', ], copyToCacheDirectory: true, }); if (!result.canceled) { const file = result.assets[0]; setSelectedFile(file); // Extract duration from the selected file setIsExtractingDuration(true); setFileDuration(null); try { if (!isSupportedMediaFile(file)) { console.warn('File may not be a supported media format, attempting extraction anyway'); } const durationResult = await extractMediaDurationWithFormat(file); setFileDuration({ milliseconds: durationResult.durationMillis, formatted: durationResult.formatted, }); console.log('Duration extracted successfully:', durationResult.formatted); } catch (error) { console.error('Failed to extract duration:', error); Alert.alert( t('upload.error_title', 'Error'), t( 'upload.duration_extraction_failed', 'Could not extract media duration. The file may be corrupted or in an unsupported format.' ), [ { text: t('common.cancel', 'Cancel'), style: 'cancel', onPress: () => { setSelectedFile(null); setFileDuration(null); }, }, { text: t('common.try_another', 'Try Another File'), onPress: () => handleFileSelect(), }, ] ); } finally { setIsExtractingDuration(false); } } } catch (error) { console.error('Error selecting file:', error); Alert.alert( t('upload.error_title', 'Error'), t('upload.file_selection_failed', 'Failed to select file. Please try again.') ); } }; const handleUpload = async () => { console.log('handleUpload called'); if (!selectedFile || isUploading) { return; } // Ensure we have duration before uploading if (!fileDuration) { Alert.alert( t('upload.error_title', 'Error'), t( 'upload.no_duration', 'Media duration could not be determined. Please select a different file.' ) ); return; } try { console.log( 'Calling onFileUpload with params:', selectedFile, selectedLanguageForUpload, selectedBlueprintForUpload, selectedDateTime, fileDuration.milliseconds ); await onFileUpload( selectedFile, selectedLanguageForUpload, selectedBlueprintForUpload, selectedDateTime, fileDuration.milliseconds ); // Modal wird nur geschlossen, wenn Upload erfolgreich war handleModalClose(); } catch (error) { console.error('Upload failed:', error); // Modal bleibt offen bei Fehlern } }; const handleLanguageSelect = () => { console.log('Language select clicked'); console.log('Current modalMode:', modalMode); setModalMode('language'); console.log('Setting modalMode to language'); }; const handleBlueprintSelect = () => { console.log('Blueprint select clicked'); setModalMode('blueprint'); loadBlueprints(); }; const handleDateSelect = () => { console.log('Date select clicked'); if (Platform.OS === 'ios') { setModalMode('date'); } else { setShowDatePicker(true); } }; const handleTimeSelect = () => { console.log('Time select clicked'); if (Platform.OS === 'ios') { setModalMode('time'); } else { setShowTimePicker(true); } }; const onDateChange = (event: any, selectedDate?: Date) => { if (Platform.OS === 'android') { setShowDatePicker(false); } if (selectedDate) { // Preserve the existing time when changing date const newDateTime = new Date(selectedDateTime); newDateTime.setFullYear(selectedDate.getFullYear()); newDateTime.setMonth(selectedDate.getMonth()); newDateTime.setDate(selectedDate.getDate()); setSelectedDateTime(newDateTime); // On iOS, don't automatically close - let user press "Done" button } }; const onTimeChange = (event: any, selectedTime?: Date) => { if (Platform.OS === 'android') { setShowTimePicker(false); } if (selectedTime) { // Preserve the existing date when changing time const newDateTime = new Date(selectedDateTime); newDateTime.setHours(selectedTime.getHours()); newDateTime.setMinutes(selectedTime.getMinutes()); setSelectedDateTime(newDateTime); setHasCustomTime(true); // On iOS, don't automatically close - let user press "Done" button } }; // Lade Blueprints const loadBlueprints = async () => { setBlueprintsLoading(true); try { // Verwende den direkten Supabase-Zugriff wie in der BlueprintModal const directSupabase = createClient( 'https://npgifbrwhftlbrbaglmi.supabase.co', 'sb_publishable_HlAZpB4BxXaMcfOCNx6VJA_-64NTxu4' ); const { data: blueprintData, error: blueprintError } = await directSupabase .from('blueprints') .select('*') .eq('is_public', true) .order('created_at', { ascending: false }); if (blueprintError) { console.error('Error loading blueprints:', blueprintError); setBlueprints([]); } else { console.log('Loaded blueprints:', blueprintData); setBlueprints(blueprintData || []); } } catch (error) { console.error('Error loading blueprints:', error); setBlueprints([]); } finally { setBlueprintsLoading(false); } }; const getSelectedLanguageDisplay = () => { if (selectedLanguageForUpload === 'auto') { return t('upload.auto_detect', 'Automatisch erkennen'); } else { return ( supportedAzureLanguages[selectedLanguageForUpload]?.nativeName || selectedLanguageForUpload ); } }; const getSelectedBlueprintDisplay = () => { if (selectedBlueprintForUpload) { const lang = currentLanguage.startsWith('de') ? 'de' : 'en'; return ( selectedBlueprintForUpload.name?.[lang] || selectedBlueprintForUpload.name?.en || selectedBlueprintForUpload.name?.de || t('blueprints.title', 'Blueprints') ); } return t('upload.no_blueprint_selected', 'No blueprint selected'); }; const getSelectedDateDisplay = () => { return selectedDateTime.toLocaleDateString( currentLanguage.startsWith('de') ? 'de-DE' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric', } ); }; const getSelectedTimeDisplay = () => { return selectedDateTime.toLocaleTimeString( currentLanguage.startsWith('de') ? 'de-DE' : 'en-US', { hour: '2-digit', minute: '2-digit', } ); }; const renderActionButton = ( iconName: string, title: string, subtitle: string, onPress: () => void, showChevron: boolean = true, isActive: boolean = false ) => ( {title} {subtitle} {showChevron && ( )} ); // Bestimme den Modal-Titel basierend auf dem Modus const getModalTitle = () => { console.log('getModalTitle called with modalMode:', modalMode); switch (modalMode) { case 'language': return t('upload.select_recording_language', 'Aufnahmesprache auswählen'); case 'blueprint': return t('upload.select_blueprint', 'Blueprint auswählen'); case 'date': return t('upload.select_date', 'Datum auswählen'); case 'time': return t('upload.select_time', 'Uhrzeit auswählen'); default: return t('upload.title', 'Audio/Video hochladen'); } }; // Bestimme den Modal-Inhalt basierend auf dem Modus const renderModalContent = () => { console.log('renderModalContent called with modalMode:', modalMode); switch (modalMode) { case 'language': return ( {Object.entries(supportedAzureLanguages) .sort(([a], [b]) => { if (a === 'auto') return -1; if (b === 'auto') return 1; // Ausgewählte Sprache zuerst, dann alphabetisch const aSelected = selectedLanguageForUpload === a; const bSelected = selectedLanguageForUpload === b; if (aSelected && !bSelected) return -1; if (!aSelected && bSelected) return 1; return supportedAzureLanguages[a].nativeName.localeCompare( supportedAzureLanguages[b].nativeName ); }) .map(([code, language]) => { const isSelected = selectedLanguageForUpload === code; return ( { console.log('Language selected:', code); setSelectedLanguageForUpload(code); // Setze nur eine einzelne Sprache setModalMode('upload'); // Zurück zum Upload-Modal }} > {language.emoji} {language.nativeName} {isSelected && ( )} ); })} ); case 'blueprint': return ( {blueprintsLoading ? ( {t('upload.loading_blueprints', 'Blueprints werden geladen...')} ) : blueprints.length === 0 ? ( {t('upload.no_blueprints_available', 'Keine Blueprints verfügbar')} ) : ( blueprints.map((blueprint) => { const isSelected = selectedBlueprintForUpload?.id === blueprint.id; const lang = currentLanguage.startsWith('de') ? 'de' : 'en'; const displayName = blueprint.name?.[lang] || blueprint.name?.en || blueprint.name?.de || 'Blueprint'; const displayDescription = blueprint.description?.[lang] || blueprint.description?.en || blueprint.description?.de || ''; return ( { console.log('Blueprint selected:', blueprint.id); setSelectedBlueprintForUpload(blueprint); setModalMode('upload'); // Zurück zum Upload-Modal }} > {displayName} {displayDescription ? ( {displayDescription} ) : null} {isSelected && ( )} ); }) )} ); case 'date': return ( {Platform.OS === 'ios' && (