feat: 视频详情页和直播详情页支持暗黑模式主题切换

This commit is contained in:
Developer
2026-03-24 21:58:00 +08:00
parent 03aee783da
commit f6ac2d3cd4
2 changed files with 56 additions and 74 deletions

View File

@@ -17,12 +17,14 @@ import { LivePlayer } from "../../components/LivePlayer";
import DanmakuList from "../../components/DanmakuList"; import DanmakuList from "../../components/DanmakuList";
import { formatCount } from "../../utils/format"; import { formatCount } from "../../utils/format";
import { proxyImageUrl } from "../../utils/imageUrl"; import { proxyImageUrl } from "../../utils/imageUrl";
import { useTheme } from "../../utils/theme";
type Tab = "intro" | "danmaku"; type Tab = "intro" | "danmaku";
export default function LiveDetailScreen() { export default function LiveDetailScreen() {
const { roomId } = useLocalSearchParams<{ roomId: string }>(); const { roomId } = useLocalSearchParams<{ roomId: string }>();
const router = useRouter(); const router = useRouter();
const theme = useTheme();
const id = parseInt(roomId ?? "0", 10); const id = parseInt(roomId ?? "0", 10);
const { room, anchor, stream, loading, error, changeQuality } = const { room, anchor, stream, loading, error, changeQuality } =
useLiveDetail(id); useLiveDetail(id);
@@ -34,17 +36,17 @@ export default function LiveDetailScreen() {
const qualities = stream?.qualities ?? []; const qualities = stream?.qualities ?? [];
const currentQn = stream?.qn ?? 0; 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 actualRoomId = room?.roomid ?? id;
const { danmakus, giftCounts } = useLiveDanmaku(isLive ? actualRoomId : 0); const { danmakus, giftCounts } = useLiveDanmaku(isLive ? actualRoomId : 0);
return ( return (
<SafeAreaView style={styles.safe}> <SafeAreaView style={[styles.safe, { backgroundColor: theme.card }]}>
{/* TopBar */} {/* TopBar */}
<View style={styles.topBar}> <View style={[styles.topBar, { borderBottomColor: theme.border }]}>
<TouchableOpacity onPress={() => router.back()} style={styles.backBtn}> <TouchableOpacity onPress={() => router.back()} style={styles.backBtn}>
<Ionicons name="chevron-back" size={24} color="#212121" /> <Ionicons name="chevron-back" size={24} color={theme.text} />
</TouchableOpacity> </TouchableOpacity>
<Text style={styles.topTitle} numberOfLines={1}> <Text style={[styles.topTitle, { color: theme.text }]} numberOfLines={1}>
{room?.title ?? "直播间"} {room?.title ?? "直播间"}
</Text> </Text>
</View> </View>
@@ -60,12 +62,12 @@ export default function LiveDetailScreen() {
/> />
{/* TabBar */} {/* TabBar */}
<View style={styles.tabBar}> <View style={[styles.tabBar, { backgroundColor: theme.card, borderBottomColor: theme.border }]}>
<TouchableOpacity <TouchableOpacity
style={styles.tabItem} style={styles.tabItem}
onPress={() => setTab("intro")} onPress={() => setTab("intro")}
> >
<Text style={[styles.tabLabel, tab === "intro" && styles.tabActive]}> <Text style={[styles.tabLabel, { color: theme.textSub }, tab === "intro" && styles.tabActive]}>
</Text> </Text>
{tab === "intro" && <View style={styles.tabUnderline} />} {tab === "intro" && <View style={styles.tabUnderline} />}
@@ -75,7 +77,7 @@ export default function LiveDetailScreen() {
onPress={() => setTab("danmaku")} onPress={() => setTab("danmaku")}
> >
<Text <Text
style={[styles.tabLabel, tab === "danmaku" && styles.tabActive]} style={[styles.tabLabel, { color: theme.textSub }, tab === "danmaku" && styles.tabActive]}
> >
{danmakus.length > 0 ? ` ${danmakus.length}` : ""} {danmakus.length > 0 ? ` ${danmakus.length}` : ""}
</Text> </Text>
@@ -83,7 +85,7 @@ export default function LiveDetailScreen() {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Content — 两个面板始终挂载,通过 display 切换,保留弹幕列表状态 */} {/* Content */}
{loading ? ( {loading ? (
<ActivityIndicator style={styles.loader} color="#00AEEC" /> <ActivityIndicator style={styles.loader} color="#00AEEC" />
) : error ? ( ) : error ? (
@@ -95,7 +97,7 @@ export default function LiveDetailScreen() {
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
<View style={styles.titleSection}> <View style={styles.titleSection}>
<Text style={styles.title}>{room?.title}</Text> <Text style={[styles.title, { color: theme.text }]}>{room?.title}</Text>
<View style={styles.metaRow}> <View style={styles.metaRow}>
{isLive ? ( {isLive ? (
<View style={styles.livePill}> <View style={styles.livePill}>
@@ -126,7 +128,7 @@ export default function LiveDetailScreen() {
</View> </View>
</View> </View>
<View style={styles.divider} /> <View style={[styles.divider, { backgroundColor: theme.border }]} />
{anchor && ( {anchor && (
<View style={styles.anchorRow}> <View style={styles.anchorRow}>
@@ -134,7 +136,7 @@ export default function LiveDetailScreen() {
source={{ uri: proxyImageUrl(anchor.face) }} source={{ uri: proxyImageUrl(anchor.face) }}
style={styles.avatar} style={styles.avatar}
/> />
<Text style={styles.anchorName}>{anchor.uname}</Text> <Text style={[styles.anchorName, { color: theme.text }]}>{anchor.uname}</Text>
<TouchableOpacity style={styles.followBtn}> <TouchableOpacity style={styles.followBtn}>
<Text style={styles.followTxt}>+ </Text> <Text style={styles.followTxt}>+ </Text>
</TouchableOpacity> </TouchableOpacity>
@@ -143,7 +145,7 @@ export default function LiveDetailScreen() {
{!!room?.description && ( {!!room?.description && (
<View style={styles.descBox}> <View style={styles.descBox}>
<Text style={styles.descText}>{room.description}</Text> <Text style={[styles.descText, { color: theme.text }]}>{room.description}</Text>
</View> </View>
)} )}
</ScrollView> </ScrollView>
@@ -166,14 +168,13 @@ export default function LiveDetailScreen() {
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
safe: { flex: 1, backgroundColor: "#fff" }, safe: { flex: 1 },
topBar: { topBar: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 8, paddingVertical: 8,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#eee",
}, },
backBtn: { padding: 4 }, backBtn: { padding: 4 },
topTitle: { topTitle: {
@@ -181,15 +182,12 @@ const styles = StyleSheet.create({
fontSize: 15, fontSize: 15,
fontWeight: "600", fontWeight: "600",
marginLeft: 4, marginLeft: 4,
color: "#212121",
}, },
loader: { marginVertical: 30 }, loader: { marginVertical: 30 },
errorText: { textAlign: "center", color: "#f00", padding: 20 }, errorText: { textAlign: "center", color: "#f00", padding: 20 },
tabBar: { tabBar: {
flexDirection: "row", flexDirection: "row",
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#eee",
backgroundColor: "#fff",
}, },
tabItem: { tabItem: {
paddingHorizontal: 20, paddingHorizontal: 20,
@@ -197,7 +195,7 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
position: "relative", position: "relative",
}, },
tabLabel: { fontSize: 14, color: "#999", fontWeight: "500" }, tabLabel: { fontSize: 14, fontWeight: "500" },
tabActive: { color: "#00AEEC", fontWeight: "700" }, tabActive: { color: "#00AEEC", fontWeight: "700" },
tabUnderline: { tabUnderline: {
position: "absolute", position: "absolute",
@@ -212,7 +210,6 @@ const styles = StyleSheet.create({
title: { title: {
fontSize: 16, fontSize: 16,
fontWeight: "600", fontWeight: "600",
color: "#212121",
lineHeight: 22, lineHeight: 22,
marginBottom: 8, marginBottom: 8,
}, },
@@ -248,7 +245,6 @@ const styles = StyleSheet.create({
}, },
divider: { divider: {
height: StyleSheet.hairlineWidth, height: StyleSheet.hairlineWidth,
backgroundColor: "#f0f0f0",
marginHorizontal: 14, marginHorizontal: 14,
}, },
anchorRow: { anchorRow: {
@@ -258,7 +254,7 @@ const styles = StyleSheet.create({
paddingVertical: 12, paddingVertical: 12,
}, },
avatar: { width: 40, height: 40, borderRadius: 20, marginRight: 10 }, 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: { followBtn: {
backgroundColor: "#00AEEC", backgroundColor: "#00AEEC",
paddingHorizontal: 14, paddingHorizontal: 14,
@@ -267,7 +263,7 @@ const styles = StyleSheet.create({
}, },
followTxt: { color: "#fff", fontSize: 12, fontWeight: "600" }, followTxt: { color: "#fff", fontSize: 12, fontWeight: "600" },
descBox: { padding: 14, paddingTop: 4 }, descBox: { padding: 14, paddingTop: 4 },
descText: { fontSize: 14, color: "#555", lineHeight: 22 }, descText: { fontSize: 14, lineHeight: 22 },
danmakuFull: { flex: 1 }, danmakuFull: { flex: 1 },
hidden: { display: "none" }, hidden: { display: "none" },
}); });

View File

@@ -22,12 +22,14 @@ import { useRelatedVideos } from "../../hooks/useRelatedVideos";
import { formatCount, formatDuration } from "../../utils/format"; import { formatCount, formatDuration } from "../../utils/format";
import { proxyImageUrl } from "../../utils/imageUrl"; import { proxyImageUrl } from "../../utils/imageUrl";
import { DownloadSheet } from "../../components/DownloadSheet"; import { DownloadSheet } from "../../components/DownloadSheet";
import { useTheme } from "../../utils/theme";
type Tab = "intro" | "comments" | "danmaku"; type Tab = "intro" | "comments" | "danmaku";
export default function VideoDetailScreen() { export default function VideoDetailScreen() {
const { bvid } = useLocalSearchParams<{ bvid: string }>(); const { bvid } = useLocalSearchParams<{ bvid: string }>();
const router = useRouter(); const router = useRouter();
const theme = useTheme();
const { const {
video, video,
playData, playData,
@@ -67,20 +69,20 @@ export default function VideoDetailScreen() {
}, [video?.cid]); }, [video?.cid]);
return ( return (
<SafeAreaView style={styles.safe}> <SafeAreaView style={[styles.safe, { backgroundColor: theme.card }]}>
{/* TopBar */} {/* TopBar */}
<View style={styles.topBar}> <View style={[styles.topBar, { borderBottomColor: theme.border }]}>
<TouchableOpacity onPress={() => router.back()} style={styles.backBtn}> <TouchableOpacity onPress={() => router.back()} style={styles.backBtn}>
<Ionicons name="chevron-back" size={24} color="#212121" /> <Ionicons name="chevron-back" size={24} color={theme.text} />
</TouchableOpacity> </TouchableOpacity>
<Text style={styles.topTitle} numberOfLines={1}> <Text style={[styles.topTitle, { color: theme.text }]} numberOfLines={1}>
{video?.title ?? "视频详情"} {video?.title ?? "视频详情"}
</Text> </Text>
<TouchableOpacity <TouchableOpacity
style={styles.miniBtn} style={styles.miniBtn}
onPress={() => setShowDownload(true)} onPress={() => setShowDownload(true)}
> >
<Ionicons name="cloud-download-outline" size={22} color="#212121" /> <Ionicons name="cloud-download-outline" size={22} color={theme.text} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@@ -105,15 +107,15 @@ export default function VideoDetailScreen() {
qualities={qualities} qualities={qualities}
/> />
{/* TabBar — sits directly below player, always visible once video loads */} {/* TabBar */}
{video && ( {video && (
<View style={styles.tabBar}> <View style={[styles.tabBar, { borderBottomColor: theme.border }]}>
<TouchableOpacity <TouchableOpacity
style={styles.tabItem} style={styles.tabItem}
onPress={() => setTab("intro")} onPress={() => setTab("intro")}
> >
<Text <Text
style={[styles.tabLabel, tab === "intro" && styles.tabActive]} style={[styles.tabLabel, { color: theme.textSub }, tab === "intro" && styles.tabActive]}
> >
</Text> </Text>
@@ -124,7 +126,7 @@ export default function VideoDetailScreen() {
onPress={() => setTab("comments")} onPress={() => setTab("comments")}
> >
<Text <Text
style={[styles.tabLabel, tab === "comments" && styles.tabActive]} style={[styles.tabLabel, { color: theme.textSub }, tab === "comments" && styles.tabActive]}
> >
{video.stat?.reply > 0 ? ` ${formatCount(video.stat.reply)}` : ""} {video.stat?.reply > 0 ? ` ${formatCount(video.stat.reply)}` : ""}
@@ -136,7 +138,7 @@ export default function VideoDetailScreen() {
onPress={() => setTab("danmaku")} onPress={() => setTab("danmaku")}
> >
<Text <Text
style={[styles.tabLabel, tab === "danmaku" && styles.tabActive]} style={[styles.tabLabel, { color: theme.textSub }, tab === "danmaku" && styles.tabActive]}
> >
{danmakus.length > 0 ? ` ${formatCount(danmakus.length)}` : ""} {danmakus.length > 0 ? ` ${formatCount(danmakus.length)}` : ""}
@@ -168,13 +170,13 @@ export default function VideoDetailScreen() {
source={{ uri: proxyImageUrl(video.owner.face) }} source={{ uri: proxyImageUrl(video.owner.face) }}
style={styles.avatar} style={styles.avatar}
/> />
<Text style={styles.upName}>{video.owner.name}</Text> <Text style={[styles.upName, { color: theme.text }]}>{video.owner.name}</Text>
<TouchableOpacity style={styles.followBtn}> <TouchableOpacity style={styles.followBtn}>
<Text style={styles.followTxt}>+ </Text> <Text style={styles.followTxt}>+ </Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.titleSection}> <View style={[styles.titleSection, { borderBottomColor: theme.border }]}>
<Text style={styles.title}>{video.title}</Text> <Text style={[styles.title, { color: theme.text }]}>{video.title}</Text>
<View style={styles.statsRow}> <View style={styles.statsRow}>
<StatBadge icon="play" count={video.stat.view} /> <StatBadge icon="play" count={video.stat.view} />
<StatBadge icon="heart" count={video.stat.like} /> <StatBadge icon="heart" count={video.stat.like} />
@@ -192,22 +194,22 @@ export default function VideoDetailScreen() {
/> />
)} )}
<View style={styles.descBox}> <View style={styles.descBox}>
<Text style={styles.descText}> <Text style={[styles.descText, { color: theme.text }]}>
{video.desc || "暂无简介"} {video.desc || "暂无简介"}
</Text> </Text>
</View> </View>
<View style={styles.relatedHeader}> <View style={[styles.relatedHeader, { backgroundColor: theme.bg, borderTopColor: theme.border }]}>
<Text style={styles.relatedHeaderText}></Text> <Text style={[styles.relatedHeaderText, { color: theme.text }]}></Text>
</View> </View>
</> </>
} }
renderItem={({ item }) => ( renderItem={({ item }) => (
<TouchableOpacity <TouchableOpacity
style={styles.relatedCard} style={[styles.relatedCard, { backgroundColor: theme.card, borderBottomColor: theme.border }]}
onPress={() => router.push(`/video/${item.bvid}` as any)} onPress={() => router.push(`/video/${item.bvid}` as any)}
activeOpacity={0.85} activeOpacity={0.85}
> >
<View style={styles.relatedThumbWrap}> <View style={[styles.relatedThumbWrap, { backgroundColor: theme.bg }]}>
<Image <Image
source={{ uri: proxyImageUrl(item.pic) }} source={{ uri: proxyImageUrl(item.pic) }}
style={styles.relatedThumb} style={styles.relatedThumb}
@@ -220,7 +222,7 @@ export default function VideoDetailScreen() {
</View> </View>
</View> </View>
<View style={styles.relatedInfo}> <View style={styles.relatedInfo}>
<Text style={styles.relatedTitle} numberOfLines={2}> <Text style={[styles.relatedTitle, { color: theme.text }]} numberOfLines={2}>
{item.title} {item.title}
</Text> </Text>
<View <View
@@ -265,7 +267,7 @@ export default function VideoDetailScreen() {
onEndReachedThreshold={0.3} onEndReachedThreshold={0.3}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
ListHeaderComponent={ ListHeaderComponent={
<View style={styles.sortRow}> <View style={[styles.sortRow, { borderBottomColor: theme.border }]}>
<Text style={styles.sortLabel}></Text> <Text style={styles.sortLabel}></Text>
<TouchableOpacity <TouchableOpacity
style={[ style={[
@@ -352,17 +354,17 @@ function SeasonSection({
currentBvid: string; currentBvid: string;
onEpisodePress: (bvid: string) => void; onEpisodePress: (bvid: string) => void;
}) { }) {
const theme = useTheme();
const episodes = season.sections?.[0]?.episodes ?? []; const episodes = season.sections?.[0]?.episodes ?? [];
const currentIndex = episodes.findIndex((ep) => ep.bvid === currentBvid); const currentIndex = episodes.findIndex((ep) => ep.bvid === currentBvid);
const listRef = useRef<FlatList>(null); const listRef = useRef<FlatList>(null);
useEffect(() => { useEffect(() => {
if (currentIndex <= 0 || episodes.length === 0) return; if (currentIndex <= 0 || episodes.length === 0) return;
// 等布局完成再滚动
const t = setTimeout(() => { const t = setTimeout(() => {
listRef.current?.scrollToIndex({ listRef.current?.scrollToIndex({
index: currentIndex, index: currentIndex,
viewPosition: 0.5, // 居中 viewPosition: 0.5,
animated: false, animated: false,
}); });
}, 200); }, 200);
@@ -370,9 +372,9 @@ function SeasonSection({
}, [currentIndex, episodes.length]); }, [currentIndex, episodes.length]);
return ( return (
<View style={styles.seasonBox}> <View style={[styles.seasonBox, { borderTopColor: theme.border }]}>
<View style={styles.seasonHeader}> <View style={styles.seasonHeader}>
<Text style={styles.seasonTitle}> · {season.title}</Text> <Text style={[styles.seasonTitle, { color: theme.text }]}> · {season.title}</Text>
<Text style={styles.seasonCount}>{season.ep_count}</Text> <Text style={styles.seasonCount}>{season.ep_count}</Text>
<Ionicons name="chevron-forward" size={14} color="#999" /> <Ionicons name="chevron-forward" size={14} color="#999" />
</View> </View>
@@ -383,7 +385,6 @@ function SeasonSection({
data={episodes} data={episodes}
keyExtractor={(ep) => ep.bvid} keyExtractor={(ep) => ep.bvid}
contentContainerStyle={{ paddingHorizontal: 12, gap: 10 }} contentContainerStyle={{ paddingHorizontal: 12, gap: 10 }}
// 每个卡片宽 120gap 10让 FlatList 直接算任意索引的偏移量
getItemLayout={(_data, index) => ({ getItemLayout={(_data, index) => ({
length: 130, length: 130,
offset: 12 + index * 130, offset: 12 + index * 130,
@@ -394,20 +395,20 @@ function SeasonSection({
const isCurrent = ep.bvid === currentBvid; const isCurrent = ep.bvid === currentBvid;
return ( return (
<TouchableOpacity <TouchableOpacity
style={[styles.epCard, isCurrent && styles.epCardActive]} style={[styles.epCard, { backgroundColor: theme.bg }, isCurrent && styles.epCardActive]}
onPress={() => !isCurrent && onEpisodePress(ep.bvid)} onPress={() => !isCurrent && onEpisodePress(ep.bvid)}
activeOpacity={0.8} activeOpacity={0.8}
> >
{ep.arc?.pic && ( {ep.arc?.pic && (
<Image <Image
source={{ uri: proxyImageUrl(ep.arc.pic) }} source={{ uri: proxyImageUrl(ep.arc.pic) }}
style={styles.epThumb} style={[styles.epThumb, { backgroundColor: theme.card }]}
/> />
)} )}
<Text style={[styles.epNum, isCurrent && styles.epNumActive]}> <Text style={[styles.epNum, isCurrent && styles.epNumActive]}>
{index + 1} {index + 1}
</Text> </Text>
<Text style={styles.epTitle} numberOfLines={2}> <Text style={[styles.epTitle, { color: theme.text }]} numberOfLines={2}>
{ep.title} {ep.title}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
@@ -419,14 +420,13 @@ function SeasonSection({
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
safe: { flex: 1, backgroundColor: "#fff" }, safe: { flex: 1 },
topBar: { topBar: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 8, paddingVertical: 8,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#eee",
}, },
backBtn: { padding: 4 }, backBtn: { padding: 4 },
topTitle: { topTitle: {
@@ -434,19 +434,16 @@ const styles = StyleSheet.create({
fontSize: 15, fontSize: 15,
fontWeight: "600", fontWeight: "600",
marginLeft: 4, marginLeft: 4,
color: "#212121",
}, },
miniBtn: { padding: 4 }, miniBtn: { padding: 4 },
loader: { marginVertical: 30 }, loader: { marginVertical: 30 },
titleSection: { titleSection: {
padding: 14, padding: 14,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#f0f0f0",
}, },
title: { title: {
fontSize: 13, fontSize: 13,
fontWeight: "600", fontWeight: "600",
color: "#212121",
lineHeight: 22, lineHeight: 22,
marginBottom: 8, marginBottom: 8,
}, },
@@ -460,8 +457,8 @@ const styles = StyleSheet.create({
paddingBottom: 0, paddingBottom: 0,
paddingTop: 12, paddingTop: 12,
}, },
avatar: { width: 48, height: 48, borderRadius:30, marginRight: 10 }, avatar: { width: 48, height: 48, borderRadius: 30, marginRight: 10 },
upName: { flex: 1, fontSize: 14, color: "#212121", fontWeight: "500" }, upName: { flex: 1, fontSize: 14, fontWeight: "500" },
followBtn: { followBtn: {
backgroundColor: "#00AEEC", backgroundColor: "#00AEEC",
paddingHorizontal: 10, paddingHorizontal: 10,
@@ -471,7 +468,6 @@ const styles = StyleSheet.create({
followTxt: { color: "#fff", fontSize: 12, fontWeight: "500" }, followTxt: { color: "#fff", fontSize: 12, fontWeight: "500" },
seasonBox: { seasonBox: {
borderTopWidth: StyleSheet.hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#f0f0f0",
paddingVertical: 10, paddingVertical: 10,
}, },
seasonHeader: { seasonHeader: {
@@ -481,23 +477,21 @@ const styles = StyleSheet.create({
paddingBottom: 8, paddingBottom: 8,
gap: 4, gap: 4,
}, },
seasonTitle: { flex: 1, fontSize: 13, fontWeight: "600", color: "#212121" }, seasonTitle: { flex: 1, fontSize: 13, fontWeight: "600" },
seasonCount: { fontSize: 12, color: "#999" }, seasonCount: { fontSize: 12, color: "#999" },
epCard: { epCard: {
width: 120, width: 120,
borderRadius: 6, borderRadius: 6,
overflow: "hidden", overflow: "hidden",
backgroundColor: "#f8f8f8",
borderWidth: 1.5, borderWidth: 1.5,
borderColor: "transparent", borderColor: "transparent",
}, },
epCardActive: { borderColor: "#00AEEC" }, epCardActive: { borderColor: "#00AEEC" },
epThumb: { width: 120, height: 68, backgroundColor: "#eee" }, epThumb: { width: 120, height: 68 },
epNum: { fontSize: 11, color: "#999", paddingHorizontal: 6, paddingTop: 4 }, epNum: { fontSize: 11, color: "#999", paddingHorizontal: 6, paddingTop: 4 },
epNumActive: { color: "#00AEEC", fontWeight: "600" }, epNumActive: { color: "#00AEEC", fontWeight: "600" },
epTitle: { epTitle: {
fontSize: 12, fontSize: 12,
color: "#333",
paddingHorizontal: 6, paddingHorizontal: 6,
paddingBottom: 6, paddingBottom: 6,
lineHeight: 16, lineHeight: 16,
@@ -505,7 +499,6 @@ const styles = StyleSheet.create({
tabBar: { tabBar: {
flexDirection: "row", flexDirection: "row",
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#eee",
}, },
tabItem: { tabItem: {
flex: 1, flex: 1,
@@ -513,7 +506,7 @@ const styles = StyleSheet.create({
paddingVertical: 12, paddingVertical: 12,
position: "relative", position: "relative",
}, },
tabLabel: { fontSize: 13, color: "#999" }, tabLabel: { fontSize: 13 },
tabActive: { color: "#00AEEC" }, tabActive: { color: "#00AEEC" },
tabUnderline: { tabUnderline: {
position: "absolute", position: "absolute",
@@ -525,28 +518,23 @@ const styles = StyleSheet.create({
}, },
tabScroll: { flex: 1 }, tabScroll: { flex: 1 },
descBox: { padding: 16 }, descBox: { padding: 16 },
descText: { fontSize: 14, color: "#555", lineHeight: 22 }, descText: { fontSize: 14, lineHeight: 22 },
danmakuTab: { flex: 1 }, danmakuTab: { flex: 1 },
emptyTxt: { textAlign: "center", color: "#bbb", padding: 30 }, emptyTxt: { textAlign: "center", color: "#bbb", padding: 30 },
relatedHeader: { relatedHeader: {
paddingHorizontal: 14, paddingHorizontal: 14,
paddingVertical: 10, paddingVertical: 10,
borderTopWidth: StyleSheet.hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#f0f0f0",
backgroundColor: "#f4f4f4",
}, },
relatedHeaderText: { relatedHeaderText: {
fontSize: 13, fontSize: 13,
fontWeight: "600" as const, fontWeight: "600" as const,
color: "#212121",
}, },
relatedCard: { relatedCard: {
flexDirection: "row", flexDirection: "row",
paddingHorizontal: 12, paddingHorizontal: 12,
paddingVertical: 8, paddingVertical: 8,
backgroundColor: "#fff",
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#f0f0f0",
gap: 10, gap: 10,
}, },
relatedThumbWrap: { relatedThumbWrap: {
@@ -555,7 +543,6 @@ const styles = StyleSheet.create({
height: 68, height: 68,
borderRadius: 4, borderRadius: 4,
overflow: "hidden", overflow: "hidden",
backgroundColor: "#eee",
flexShrink: 0, flexShrink: 0,
}, },
relatedThumb: { width: 120, height: 68 }, relatedThumb: { width: 120, height: 68 },
@@ -574,7 +561,7 @@ const styles = StyleSheet.create({
justifyContent: "space-between", justifyContent: "space-between",
paddingVertical: 2, paddingVertical: 2,
}, },
relatedTitle: { fontSize: 13, color: "#212121", lineHeight: 18 }, relatedTitle: { fontSize: 13, lineHeight: 18 },
relatedOwner: { fontSize: 12, color: "#999" }, relatedOwner: { fontSize: 12, color: "#999" },
relatedView: { fontSize: 11, color: "#bbb" }, relatedView: { fontSize: 11, color: "#bbb" },
sortRow: { sortRow: {
@@ -584,7 +571,6 @@ const styles = StyleSheet.create({
paddingVertical: 10, paddingVertical: 10,
gap: 8, gap: 8,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#f0f0f0",
}, },
sortLabel: { fontSize: 13, color: "#999", marginRight: 4 }, sortLabel: { fontSize: 13, color: "#999", marginRight: 4 },
sortBtn: { sortBtn: {