import React, { useEffect } from "react"; import { View, Text, StyleSheet, Image, ScrollView, ActivityIndicator } from "react-native"; import { useLocalSearchParams, useRouter } from "expo-router"; import { ThemedView } from "@/components/ThemedView"; import { ThemedText } from "@/components/ThemedText"; import { StyledButton } from "@/components/StyledButton"; import VideoLoadingAnimation from "@/components/VideoLoadingAnimation"; import useDetailStore from "@/stores/detailStore"; import { FontAwesome } from "@expo/vector-icons"; import { useResponsiveLayout } from "@/hooks/useResponsiveLayout"; import { getCommonResponsiveStyles } from "@/utils/ResponsiveStyles"; import ResponsiveNavigation from "@/components/navigation/ResponsiveNavigation"; import ResponsiveHeader from "@/components/navigation/ResponsiveHeader"; export default function DetailScreen() { const { q, source, id } = useLocalSearchParams<{ q: string; source?: string; id?: string }>(); const router = useRouter(); // 响应式布局配置 const responsiveConfig = useResponsiveLayout(); const commonStyles = getCommonResponsiveStyles(responsiveConfig); const { deviceType, spacing } = responsiveConfig; const { detail, searchResults, loading, error, allSourcesLoaded, init, setDetail, abort, isFavorited, toggleFavorite, } = useDetailStore(); useEffect(() => { if (q) { init(q, source, id); } return () => { abort(); }; }, [abort, init, q, source, id]); const handlePlay = (episodeIndex: number) => { if (!detail) return; abort(); // Cancel any ongoing fetches router.push({ pathname: "/play", params: { // Pass necessary identifiers, the rest will be in the store q: detail.title, source: detail.source, id: detail.id.toString(), episodeIndex: episodeIndex.toString(), }, }); }; if (loading) { return ; } if (error) { const content = ( {error} ); if (deviceType === 'tv') { return content; } return ( {content} ); } if (!detail) { const content = ( 未找到详情信息 ); if (deviceType === 'tv') { return content; } return ( {content} ); } // 动态样式 const dynamicStyles = createResponsiveStyles(deviceType, spacing); const renderDetailContent = () => { if (deviceType === 'mobile') { // 移动端垂直布局 return ( {/* 海报和基本信息 */} {detail.title} {detail.year} {detail.type_name} {/* 描述 */} {detail.desc} {/* 播放源 */} 播放源 ({searchResults.length}) {!allSourcesLoaded && } {searchResults.map((item, index) => { const isSelected = detail?.source === item.source; return ( setDetail(item)} isSelected={isSelected} style={dynamicStyles.sourceButton} > {item.source_name} {item.episodes.length > 1 && ( {item.episodes.length > 99 ? "99+" : `${item.episodes.length}`} 集 )} {item.resolution && ( {item.resolution} )} ); })} {/* 剧集列表 */} 播放列表 {detail.episodes.map((episode, index) => ( handlePlay(index)} text={`第 ${index + 1} 集`} textStyle={dynamicStyles.episodeButtonText} /> ))} ); } else { // 平板和TV端水平布局 return ( {detail.title} {detail.year} {detail.type_name} {detail.desc} 选择播放源 共 {searchResults.length} 个 {!allSourcesLoaded && } {searchResults.map((item, index) => { const isSelected = detail?.source === item.source; return ( setDetail(item)} hasTVPreferredFocus={index === 0} isSelected={isSelected} style={dynamicStyles.sourceButton} > {item.source_name} {item.episodes.length > 1 && ( {item.episodes.length > 99 ? "99+" : `${item.episodes.length}`} 集 )} {item.resolution && ( {item.resolution} )} ); })} 播放列表 {detail.episodes.map((episode, index) => ( handlePlay(index)} text={`第 ${index + 1} 集`} textStyle={dynamicStyles.episodeButtonText} /> ))} ); } }; const content = ( {renderDetailContent()} ); // 根据设备类型决定是否包装在响应式导航中 if (deviceType === 'tv') { return content; } return ( {content} ); } const createResponsiveStyles = (deviceType: string, spacing: number) => { const isTV = deviceType === 'tv'; const isTablet = deviceType === 'tablet'; const isMobile = deviceType === 'mobile'; return StyleSheet.create({ scrollContainer: { flex: 1, }, // 移动端专用样式 mobileTopContainer: { paddingHorizontal: spacing, paddingTop: spacing, paddingBottom: spacing / 2, }, mobilePoster: { width: '100%', height: 280, borderRadius: 8, alignSelf: 'center', marginBottom: spacing, }, mobileInfoContainer: { flex: 1, }, descriptionContainer: { paddingHorizontal: spacing, paddingBottom: spacing, }, // 平板和TV端样式 topContainer: { flexDirection: "row", padding: spacing, }, poster: { width: isTV ? 200 : 160, height: isTV ? 300 : 240, borderRadius: 8, }, infoContainer: { flex: 1, marginLeft: spacing, justifyContent: "flex-start", }, descriptionScrollView: { height: 150, }, // 通用样式 titleContainer: { flexDirection: "row", alignItems: "center", marginBottom: spacing / 2, }, title: { paddingTop: 16, fontSize: isMobile ? 20 : isTablet ? 24 : 28, fontWeight: "bold", flexShrink: 1, color: 'white', }, favoriteButton: { padding: 10, marginLeft: 10, backgroundColor: "transparent", }, metaContainer: { flexDirection: "row", marginBottom: spacing / 2, }, metaText: { color: "#aaa", marginRight: spacing / 2, fontSize: isMobile ? 12 : 14, }, description: { fontSize: isMobile ? 13 : 14, color: "#ccc", lineHeight: isMobile ? 18 : 22, }, // 播放源和剧集样式 bottomContainer: { paddingHorizontal: spacing, }, sourcesContainer: { marginTop: spacing, }, sourcesTitleContainer: { flexDirection: "row", alignItems: "center", marginBottom: spacing / 2, }, sourcesTitle: { fontSize: isMobile ? 16 : isTablet ? 18 : 20, fontWeight: "bold", color: 'white', }, sourceList: { flexDirection: "row", flexWrap: "wrap", }, sourceButton: { margin: isMobile ? 4 : 8, minHeight: isMobile ? 36 : 44, }, sourceButtonText: { color: "white", fontSize: isMobile ? 14 : 16, }, badge: { backgroundColor: "#666", borderRadius: 10, paddingHorizontal: 6, paddingVertical: 2, marginLeft: 8, }, badgeText: { color: "#fff", fontSize: isMobile ? 10 : 12, fontWeight: "bold", paddingBottom: 2.5, }, selectedBadge: { backgroundColor: "#4c4c4c", }, episodesContainer: { marginTop: spacing, paddingBottom: spacing * 2, }, episodesTitle: { fontSize: isMobile ? 16 : isTablet ? 18 : 20, fontWeight: "bold", marginBottom: spacing / 2, color: 'white', }, episodeList: { flexDirection: "row", flexWrap: "wrap", }, episodeButton: { margin: isMobile ? 3 : 5, minHeight: isMobile ? 32 : 36, }, episodeButtonText: { color: "white", fontSize: isMobile ? 12 : 14, }, }); };