From f6ac2d3cd47a71b16e4f710d5f72a54ca3a1272e Mon Sep 17 00:00:00 2001 From: Developer Date: Tue, 24 Mar 2026 21:58:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E5=92=8C=E7=9B=B4=E6=92=AD=E8=AF=A6=E6=83=85=E9=A1=B5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9A=97=E9=BB=91=E6=A8=A1=E5=BC=8F=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/live/[roomId].tsx | 42 ++++++++++----------- app/video/[bvid].tsx | 88 ++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 74 deletions(-) diff --git a/app/live/[roomId].tsx b/app/live/[roomId].tsx index 135d947..9881f34 100644 --- a/app/live/[roomId].tsx +++ b/app/live/[roomId].tsx @@ -17,12 +17,14 @@ 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"; 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); const { room, anchor, stream, loading, error, changeQuality } = useLiveDetail(id); @@ -34,17 +36,17 @@ export default function LiveDetailScreen() { const qualities = stream?.qualities ?? []; const currentQn = stream?.qn ?? 0; - // Use actual roomid from room detail (not the short/alias ID from the URL) const actualRoomId = room?.roomid ?? id; const { danmakus, giftCounts } = useLiveDanmaku(isLive ? actualRoomId : 0); + return ( - + {/* TopBar */} - + router.back()} style={styles.backBtn}> - + - + {room?.title ?? "直播间"} @@ -60,12 +62,12 @@ export default function LiveDetailScreen() { /> {/* TabBar */} - + setTab("intro")} > - + 简介 {tab === "intro" && } @@ -75,7 +77,7 @@ export default function LiveDetailScreen() { onPress={() => setTab("danmaku")} > 弹幕{danmakus.length > 0 ? ` ${danmakus.length}` : ""} @@ -83,7 +85,7 @@ export default function LiveDetailScreen() { - {/* Content — 两个面板始终挂载,通过 display 切换,保留弹幕列表状态 */} + {/* Content */} {loading ? ( ) : error ? ( @@ -95,7 +97,7 @@ export default function LiveDetailScreen() { showsVerticalScrollIndicator={false} > - {room?.title} + {room?.title} {isLive ? ( @@ -126,7 +128,7 @@ export default function LiveDetailScreen() { - + {anchor && ( @@ -134,7 +136,7 @@ export default function LiveDetailScreen() { source={{ uri: proxyImageUrl(anchor.face) }} style={styles.avatar} /> - {anchor.uname} + {anchor.uname} + 关注 @@ -143,7 +145,7 @@ export default function LiveDetailScreen() { {!!room?.description && ( - {room.description} + {room.description} )} @@ -166,14 +168,13 @@ export default function LiveDetailScreen() { } const styles = StyleSheet.create({ - safe: { flex: 1, backgroundColor: "#fff" }, + safe: { flex: 1 }, topBar: { flexDirection: "row", alignItems: "center", paddingHorizontal: 8, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#eee", }, backBtn: { padding: 4 }, topTitle: { @@ -181,15 +182,12 @@ const styles = StyleSheet.create({ fontSize: 15, fontWeight: "600", marginLeft: 4, - color: "#212121", }, loader: { marginVertical: 30 }, errorText: { textAlign: "center", color: "#f00", padding: 20 }, tabBar: { flexDirection: "row", borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#eee", - backgroundColor: "#fff", }, tabItem: { paddingHorizontal: 20, @@ -197,7 +195,7 @@ const styles = StyleSheet.create({ alignItems: "center", position: "relative", }, - tabLabel: { fontSize: 14, color: "#999", fontWeight: "500" }, + tabLabel: { fontSize: 14, fontWeight: "500" }, tabActive: { color: "#00AEEC", fontWeight: "700" }, tabUnderline: { position: "absolute", @@ -212,7 +210,6 @@ const styles = StyleSheet.create({ title: { fontSize: 16, fontWeight: "600", - color: "#212121", lineHeight: 22, marginBottom: 8, }, @@ -248,7 +245,6 @@ const styles = StyleSheet.create({ }, divider: { height: StyleSheet.hairlineWidth, - backgroundColor: "#f0f0f0", marginHorizontal: 14, }, anchorRow: { @@ -258,7 +254,7 @@ const styles = StyleSheet.create({ paddingVertical: 12, }, avatar: { width: 40, height: 40, borderRadius: 20, marginRight: 10 }, - anchorName: { flex: 1, fontSize: 14, color: "#212121", fontWeight: "500" }, + anchorName: { flex: 1, fontSize: 14, fontWeight: "500" }, followBtn: { backgroundColor: "#00AEEC", paddingHorizontal: 14, @@ -267,7 +263,7 @@ const styles = StyleSheet.create({ }, followTxt: { color: "#fff", fontSize: 12, fontWeight: "600" }, descBox: { padding: 14, paddingTop: 4 }, - descText: { fontSize: 14, color: "#555", lineHeight: 22 }, + descText: { fontSize: 14, lineHeight: 22 }, danmakuFull: { flex: 1 }, hidden: { display: "none" }, }); diff --git a/app/video/[bvid].tsx b/app/video/[bvid].tsx index 574bf78..f1fef41 100644 --- a/app/video/[bvid].tsx +++ b/app/video/[bvid].tsx @@ -22,12 +22,14 @@ import { useRelatedVideos } from "../../hooks/useRelatedVideos"; import { formatCount, formatDuration } from "../../utils/format"; import { proxyImageUrl } from "../../utils/imageUrl"; import { DownloadSheet } from "../../components/DownloadSheet"; +import { useTheme } from "../../utils/theme"; type Tab = "intro" | "comments" | "danmaku"; export default function VideoDetailScreen() { const { bvid } = useLocalSearchParams<{ bvid: string }>(); const router = useRouter(); + const theme = useTheme(); const { video, playData, @@ -67,20 +69,20 @@ export default function VideoDetailScreen() { }, [video?.cid]); return ( - + {/* TopBar */} - + router.back()} style={styles.backBtn}> - + - + {video?.title ?? "视频详情"} setShowDownload(true)} > - + @@ -105,15 +107,15 @@ export default function VideoDetailScreen() { qualities={qualities} /> - {/* TabBar — sits directly below player, always visible once video loads */} + {/* TabBar */} {video && ( - + setTab("intro")} > 简介 @@ -124,7 +126,7 @@ export default function VideoDetailScreen() { onPress={() => setTab("comments")} > 评论 {video.stat?.reply > 0 ? ` ${formatCount(video.stat.reply)}` : ""} @@ -136,7 +138,7 @@ export default function VideoDetailScreen() { onPress={() => setTab("danmaku")} > 弹幕 {danmakus.length > 0 ? ` ${formatCount(danmakus.length)}` : ""} @@ -168,13 +170,13 @@ export default function VideoDetailScreen() { source={{ uri: proxyImageUrl(video.owner.face) }} style={styles.avatar} /> - {video.owner.name} + {video.owner.name} + 关注 - - {video.title} + + {video.title} @@ -192,22 +194,22 @@ export default function VideoDetailScreen() { /> )} - + {video.desc || "暂无简介"} - - 推荐视频 + + 推荐视频 } renderItem={({ item }) => ( router.push(`/video/${item.bvid}` as any)} activeOpacity={0.85} > - + - + {item.title} + 排序 void; }) { + const theme = useTheme(); const episodes = season.sections?.[0]?.episodes ?? []; const currentIndex = episodes.findIndex((ep) => ep.bvid === currentBvid); const listRef = useRef(null); useEffect(() => { if (currentIndex <= 0 || episodes.length === 0) return; - // 等布局完成再滚动 const t = setTimeout(() => { listRef.current?.scrollToIndex({ index: currentIndex, - viewPosition: 0.5, // 居中 + viewPosition: 0.5, animated: false, }); }, 200); @@ -370,9 +372,9 @@ function SeasonSection({ }, [currentIndex, episodes.length]); return ( - + - 合集 · {season.title} + 合集 · {season.title} {season.ep_count}个视频 @@ -383,7 +385,6 @@ function SeasonSection({ data={episodes} keyExtractor={(ep) => ep.bvid} contentContainerStyle={{ paddingHorizontal: 12, gap: 10 }} - // 每个卡片宽 120,gap 10,让 FlatList 直接算任意索引的偏移量 getItemLayout={(_data, index) => ({ length: 130, offset: 12 + index * 130, @@ -394,20 +395,20 @@ function SeasonSection({ const isCurrent = ep.bvid === currentBvid; return ( !isCurrent && onEpisodePress(ep.bvid)} activeOpacity={0.8} > {ep.arc?.pic && ( )} 第{index + 1}集 - + {ep.title} @@ -419,14 +420,13 @@ function SeasonSection({ } const styles = StyleSheet.create({ - safe: { flex: 1, backgroundColor: "#fff" }, + safe: { flex: 1 }, topBar: { flexDirection: "row", alignItems: "center", paddingHorizontal: 8, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#eee", }, backBtn: { padding: 4 }, topTitle: { @@ -434,19 +434,16 @@ const styles = StyleSheet.create({ fontSize: 15, fontWeight: "600", marginLeft: 4, - color: "#212121", }, miniBtn: { padding: 4 }, loader: { marginVertical: 30 }, titleSection: { padding: 14, borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#f0f0f0", }, title: { fontSize: 13, fontWeight: "600", - color: "#212121", lineHeight: 22, marginBottom: 8, }, @@ -460,8 +457,8 @@ const styles = StyleSheet.create({ paddingBottom: 0, paddingTop: 12, }, - avatar: { width: 48, height: 48, borderRadius:30, marginRight: 10 }, - upName: { flex: 1, fontSize: 14, color: "#212121", fontWeight: "500" }, + avatar: { width: 48, height: 48, borderRadius: 30, marginRight: 10 }, + upName: { flex: 1, fontSize: 14, fontWeight: "500" }, followBtn: { backgroundColor: "#00AEEC", paddingHorizontal: 10, @@ -471,7 +468,6 @@ const styles = StyleSheet.create({ followTxt: { color: "#fff", fontSize: 12, fontWeight: "500" }, seasonBox: { borderTopWidth: StyleSheet.hairlineWidth, - borderTopColor: "#f0f0f0", paddingVertical: 10, }, seasonHeader: { @@ -481,23 +477,21 @@ const styles = StyleSheet.create({ paddingBottom: 8, gap: 4, }, - seasonTitle: { flex: 1, fontSize: 13, fontWeight: "600", color: "#212121" }, + seasonTitle: { flex: 1, fontSize: 13, fontWeight: "600" }, seasonCount: { fontSize: 12, color: "#999" }, epCard: { width: 120, borderRadius: 6, overflow: "hidden", - backgroundColor: "#f8f8f8", borderWidth: 1.5, borderColor: "transparent", }, epCardActive: { borderColor: "#00AEEC" }, - epThumb: { width: 120, height: 68, backgroundColor: "#eee" }, + epThumb: { width: 120, height: 68 }, epNum: { fontSize: 11, color: "#999", paddingHorizontal: 6, paddingTop: 4 }, epNumActive: { color: "#00AEEC", fontWeight: "600" }, epTitle: { fontSize: 12, - color: "#333", paddingHorizontal: 6, paddingBottom: 6, lineHeight: 16, @@ -505,7 +499,6 @@ const styles = StyleSheet.create({ tabBar: { flexDirection: "row", borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#eee", }, tabItem: { flex: 1, @@ -513,7 +506,7 @@ const styles = StyleSheet.create({ paddingVertical: 12, position: "relative", }, - tabLabel: { fontSize: 13, color: "#999" }, + tabLabel: { fontSize: 13 }, tabActive: { color: "#00AEEC" }, tabUnderline: { position: "absolute", @@ -525,28 +518,23 @@ const styles = StyleSheet.create({ }, tabScroll: { flex: 1 }, descBox: { padding: 16 }, - descText: { fontSize: 14, color: "#555", lineHeight: 22 }, + descText: { fontSize: 14, lineHeight: 22 }, danmakuTab: { flex: 1 }, emptyTxt: { textAlign: "center", color: "#bbb", padding: 30 }, relatedHeader: { paddingHorizontal: 14, paddingVertical: 10, borderTopWidth: StyleSheet.hairlineWidth, - borderTopColor: "#f0f0f0", - backgroundColor: "#f4f4f4", }, relatedHeaderText: { fontSize: 13, fontWeight: "600" as const, - color: "#212121", }, relatedCard: { flexDirection: "row", paddingHorizontal: 12, paddingVertical: 8, - backgroundColor: "#fff", borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#f0f0f0", gap: 10, }, relatedThumbWrap: { @@ -555,7 +543,6 @@ const styles = StyleSheet.create({ height: 68, borderRadius: 4, overflow: "hidden", - backgroundColor: "#eee", flexShrink: 0, }, relatedThumb: { width: 120, height: 68 }, @@ -574,7 +561,7 @@ const styles = StyleSheet.create({ justifyContent: "space-between", paddingVertical: 2, }, - relatedTitle: { fontSize: 13, color: "#212121", lineHeight: 18 }, + relatedTitle: { fontSize: 13, lineHeight: 18 }, relatedOwner: { fontSize: 12, color: "#999" }, relatedView: { fontSize: 11, color: "#bbb" }, sortRow: { @@ -584,7 +571,6 @@ const styles = StyleSheet.create({ paddingVertical: 10, gap: 8, borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: "#f0f0f0", }, sortLabel: { fontSize: 13, color: "#999", marginRight: 4 }, sortBtn: {