import React, { useState, useEffect } from 'react'; import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Dimensions, Platform, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; import { Image } from 'expo-image'; import Animated, { FadeIn, FadeOut, useAnimatedStyle, useSharedValue, withTiming, withSpring, runOnJS, } from 'react-native-reanimated'; import { LinearGradient } from 'expo-linear-gradient'; import { useTheme } from '../../contexts/ThemeContext'; import { Cast } from '../../types/cast'; import { tmdbService } from '../../services/tmdbService'; interface CastDetailsModalProps { visible: boolean; onClose: () => void; castMember: Cast | null; } const { width, height } = Dimensions.get('window'); const MODAL_WIDTH = Math.min(width - 40, 400); const MODAL_HEIGHT = height * 0.7; interface PersonDetails { id: number; name: string; biography: string; birthday: string | null; place_of_birth: string | null; known_for_department: string; profile_path: string | null; also_known_as: string[]; } export const CastDetailsModal: React.FC = ({ visible, onClose, castMember, }) => { const { currentTheme } = useTheme(); const [personDetails, setPersonDetails] = useState(null); const [loading, setLoading] = useState(false); const [hasFetched, setHasFetched] = useState(false); const modalOpacity = useSharedValue(0); const modalScale = useSharedValue(0.9); useEffect(() => { if (visible && castMember) { modalOpacity.value = withTiming(1, { duration: 250 }); modalScale.value = withSpring(1, { damping: 20, stiffness: 200 }); if (!hasFetched || personDetails?.id !== castMember.id) { fetchPersonDetails(); } } else { modalOpacity.value = withTiming(0, { duration: 200 }); modalScale.value = withTiming(0.9, { duration: 200 }); if (!visible) { setHasFetched(false); setPersonDetails(null); } } }, [visible, castMember]); const fetchPersonDetails = async () => { if (!castMember || loading) return; setLoading(true); try { const details = await tmdbService.getPersonDetails(castMember.id); setPersonDetails(details); setHasFetched(true); } catch (error) { console.error('Error fetching person details:', error); } finally { setLoading(false); } }; const modalStyle = useAnimatedStyle(() => ({ opacity: modalOpacity.value, transform: [{ scale: modalScale.value }], })); const handleClose = () => { modalOpacity.value = withTiming(0, { duration: 200 }); modalScale.value = withTiming(0.9, { duration: 200 }, () => { runOnJS(onClose)(); }); }; const formatDate = (dateString: string | null) => { if (!dateString) return null; const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); }; const calculateAge = (birthday: string | null) => { if (!birthday) return null; const today = new Date(); const birthDate = new Date(birthday); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { age--; } return age; }; if (!visible || !castMember) return null; return ( {Platform.OS === 'ios' ? ( {renderContent()} ) : ( renderContent() )} ); function renderContent() { return ( <> {/* Header */} {castMember.profile_path ? ( ) : ( {castMember.name.split(' ').reduce((prev: string, current: string) => prev + current[0], '').substring(0, 2)} )} {castMember.name} {castMember.character && ( as {castMember.character} )} {/* Content */} {loading ? ( Loading details... ) : ( {/* Quick Info */} {(personDetails?.known_for_department || personDetails?.birthday || personDetails?.place_of_birth) && ( {personDetails?.known_for_department && ( Department {personDetails.known_for_department} )} {personDetails?.birthday && ( Age {calculateAge(personDetails.birthday)} years old )} {personDetails?.place_of_birth && ( Born in {personDetails.place_of_birth} )} {personDetails?.birthday && ( Born on {formatDate(personDetails.birthday)} )} )} {/* Biography */} {personDetails?.biography && ( Biography {personDetails.biography} )} {/* Also Known As - Compact */} {personDetails?.also_known_as && personDetails.also_known_as.length > 0 && ( Also Known As {personDetails.also_known_as.slice(0, 4).join(' • ')} )} {/* No details available */} {!loading && !personDetails?.biography && !personDetails?.birthday && !personDetails?.place_of_birth && ( No additional details available )} )} ); } }; export default CastDetailsModal;