import React, { useState, useEffect } from 'react'; import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Image, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { VideoPlayer } from '../../components/VideoPlayer'; import { CommentItem } from '../../components/CommentItem'; import { useVideoDetail } from '../../hooks/useVideoDetail'; import { useComments } from '../../hooks/useComments'; import { useVideoStore } from '../../store/videoStore'; import { formatCount } from '../../utils/format'; import { proxyImageUrl } from '../../utils/imageUrl'; type Tab = 'intro' | 'comments'; export default function VideoDetailScreen() { const { bvid } = useLocalSearchParams<{ bvid: string }>(); const router = useRouter(); const { video, playData, loading: videoLoading, qualities, currentQn, changeQuality } = useVideoDetail(bvid as string); const { comments, loading: cmtLoading, load: loadComments } = useComments(video?.aid ?? 0); const [tab, setTab] = useState('comments'); const { setVideo, clearVideo } = useVideoStore(); useEffect(() => { clearVideo(); }, [bvid]); useEffect(() => { if (video?.aid) loadComments(); }, [video?.aid]); function handleMiniPlayer() { if (video) { setVideo(bvid as string, video.title, video.pic); router.back(); } } return ( router.back()} style={styles.backBtn}> {video?.title ?? '视频详情'} {videoLoading ? ( ) : video ? ( <> {video.title} {video.owner.name} + 关注 setTab('intro')}> 简介 {tab === 'intro' && } setTab('comments')}> 评论 {video.stat.reply > 0 ? formatCount(video.stat.reply) : ''} {tab === 'comments' && } {tab === 'intro' ? ( {video.desc || '暂无简介'} ) : ( <> {comments.map(c => )} {cmtLoading && } {!cmtLoading && comments.length > 0 && ( 加载更多评论 )} {!cmtLoading && comments.length === 0 && !videoLoading && ( 暂无评论 )} )} ) : null} ); } function StatBadge({ icon, count }: { icon: string; count: number }) { return ( {formatCount(count)} ); } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: '#fff' }, topBar: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 8, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#eee' }, backBtn: { padding: 4 }, topTitle: { flex: 1, fontSize: 15, fontWeight: '600', marginLeft: 4, color: '#212121' }, miniBtn: { padding: 4 }, scroll: { flex: 1 }, loader: { marginVertical: 30 }, titleSection: { padding: 14 }, title: { fontSize: 16, fontWeight: '600', color: '#212121', lineHeight: 22, marginBottom: 8 }, statsRow: { flexDirection: 'row', gap: 16 }, stat: { flexDirection: 'row', alignItems: 'center', gap: 3 }, statText: { fontSize: 12, color: '#999' }, upRow: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingBottom: 12, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#f0f0f0' }, avatar: { width: 38, height: 38, borderRadius: 19, marginRight: 10 }, upName: { flex: 1, fontSize: 14, color: '#212121', fontWeight: '500' }, followBtn: { backgroundColor: '#00AEEC', paddingHorizontal: 14, paddingVertical: 5, borderRadius: 14 }, followTxt: { color: '#fff', fontSize: 12, fontWeight: '600' }, tabBar: { flexDirection: 'row', borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#eee' }, tabItem: { flex: 1, alignItems: 'center', paddingVertical: 12, position: 'relative' }, tabLabel: { fontSize: 14, color: '#999' }, tabActive: { color: '#00AEEC', fontWeight: '700' }, tabUnderline: { position: 'absolute', bottom: 0, width: 24, height: 2, backgroundColor: '#00AEEC', borderRadius: 1 }, descBox: { padding: 16 }, descText: { fontSize: 14, color: '#555', lineHeight: 22 }, loadMore: { alignItems: 'center', padding: 16 }, loadMoreTxt: { color: '#00AEEC', fontSize: 13 }, emptyTxt: { textAlign: 'center', color: '#bbb', padding: 30 }, });