import React, { useState, useEffect, useLayoutEffect, useRef } 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 { useLiveDetail } from "../../hooks/useLiveDetail"; import { useLiveDanmaku } from "../../hooks/useLiveDanmaku"; import { LivePlayer } from "../../components/LivePlayer"; import DanmakuList from "../../components/DanmakuList"; import { formatCount } from "../../utils/format"; import { proxyImageUrl } from "../../utils/imageUrl"; import { useTheme } from "../../utils/theme"; import { useLiveStore } from "../../store/liveStore"; type Tab = "intro" | "danmaku"; export default function LiveDetailScreen() { const { roomId } = useLocalSearchParams<{ roomId: string }>(); const router = useRouter(); const theme = useTheme(); const id = parseInt(roomId ?? "0", 10); // 进入详情页时立即清除小窗(useLayoutEffect 在绘制前同步执行) useLayoutEffect(() => { useLiveStore.getState().clearLive(); }, []); const { room, anchor, stream, loading, error, changeQuality } = useLiveDetail(id); const [tab, setTab] = useState("intro"); const isLive = room?.live_status === 1; const hlsUrl = stream?.hlsUrl ?? ""; const flvUrl = stream?.flvUrl ?? ""; const qualities = stream?.qualities ?? []; const currentQn = stream?.qn ?? 0; const setLive = useLiveStore(s => s.setLive); const actualRoomId = room?.roomid ?? id; const { danmakus, giftCounts } = useLiveDanmaku(isLive ? actualRoomId : 0); return ( {/* TopBar */} router.back()} style={styles.backBtn}> {room?.title ?? "直播间"} {isLive && hlsUrl ? ( { setLive(id, room?.title ?? '', room?.keyframe ?? '', hlsUrl); router.back(); }} > ) : ( )} {/* Player */} {/* TabBar */} setTab("intro")} > 简介 {tab === "intro" && } setTab("danmaku")} > 弹幕{danmakus.length > 0 ? ` ${danmakus.length}` : ""} {tab === "danmaku" && } {/* Content */} {loading ? ( ) : error ? ( {error} ) : ( <> {room?.title} {isLive ? ( 直播中 ) : ( 未开播 )} {formatCount(room?.online ?? 0)} {room?.parent_area_name ? ( {room.parent_area_name} ) : null} {room?.area_name ? ( {room.area_name} ) : null} {anchor && ( {anchor.uname} + 关注 )} {!!room?.description && ( {room.description} )} {}} style={[styles.danmakuFull, tab !== "danmaku" && styles.hidden]} hideHeader isLive maxItems={500} giftCounts={giftCounts} /> )} ); } const styles = StyleSheet.create({ safe: { flex: 1 }, topBar: { flexDirection: "row", alignItems: "center", paddingHorizontal: 8, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth, }, backBtn: { padding: 4 }, pipBtn: { padding: 4, width: 32, alignItems: 'center' }, topTitle: { flex: 1, fontSize: 15, fontWeight: "600", marginLeft: 4, }, loader: { marginVertical: 30 }, errorText: { textAlign: "center", color: "#f00", padding: 20 }, tabBar: { flexDirection: "row", borderBottomWidth: StyleSheet.hairlineWidth, }, tabItem: { paddingHorizontal: 20, paddingVertical: 10, alignItems: "center", position: "relative", }, tabLabel: { fontSize: 14, fontWeight: "500" }, tabActive: { color: "#00AEEC", fontWeight: "700" }, tabUnderline: { position: "absolute", bottom: 0, width: 24, height: 2, backgroundColor: "#00AEEC", borderRadius: 2, }, scroll: { flex: 1 }, titleSection: { padding: 14 }, title: { fontSize: 16, fontWeight: "600", lineHeight: 22, marginBottom: 8, }, metaRow: { flexDirection: "row", alignItems: "center", gap: 10, marginBottom: 8, }, livePill: { flexDirection: "row", alignItems: "center", backgroundColor: "#fff0f0", paddingHorizontal: 8, paddingVertical: 3, borderRadius: 4, gap: 4, }, offlinePill: { backgroundColor: "#f5f5f5" }, liveDot: { width: 6, height: 6, borderRadius: 3, backgroundColor: "#f00" }, livePillText: { fontSize: 12, color: "#f00", fontWeight: "600" }, offlinePillText: { color: "#999" }, onlineRow: { flexDirection: "row", alignItems: "center", gap: 3 }, onlineText: { fontSize: 12, color: "#999" }, areaRow: { flexDirection: "row", gap: 6 }, areaTag: { fontSize: 11, color: "#00AEEC", backgroundColor: "#e8f7fd", paddingHorizontal: 8, paddingVertical: 2, borderRadius: 10, }, divider: { height: StyleSheet.hairlineWidth, marginHorizontal: 14, }, anchorRow: { flexDirection: "row", alignItems: "center", paddingHorizontal: 14, paddingVertical: 12, }, avatar: { width: 40, height: 40, borderRadius: 20, marginRight: 10 }, anchorName: { flex: 1, fontSize: 14, fontWeight: "500" }, followBtn: { backgroundColor: "#00AEEC", paddingHorizontal: 14, paddingVertical: 5, borderRadius: 14, }, followTxt: { color: "#fff", fontSize: 12, fontWeight: "600" }, descBox: { padding: 14, paddingTop: 4 }, descText: { fontSize: 14, lineHeight: 22 }, danmakuFull: { flex: 1 }, hidden: { display: "none" }, });