import React, { useState, useEffect } from 'react'; import { View, Text, TouchableOpacity, useWindowDimensions, StyleSheet, TextInput, ActivityIndicator, ScrollView } from 'react-native'; import { Ionicons, MaterialIcons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown, } from 'react-native-reanimated'; import { useSettings } from '../../../hooks/useSettings'; import { introService, SkipType } from '../../../services/introService'; import { toastService } from '../../../services/toastService'; interface SubmitIntroModalProps { visible: boolean; onClose: () => void; currentTime: number; imdbId?: string; season?: number; episode?: number; } /** * Parses time string (MM:SS or SS) to seconds */ const parseTimeToSeconds = (input: string): number | null => { if (!input) return null; // Format: MM:SS if (input.includes(':')) { const parts = input.split(':'); if (parts.length !== 2) return null; const mins = parseInt(parts[0], 10); const secs = parseInt(parts[1], 10); if (isNaN(mins) || isParseSecs(secs)) return null; return mins * 60 + secs; } // Format: Seconds only const secs = parseInt(input, 10); return isNaN(secs) ? null : secs; }; const isParseSecs = (secs: number) => isNaN(secs) || secs < 0 || secs >= 60; /** * Formats seconds to MM:SS */ const formatSecondsToMMSS = (seconds: number): string => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; export const SubmitIntroModal: React.FC = ({ visible, onClose, currentTime, imdbId, season, episode, }) => { const { t } = useTranslation(); const { width } = useWindowDimensions(); const { settings } = useSettings(); const [startTimeStr, setStartTimeStr] = useState('00:00'); const [endTimeStr, setEndTimeStr] = useState(formatSecondsToMMSS(currentTime)); const [segmentType, setSegmentType] = useState('intro'); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { if (visible) { setEndTimeStr(formatSecondsToMMSS(currentTime)); } }, [visible, currentTime]); if (!visible) return null; const handleCaptureStart = () => setStartTimeStr(formatSecondsToMMSS(currentTime)); const handleCaptureEnd = () => setEndTimeStr(formatSecondsToMMSS(currentTime)); const handleSubmit = async () => { const startSec = parseTimeToSeconds(startTimeStr); const endSec = parseTimeToSeconds(endTimeStr); if (startSec === null || endSec === null) { toastService.error('Invalid format', 'Please use MM:SS format'); return; } if (endSec <= startSec) { toastService.warning('Invalid duration', 'End time must be after start time'); return; } if (!imdbId || season === undefined || episode === undefined) { toastService.error('Missing metadata', 'Could not identify this episode'); return; } setIsSubmitting(true); try { const success = await introService.submitIntro( settings.introDbApiKey, imdbId, season, episode, startSec, endSec, segmentType ); if (success) { toastService.success(t('player_ui.intro_submitted', { defaultValue: 'Segment submitted successfully' })); onClose(); } else { toastService.error(t('player_ui.intro_submit_failed', { defaultValue: 'Failed to submit segment' })); } } catch (error) { toastService.error('Error', 'An unexpected error occurred'); } finally { setIsSubmitting(false); } }; const segmentTypes: { label: string; value: SkipType; icon: any }[] = [ { label: 'Intro', value: 'intro', icon: 'play-circle-outline' }, { label: 'Recap', value: 'recap', icon: 'replay' }, { label: 'Outro', value: 'outro', icon: 'stop-circle' }, ]; return ( Submit Timestamps {/* Segment Type Selector */} Segment Type {segmentTypes.map((type) => ( setSegmentType(type.value)} style={[ localStyles.typeButton, segmentType === type.value && localStyles.typeButtonActive ]} > {type.label} ))} {/* Start Time Input */} Start Time (MM:SS) Capture {/* End Time Input */} End Time (MM:SS) Capture {/* Action Buttons */} Cancel {isSubmitting ? ( ) : ( <> Submit )} ); }; const localStyles = StyleSheet.create({ centeredView: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center', paddingBottom: 40, }, modalContainer: { backgroundColor: 'rgba(20, 20, 20, 0.98)', borderRadius: 28, padding: 24, borderWidth: 1, borderColor: 'rgba(255,255,255,0.1)', shadowColor: "#000", shadowOffset: { width: 0, height: 10 }, shadowOpacity: 0.5, shadowRadius: 15, elevation: 20, maxHeight: '80%', }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24, }, title: { color: 'white', fontSize: 18, fontWeight: '700', }, closeButton: { padding: 4, }, content: { gap: 20, }, typeRow: { flexDirection: 'row', gap: 8, }, typeButton: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, backgroundColor: 'rgba(255,255,255,0.05)', borderRadius: 12, paddingVertical: 10, borderWidth: 1, borderColor: 'rgba(255,255,255,0.1)', }, typeButtonActive: { backgroundColor: 'white', borderColor: 'white', }, typeButtonText: { color: 'rgba(255,255,255,0.6)', fontSize: 13, fontWeight: '600', }, typeButtonTextActive: { color: 'black', }, inputRow: { flexDirection: 'row', alignItems: 'flex-end', gap: 12, }, label: { color: 'rgba(255,255,255,0.6)', fontSize: 12, marginBottom: 8, fontWeight: '600', textTransform: 'uppercase', letterSpacing: 0.5, }, input: { backgroundColor: 'rgba(255,255,255,0.05)', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 12, color: 'white', fontSize: 16, borderWidth: 1, borderColor: 'rgba(255,255,255,0.1)', }, captureBtn: { backgroundColor: 'rgba(255,255,255,0.1)', height: 48, paddingHorizontal: 12, borderRadius: 12, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, }, captureText: { color: 'white', fontSize: 13, fontWeight: '600', }, buttonRow: { flexDirection: 'row', gap: 12, marginTop: 12, }, cancelBtn: { flex: 1, backgroundColor: 'rgba(255,255,255,0.1)', borderRadius: 16, height: 56, alignItems: 'center', justifyContent: 'center', }, cancelBtnText: { color: 'white', fontSize: 16, fontWeight: '600', }, submitBtn: { flex: 2, backgroundColor: 'white', borderRadius: 16, height: 56, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, }, submitBtnText: { color: 'black', fontSize: 16, fontWeight: '700', }, });