import { ThemedText } from '@/components/themed-text'; import { VideoPlayer } from '@/components/video/video-player'; import { useThemeColor } from '@/hooks/use-theme-color'; import { Template } from '@/lib/types/template'; import { Image } from 'expo-image'; import React, { useEffect, useRef, useState, useCallback } from 'react'; // ResizeMode 兼容映射 const ResizeMode = { CONTAIN: 'contain' as const, COVER: 'cover' as const, STRETCH: 'fill' as const, }; import { ActivityIndicator, Dimensions, Modal, PanResponder, Platform, StatusBar, StyleSheet, TouchableOpacity, View, BackHandler, } from 'react-native'; import { isVideoTemplate, canLoadMedia, getEffectiveMediaUrl } from '@/utils/media-utils'; const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); export interface FullscreenMediaModalProps { visible: boolean; onClose: () => void; currentIndex: number; templates: Template[]; onIndexChanged: (index: number) => void; } export function FullscreenMediaModal({ visible, onClose, currentIndex, templates, onIndexChanged, }: FullscreenMediaModalProps) { const [isLoading, setIsLoading] = useState(true); const backgroundColor = useThemeColor({}, 'background'); // 获取当前模板 const currentTemplate = templates[currentIndex]; const isCurrentVideo = currentTemplate ? isVideoTemplate(currentTemplate) : false; const canLoadCurrentMedia = currentTemplate ? canLoadMedia(currentTemplate) : false; // 重置状态当模态框打开/关闭时 useEffect(() => { if (visible) { setIsLoading(true); // 添加备用机制:3秒后自动隐藏loading,防止卡住 const timer = setTimeout(() => { setIsLoading(false); }, 3000); return () => clearTimeout(timer); } }, [visible, currentIndex]); const markMediaLoaded = () => { setIsLoading(false); }; // 处理Web平台视频加载完成 const handleWebVideoLoad = () => { setIsLoading(false); }; // 处理媒体加载错误 const handleMediaError = (error: any) => { console.error('媒体加载错误:', { template: currentTemplate?.title, mediaType: isCurrentVideo ? 'video' : 'image', url: getEffectiveMediaUrl(currentTemplate), error: error }); setIsLoading(false); }; // 媒体点击处理(直接关闭) const handleClose = useCallback(() => { onClose(); }, [onClose]); const handleMediaPress = () => { handleClose(); }; // 统一的切换处理函数 const handlePrevious = () => { if (currentIndex > 0) { const newIndex = currentIndex - 1; onIndexChanged(newIndex); } }; const handleNext = () => { if (currentIndex < templates.length - 1) { const newIndex = currentIndex + 1; onIndexChanged(newIndex); } }; // 创建手势处理器 const panResponder = useRef( PanResponder.create({ onMoveShouldSetPanResponder: (_, gestureState) => { // 只有水平移动时才响应 return Math.abs(gestureState.dx) > Math.abs(gestureState.dy) && Math.abs(gestureState.dx) > 10; }, onPanResponderRelease: (_, gestureState) => { const threshold = screenWidth / 4; // 滑动屏幕宽度的1/4触发切换 if (gestureState.dx > threshold && currentIndex > 0) { // 向右滑动,显示上一个 handlePrevious(); } else if (gestureState.dx < -threshold && currentIndex < templates.length - 1) { // 向左滑动,显示下一个 handleNext(); } }, }) ).current; // 处理返回键(Android) useEffect(() => { const handleBackPress = () => { if (visible) { onClose(); return true; } return false; }; const subscription = BackHandler.addEventListener('hardwareBackPress', handleBackPress); return () => subscription.remove(); }, [visible, onClose]); if (!visible || !currentTemplate) return null; return ( {/* 状态栏处理 */} {Platform.OS === 'android' && ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#000', }, mediaContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, mediaWrapper: { flex: 1, width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center', }, image: { width: screenWidth, height: screenHeight, backgroundColor: '#000', }, video: { width: screenWidth, height: screenHeight, backgroundColor: '#000', }, loadingOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0, 0, 0, 0.3)', }, leftIndicator: { position: 'absolute', left: 20, top: '50%', marginTop: -20, width: 40, height: 40, borderRadius: 20, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', zIndex: 10, }, rightIndicator: { position: 'absolute', right: 20, top: '50%', marginTop: -20, width: 40, height: 40, borderRadius: 20, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', zIndex: 10, }, indicatorIcon: { fontSize: 24, color: '#fff', fontWeight: 'bold', }, }); export default FullscreenMediaModal;