import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, Modal, TouchableOpacity, TextInput, ScrollView, KeyboardAvoidingView, Platform, ActivityIndicator, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; import { useTheme } from '../../contexts/ThemeContext'; import { MalApiService } from '../../services/mal/MalApi'; import { MalListStatus, MalAnimeNode } from '../../types/mal'; import { useToast } from '../../contexts/ToastContext'; interface MalEditModalProps { visible: boolean; onClose: () => void; anime: MalAnimeNode; onUpdateSuccess: () => void; } export const MalEditModal: React.FC = ({ visible, onClose, anime, onUpdateSuccess, }) => { const { currentTheme } = useTheme(); const { showSuccess, showError } = useToast(); const [status, setStatus] = useState(anime.list_status.status); const [episodes, setEpisodes] = useState(anime.list_status.num_episodes_watched.toString()); const [score, setScore] = useState(anime.list_status.score.toString()); const [isUpdating, setIsUpdating] = useState(false); useEffect(() => { if (visible) { setStatus(anime.list_status.status); setEpisodes(anime.list_status.num_episodes_watched.toString()); setScore(anime.list_status.score.toString()); } }, [visible, anime]); const handleUpdate = async () => { setIsUpdating(true); try { const epNum = parseInt(episodes, 10) || 0; let scoreNum = parseInt(score, 10) || 0; // Validation: MAL scores must be between 0 and 10 scoreNum = Math.max(0, Math.min(10, scoreNum)); await MalApiService.updateStatus(anime.node.id, status, epNum, scoreNum); showSuccess('Updated', `${anime.node.title} status updated on MAL`); onUpdateSuccess(); onClose(); } catch (error) { showError('Update Failed', 'Could not update MAL status'); } finally { setIsUpdating(false); } }; const statusOptions: { label: string; value: MalListStatus }[] = [ { label: 'Watching', value: 'watching' }, { label: 'Completed', value: 'completed' }, { label: 'On Hold', value: 'on_hold' }, { label: 'Dropped', value: 'dropped' }, { label: 'Plan to Watch', value: 'plan_to_watch' }, ]; return ( Edit {anime.node.title} Status {statusOptions.map((option) => ( setStatus(option.value)} > {option.label} ))} Episodes ({anime.node.num_episodes || '?'}) Score (0-10) {isUpdating ? ( ) : ( Update MAL )} ); }; const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.7)', justifyContent: 'center', alignItems: 'center', padding: 20, }, container: { width: '100%', maxWidth: 400, }, modalContent: { borderRadius: 16, padding: 20, maxHeight: '90%', }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20, }, title: { fontSize: 18, fontWeight: '700', flex: 1, marginRight: 10, }, label: { fontSize: 14, fontWeight: '600', marginBottom: 8, marginTop: 12, }, statusGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, }, statusChip: { paddingHorizontal: 12, paddingVertical: 8, borderRadius: 20, borderWidth: 1, marginBottom: 4, }, statusText: { fontSize: 13, fontWeight: '500', }, inputRow: { flexDirection: 'row', gap: 16, marginTop: 8, }, inputGroup: { flex: 1, }, input: { height: 44, borderRadius: 8, borderWidth: 1, paddingHorizontal: 12, fontSize: 16, }, updateButton: { height: 48, borderRadius: 24, justifyContent: 'center', alignItems: 'center', marginTop: 24, marginBottom: 10, }, updateButtonText: { color: 'white', fontSize: 16, fontWeight: '700', }, });