diff --git a/src/components/metadata/CastDetailsModal.tsx b/src/components/metadata/CastDetailsModal.tsx
index 074ff15b..a1c01040 100644
--- a/src/components/metadata/CastDetailsModal.tsx
+++ b/src/components/metadata/CastDetailsModal.tsx
@@ -17,6 +17,7 @@ import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
+ withSpring,
runOnJS,
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
@@ -31,8 +32,8 @@ interface CastDetailsModalProps {
}
const { width, height } = Dimensions.get('window');
-const MODAL_WIDTH = Math.min(width - 32, 520);
-const MODAL_MAX_HEIGHT = height * 0.85;
+const MODAL_WIDTH = Math.min(width - 40, 400);
+const MODAL_HEIGHT = height * 0.7;
interface PersonDetails {
id: number;
@@ -45,57 +46,6 @@ interface PersonDetails {
also_known_as: string[];
}
-const InfoBadge = ({
- label,
- value,
- icon,
- color = '#6B7280',
- bgColor = 'rgba(107, 114, 128, 0.15)'
-}: {
- label: string;
- value: string;
- icon?: string;
- color?: string;
- bgColor?: string;
-}) => (
-
- {icon && (
-
- )}
-
-
- {label}
-
-
- {value}
-
-
-
-);
-
export const CastDetailsModal: React.FC = ({
visible,
onClose,
@@ -104,24 +54,37 @@ export const CastDetailsModal: React.FC = ({
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: 200 });
- fetchPersonDetails();
+ 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: 150 });
+ 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) return;
+ 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 {
@@ -131,10 +94,12 @@ export const CastDetailsModal: React.FC = ({
const modalStyle = useAnimatedStyle(() => ({
opacity: modalOpacity.value,
+ transform: [{ scale: modalScale.value }],
}));
const handleClose = () => {
- modalOpacity.value = withTiming(0, { duration: 150 }, () => {
+ modalOpacity.value = withTiming(0, { duration: 200 });
+ modalScale.value = withTiming(0.9, { duration: 200 }, () => {
runOnJS(onClose)();
});
};
@@ -144,7 +109,7 @@ export const CastDetailsModal: React.FC = ({
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
year: 'numeric',
- month: 'long',
+ month: 'short',
day: 'numeric',
});
};
@@ -167,19 +132,19 @@ export const CastDetailsModal: React.FC = ({
return (
= ({
style={[
{
width: MODAL_WIDTH,
- maxHeight: MODAL_MAX_HEIGHT,
- minHeight: height * 0.4,
+ height: MODAL_HEIGHT,
overflow: 'hidden',
- elevation: 25,
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 12 },
- shadowOpacity: 0.4,
- shadowRadius: 25,
- alignSelf: 'center',
+ borderRadius: 24,
+ backgroundColor: Platform.OS === 'android'
+ ? 'rgba(20, 20, 20, 0.95)'
+ : 'transparent',
},
modalStyle,
]}
>
-
-
-
-
- {castMember.profile_path ? (
-
- ) : (
-
-
- {castMember.name.split(' ').reduce((prev: string, current: string) => prev + current[0], '').substring(0, 2)}
-
-
- )}
-
-
-
-
- {castMember.name}
-
- {castMember.character && (
+ {renderContent()}
+
+ ) : (
+ renderContent()
+ )}
+
+
+ );
+
+ function renderContent() {
+ return (
+ <>
+ {/* Header */}
+
+
+
+ {castMember.profile_path ? (
+
+ ) : (
+
- as {castMember.character}
+ color: '#fff',
+ fontSize: 18,
+ fontWeight: '700',
+ }}>
+ {castMember.name.split(' ').reduce((prev: string, current: string) => prev + current[0], '').substring(0, 2)}
- )}
-
+
+ )}
+
+
+
+
+ {castMember.name}
+
+ {castMember.character && (
+
+ as {castMember.character}
+
+ )}
-
+
+
-
- {loading ? (
-
+ {loading ? (
+
+
+
-
-
+
+ ) : (
+
+ {/* Quick Info */}
+ {(personDetails?.known_for_department || personDetails?.birthday || personDetails?.place_of_birth) && (
+
- Loading details...
-
-
- ) : (
-
- {/* Personal Information */}
-
-
- Personal Information
-
+ {personDetails?.known_for_department && (
+
+
+
+ Department
+
+
+ {personDetails.known_for_department}
+
+
+ )}
-
- {personDetails?.known_for_department && (
-
- )}
-
- {personDetails?.birthday && (
-
- )}
-
- {personDetails?.place_of_birth && (
-
- )}
-
+ {personDetails?.birthday && (
+
+
+
+ Age
+
+
+ {calculateAge(personDetails.birthday)} years old
+
+
+ )}
+
+ {personDetails?.place_of_birth && (
+
+
+
+ Born in
+
+
+ {personDetails.place_of_birth}
+
+
+ )}
{personDetails?.birthday && (
-
-
-
- Born
-
-
- {formatDate(personDetails.birthday)}
+ Born on {formatDate(personDetails.birthday)}
)}
+ )}
- {/* Biography */}
- {personDetails?.biography && (
-
-
- Biography
-
-
-
-
- {personDetails.biography}
-
-
-
- )}
-
- {/* Also Known As */}
- {personDetails?.also_known_as && personDetails.also_known_as.length > 0 && (
-
-
- Also Known As
-
-
-
- {personDetails.also_known_as.slice(0, 6).map((alias, index) => (
-
-
- {alias}
-
-
- ))}
-
-
- )}
-
- {/* No details available */}
- {!loading && !personDetails?.biography && !personDetails?.birthday && !personDetails?.place_of_birth && (
-
+
-
-
- No additional details available
-
-
- )}
-
- )}
-
-
-
-
- );
+ 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;
\ No newline at end of file