import React, { useEffect, useState, useRef, useMemo } from "react"; import { View, StyleSheet, Text, TouchableOpacity, ActivityIndicator, Animated, Image, RefreshControl, ViewToken, } from "react-native"; import { SafeAreaView, useSafeAreaInsets, } from "react-native-safe-area-context"; import { useRouter } from "expo-router"; import { Ionicons } from "@expo/vector-icons"; import { VideoCard } from "../components/VideoCard"; import { LoginModal } from "../components/LoginModal"; import { useVideoList } from "../hooks/useVideoList"; import { useAuthStore } from "../store/authStore"; import { toListRows, type ListRow, type BigRow } from "../utils/videoRows"; import { BigVideoCard } from "../components/BigVideoCard"; const HEADER_H = 44; const TAB_H = 38; const NAV_H = HEADER_H + TAB_H; const VIEWABILITY_CONFIG = { itemVisiblePercentThreshold: 50 }; export default function HomeScreen() { const router = useRouter(); const { videos, loading, refreshing, load, refresh } = useVideoList(); const { isLoggedIn, face, logout } = useAuthStore(); const [showLogin, setShowLogin] = useState(false); const insets = useSafeAreaInsets(); const [visibleBigKey, setVisibleBigKey] = useState(null); const rows = useMemo(() => toListRows(videos), [videos]); const onViewableItemsChangedRef = useRef( ({ viewableItems }: { viewableItems: ViewToken[] }) => { const bigRow = viewableItems.find( (v) => v.item && (v.item as ListRow).type === 'big', ); setVisibleBigKey( bigRow ? (bigRow.item as BigRow).item.bvid : null, ); }, ).current; const scrollY = useRef(new Animated.Value(0)).current; const headerTranslate = scrollY.interpolate({ inputRange: [0, NAV_H], outputRange: [0, -NAV_H], extrapolate: "clamp", }); useEffect(() => { load(); }, []); const renderItem = ({ item: row }: { item: ListRow }) => { if (row.type === 'big') { return ( router.push(`/video/${row.item.bvid}` as any)} /> ); } // Normal pair row return ( router.push(`/video/${row.left.bvid}` as any)} /> {row.right && ( router.push(`/video/${row.right!.bvid}` as any)} /> )} ); }; return ( row.type === 'big' ? `big-${row.item.bvid}` : `pair-${row.left.bvid}-${row.right?.bvid ?? 'empty'}-${index}` } contentContainerStyle={{ paddingTop: insets.top + NAV_H + 6, paddingBottom: insets.bottom + 16, }} renderItem={renderItem} refreshControl={ } onEndReached={() => load()} onEndReachedThreshold={0.5} viewabilityConfig={VIEWABILITY_CONFIG} onViewableItemsChanged={onViewableItemsChangedRef} ListFooterComponent={ {loading && } } onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true }, )} scrollEventThrottle={16} /> {/* 绝对定位导航栏:paddingTop 手动适配刘海/状态栏 */} 哔哩哔哩 (isLoggedIn ? logout() : setShowLogin(true))} > {isLoggedIn && face ? ( ) : ( )} 热门 setShowLogin(false)} /> ); } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: "#f4f4f4" }, listContainer: { flex: 1 }, navBar: { position: "absolute", top: 0, left: 0, right: 0, zIndex: 10, backgroundColor: "#fff", overflow: "hidden", // 安卓投影 elevation: 2, // iOS 投影 shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 2, }, header: { height: HEADER_H, flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 16, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: "#eee", }, logo: { fontSize: 20, fontWeight: "800", color: "#00AEEC", letterSpacing: -0.5, }, headerRight: { flexDirection: "row", gap: 8 }, headerBtn: { padding: 6 }, userAvatar: { width: 28, height: 28, borderRadius: 14, backgroundColor: "#eee", }, tabRow: { height: TAB_H, backgroundColor: "#fff", paddingHorizontal: 16, flexDirection: "row", alignItems: "center", }, tabActive: { fontSize: 15, fontWeight: "700", color: "#00AEEC" }, tabUnderline: { position: "absolute", bottom: 0, left: 16, width: 24, height: 2, backgroundColor: "#00AEEC", borderRadius: 1, }, row: { flexDirection: 'row', paddingHorizontal: 1, justifyContent: "flex-start" }, leftCol: { marginLeft: 4, marginRight: 2 }, rightCol: { marginLeft: 2, marginRight: 4 }, footer: { height: 48, alignItems: "center", justifyContent: "center" }, });