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
},
});