import { Barrage } from "@/components/Barrage"; import { CouponModal } from "@/components/CouponModal"; import { Colors } from "@/constants/Colors"; import { Images } from "@/constants/images"; import { CouponItem, getNewbieGiftBagInfo, listValidCoupon } from "@/services/activity"; import { getFeedbackList, getPoolList, PoolItem } from "@/services/award"; import { getToken } from "@/services/http"; import { Image } from "expo-image"; import { useRouter } from "expo-router"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { ActivityIndicator, Dimensions, FlatList, RefreshControl, StatusBar, StyleSheet, Text, TextInput, TouchableOpacity, View, } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const typeList = [ { label: "全部", value: "", type: 0 }, { label: "高保赏", value: "UNLIMITED", type: 2 }, { label: "高爆赏", value: "UNLIMITED", type: 1 }, { label: "擂台赏", value: "YFS_PRO", type: 7 }, { label: "一番赏", value: "YFS_PRO", type: 0 }, ]; interface BarrageItem { id: string; content: string; nickname?: string; avatar: string; poolName?: string; type?: string; text?: string; poolId?: string; } // Static Header Component const StaticHeader = React.memo( ({ barrageList }: { barrageList: BarrageItem[] }) => ( {/* Featured Banner Area */} {/* Replace image with a styled cyber container for now */} MATRIX SUPPLY CRATE {/* Visual tech lines */} {/* Barrage Area */} {barrageList && barrageList.length > 0 && ( )} ), ); // Type Selector Component const TypeSelector = React.memo( ({ typeIndex, priceSort, onTypeChange, onSortChange, }: { typeIndex: number; priceSort: number; onTypeChange: (index: number) => void; onSortChange: () => void; }) => ( index.toString()} renderItem={({ item, index }) => { const isActive = typeIndex === index; return ( onTypeChange(index)} activeOpacity={0.7} > {item.label} ); }} contentContainerStyle={styles.typeListContent} /> 价格 {priceSort === 0 ? "-" : priceSort === 1 ? "↑" : "↓"} ), ); export default function BoxScreen() { const router = useRouter(); const insets = useSafeAreaInsets(); const [keyword, setKeyword] = useState(""); const [typeIndex, setTypeIndex] = useState(0); const [priceSort, setPriceSort] = useState(0); const [list, setList] = useState([]); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [current, setCurrent] = useState(1); const [hasMore, setHasMore] = useState(true); const [barrageList, setBarrageList] = useState([]); // 新人优惠券弹窗 const [couponVisible, setCouponVisible] = useState(false); const [couponList, setCouponList] = useState([]); const [newPeopleChecked, setNewPeopleChecked] = useState(false); // 加载弹幕 const loadBarrage = useCallback(async () => { try { const res = await getFeedbackList(); if (res.data) { setBarrageList(res.data); } } catch (error) { console.error("加载弹幕失败:", error); } }, []); // 新人大礼包检测(Vue: initNewPeople) const initNewPeople = useCallback(async () => { if (newPeopleChecked) return; const token = getToken(); if (!token) return; // 未登录不检测 try { const data = await getNewbieGiftBagInfo(); if (data) { setNewPeopleChecked(true); // Vue 原项目只是展示一张海报图片,这里跳过(无具体交互) } } catch (e) { // 静默失败 } }, [newPeopleChecked]); // 优惠券检测(Vue: initCoupon) const initCoupon = useCallback(async () => { const token = getToken(); if (!token) return; try { const coupons = await listValidCoupon("LUCK"); if (coupons && coupons.length > 0) { setCouponList(coupons); setCouponVisible(true); } } catch (e) { // 静默失败 } }, []); const loadData = useCallback( async (isRefresh = false) => { if (loading) return; const page = isRefresh ? 1 : current; if (!isRefresh && !hasMore) return; setLoading(true); try { const selectedType = typeList[typeIndex]; const res = await getPoolList({ current: page, size: 10, mode: selectedType.value || undefined, type: selectedType.type, keyword: keyword || undefined, priceSort: priceSort || undefined, }); if (res.success && res.data) { const newList = isRefresh ? res.data : [...list, ...res.data]; setList(newList); setCurrent(page + 1); setHasMore(newList.length < (res.count || 0)); } } catch (error) { console.error("加载奖池列表失败:", error); } setLoading(false); setRefreshing(false); }, [current, hasMore, loading, typeIndex, list, keyword, priceSort], ); useEffect(() => { loadData(true); loadBarrage(); initNewPeople(); initCoupon(); }, [typeIndex, priceSort]); // 执行搜索 const handleSearch = () => { setList([]); setCurrent(1); setHasMore(true); setTimeout(() => { loadData(true); }, 100); }; const handleRefresh = () => { setRefreshing(true); loadData(true); }; const handleLoadMore = () => { if (!loading && hasMore) { loadData(false); } }; const handleTypeChange = useCallback((index: number) => { setTypeIndex(index); setList([]); setCurrent(1); setHasMore(true); }, []); const handlePriceSort = useCallback(() => { setPriceSort((prev) => (prev + 1) % 3); }, []); const handleItemPress = useCallback( (item: PoolItem) => { if (item.status !== undefined && item.status !== 1) return; if (item.type === 7) { router.push({ pathname: "/boxInBox", params: { poolId: item.id }, } as any); } else if (item.mode === "UNLIMITED") { router.push({ pathname: "/award-detail", params: { poolId: item.id }, } as any); } else if (item.mode === "YFS_PRO") { router.push({ pathname: "/award-detail-yfs", params: { poolId: item.id }, } as any); } else { router.push(`/product/${item.id}` as any); } }, [router], ); const renderItem = useCallback( ({ item }: { item: PoolItem }) => ( handleItemPress(item)} activeOpacity={0.8} > {/* Cyber Border Overlay */} {item.name} ¥ {item.price} ), [handleItemPress], ); const ListHeader = useMemo( () => ( ), [barrageList, typeIndex, priceSort, handleTypeChange, handlePriceSort], ); const renderFooter = useCallback(() => { if (!loading) return null; return ( 数据传输中... ); }, [loading]); const renderEmpty = useCallback(() => { if (loading) return null; return ( 暂无数据 ); }, [loading]); return ( {/* Header */} CYBERBOX {/* List */} item.id} ListHeaderComponent={ListHeader} ListFooterComponent={renderFooter} ListEmptyComponent={renderEmpty} contentContainerStyle={styles.listContent} showsVerticalScrollIndicator={false} refreshControl={ } onEndReached={handleLoadMore} onEndReachedThreshold={0.3} /> {/* 新人优惠券弹窗 */} setCouponVisible(false)} onSuccess={() => { setCouponList([]); loadData(true); }} /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: Colors.darkBg, }, background: { flex: 1, backgroundColor: Colors.darkBg, }, header: { position: "relative", zIndex: 11, flexDirection: "row", alignItems: "center", paddingHorizontal: 15, paddingBottom: 10, backgroundColor: Colors.darkBg, }, logoArea: { marginRight: 15, }, logoText: { color: "#fff", fontWeight: "bold", fontSize: 18, fontStyle: "italic", }, logoHighlight: { color: Colors.neonPink, }, searchBar: { flex: 1, flexDirection: "row", alignItems: "center", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: 20, paddingHorizontal: 15, height: 32, borderWidth: 1, borderColor: "rgba(0, 243, 255, 0.3)", }, searchIcon: { width: 14, height: 14, marginRight: 8, }, searchInput: { flex: 1, color: "#fff", fontSize: 12, padding: 0, }, // Banner bannerContainer: { height: 180, marginHorizontal: 10, marginTop: 10, marginBottom: 20, borderRadius: 12, overflow: "hidden", }, cyberBanner: { flex: 1, backgroundColor: Colors.darkCard, justifyContent: "center", alignItems: "center", borderWidth: 1, borderColor: Colors.neonPink, position: "relative", }, bannerTitle: { fontSize: 32, fontWeight: "bold", color: "#fff", textShadowColor: Colors.neonPink, textShadowRadius: 10, fontStyle: "italic", }, bannerSubtitle: { fontSize: 14, color: Colors.neonBlue, letterSpacing: 4, marginTop: 5, }, techLine1: { position: "absolute", top: 10, left: 10, width: 20, height: 2, backgroundColor: Colors.neonBlue, }, techLine2: { position: "absolute", bottom: 10, right: 10, width: 20, height: 2, backgroundColor: Colors.neonBlue, }, // Type Selector typeSection: { flexDirection: "row", alignItems: "center", paddingHorizontal: 10, paddingVertical: 10, zIndex: 10, }, typeListContainer: { flex: 1, marginRight: 5, }, typeListContent: { paddingRight: 10, }, typeItem: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 15, backgroundColor: "rgba(255,255,255,0.05)", marginRight: 8, borderWidth: 1, borderColor: "transparent", }, typeItemActive: { backgroundColor: "rgba(0, 243, 255, 0.15)", borderColor: Colors.neonBlue, }, typeText: { color: Colors.textSecondary, fontSize: 12, fontWeight: "600", }, typeTextActive: { color: "#fff", textShadowColor: Colors.neonBlue, textShadowRadius: 5, }, sortBtn: { paddingHorizontal: 8, paddingVertical: 6, alignItems: "center", justifyContent: "center", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: 15, }, sortBtnText: { color: Colors.textTertiary, fontSize: 12, }, // List listContent: { paddingHorizontal: 10, paddingBottom: 100, }, itemContainer: { marginBottom: 12, }, itemCard: { width: "100%", backgroundColor: Colors.darkCard, borderRadius: 8, overflow: "hidden", borderWidth: 1, borderColor: "rgba(0, 243, 255, 0.2)", }, itemImageContainer: { height: 160, width: "100%", position: "relative", }, itemImage: { width: "100%", height: "100%", }, itemCyberOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: "rgba(0,0,0,0.1)", // Slight dark overlay borderBottomWidth: 1, borderBottomColor: Colors.neonBlue, }, itemInfo: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 12, }, itemName: { flex: 1, color: Colors.textPrimary, fontSize: 14, fontWeight: "bold", }, itemPrice: { color: Colors.neonBlue, fontSize: 16, fontWeight: "bold", marginLeft: 10, textShadowColor: Colors.neonBlue, textShadowRadius: 5, }, priceUnit: { fontSize: 12, marginRight: 2, }, priceStart: { fontSize: 10, color: Colors.textTertiary, fontWeight: "normal", }, // Footer footer: { paddingVertical: 15, alignItems: "center", flexDirection: "row", justifyContent: "center", }, footerText: { color: Colors.textTertiary, fontSize: 12, marginLeft: 8, }, empty: { alignItems: "center", paddingVertical: 50, }, emptyText: { color: Colors.textTertiary, fontSize: 14, }, // Barrage barrageSection: { marginVertical: 10, paddingHorizontal: 5, // Edge to edge }, });