import { Audio } from 'expo-av'; import { Image } from 'expo-image'; import { Stack, useLocalSearchParams, useRouter } from 'expo-router'; import React, { useEffect, useRef, useState } from 'react'; import { Dimensions, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import LotteryReel from './components/LotteryReel'; import { Images } from '@/constants/images'; import services from '@/services/api'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); // Sound URL from Uniapp config const SOUND_URL = 'https://cdn.acetoys.cn/kai_xin_ma_te/resource/magic/lottery.mp3'; export default function LotteryScreen() { const router = useRouter(); const params = useLocalSearchParams(); const insets = useSafeAreaInsets(); const num = Number(params.num) || 1; const tradeNo = params.tradeNo as string; const poolId = params.poolId as string; const [results, setResults] = useState([]); const [pool, setPool] = useState([]); const [loading, setLoading] = useState(true); const [sound, setSound] = useState(); // Timer Ref for cleanup const timerRef = useRef(null); // Layout calculations const itemWidth = (num === 1) ? 94 : 72; // Adjusted to 72 to match mask calculation exactly (was 71) // Uniapp width logic: // width() { return (screenWidth - (this.num == 1 ? 94 : 72)) / 2 } const maskWidth = (SCREEN_WIDTH - ((num === 1) ? 94 : 72)) / 2; const padding = SCREEN_WIDTH > 375 ? 32 : 0; useEffect(() => { init(); return () => { sound?.unloadAsync(); if (timerRef.current) clearTimeout(timerRef.current); }; }, []); const init = async () => { try { console.log('LotteryScreen Init: num', num, 'tradeNo', tradeNo, 'poolId', poolId); await Promise.all([loadData(), playSound()]); } catch (e) { console.error('LotteryScreen Init Error', e); } finally { setLoading(false); } } const playSound = async () => { try { const { sound } = await Audio.Sound.createAsync( { uri: SOUND_URL } ); setSound(sound); await sound.playAsync(); } catch (error) { console.log('Error playing sound', error); } }; const loadData = async () => { if (tradeNo) { try { const res: any = await services.award.getApplyResult(tradeNo); const list = res?.data?.inventoryList || res?.inventoryList || []; setResults(list); // 2. Fetch Pool (Goods) if not passed // In a real app we might pass this via context or params if small // For now, fetch detail to get the goods list if (poolId) { const detailRes = await services.award.getPoolDetail(poolId); const goods = detailRes?.luckGoodsList || []; setPool(goods); } else { // Fallback if no poolId, just use result as pool (not ideal as it's small) setPool(list); } // Auto show result after animation if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { handleFinish(list); }, 2800); // 2000ms duration + buffer } catch (error) { console.error('Load Data Error', error); // Safety fallback? } } }; const handleFinish = (finalResults?: any[]) => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } // Navigate to result modal/screen const data = finalResults || results; // We can navigate to a result page or show a local modal components // Assuming we have a result route router.replace({ pathname: '/lottery/result' as any, params: { results: JSON.stringify(data), poolId } }); }; const handleSkip = () => { sound?.stopAsync(); handleFinish(); }; if (loading) return ( ); return ( {/* Left Column Masks */} {/* Reels */} {results.map((item, index) => ( 0 ? pool : results} result={item} width={SCREEN_WIDTH} itemWidth={itemWidth} index={index} delay={index * 30} // Very fast stagger duration={2000} // Faster spin (was 3000) /> ))} {/* Right Column Masks */} {/* Middle Frame */} 跳过动画 ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#222335', }, bg: { position: 'absolute', width: '100%', height: '100%', zIndex: -1, }, maskPage: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.4)', zIndex: 0, }, wrapper: { flex: 1, // justifyContent: 'center', alignItems: 'center', marginTop: 100, // Adjust based on visual }, reelsContainer: { position: 'relative', alignItems: 'center', justifyContent: 'center', // backgroundColor: 'rgba(255,0,0,0.1)', // Debug }, reelRow: { overflow: 'hidden', width: SCREEN_WIDTH, alignItems: 'center', justifyContent: 'center', marginBottom: 12, // Gap between rows height: 94, // Height of one row (card height + tiny padding) }, height1: { height: 130, // 1 row + gaps justifyContent: 'center', }, height5: { height: 540, // 5 rows * (~100) justifyContent: 'center', paddingVertical: 10, }, maskLeft: { position: 'absolute', left: 0, top: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', zIndex: 10, }, maskRight: { position: 'absolute', right: 0, top: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', zIndex: 10, }, middleImage: { position: 'absolute', width: SCREEN_WIDTH, zIndex: 20, left: 0, // The image frame should be centered over the reels // top is handled dynamically or via flex centering in parent if possible }, bottom: { position: 'absolute', bottom: 50, width: '100%', alignItems: 'center', zIndex: 30, }, skipBtn: { backgroundColor: 'rgba(255,255,255,0.2)', paddingHorizontal: 30, paddingVertical: 10, borderRadius: 32, borderWidth: 1, borderColor: 'rgba(255,255,255,0.4)', }, skipText: { color: '#fff', fontSize: 14, }, });