diff --git a/components/common/AnimatedText.tsx b/components/common/AnimatedText.tsx index c9c0a9c..6e6947a 100644 --- a/components/common/AnimatedText.tsx +++ b/components/common/AnimatedText.tsx @@ -26,13 +26,13 @@ const AnimatedText = ({ className={className} initial={{ y: initialY, opacity: 0 }} animate={{ y: 0, opacity: 1 }} - exit={{ y: initialY, opacity: 0 }}> + exit={{ y: '100%', opacity: 0 }}> {words.map((word, i) => ( { // UI States @@ -29,16 +30,15 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => 'not-selected' | 'is-selecting' | 'selected' >('not-selected'); const [pendingWinner, setPendingWinner] = useState(null); - - // Refs - const wsRef = useRef(null); - const pingIntervalRef = useRef(); - const mountedRef = useRef(false); - // Add new state for display text const [displayText, setDisplayText] = useState('...'); const [winnerText, setWinnerText] = useState(''); + const wsRef = useRef(null); + const pingIntervalRef = useRef(null); + const reconnectTimeoutRef = useRef(null); + const mountedRef = useRef(false); + // Initialize winners from lottery data useEffect(() => { if (lotteryData?.data.winners) { @@ -52,17 +52,18 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => } }, [lotteryData]); - // Handle WebSocket connection + // WebSocket Setup useEffect(() => { mountedRef.current = true; const setupWebSocket = () => { + if (wsRef.current) return; // Prevent duplicate connections + try { const socket = new WebSocket(WEBSOCKET_URL); wsRef.current = socket; socket.addEventListener('open', () => { - if (!mountedRef.current) return; console.log('WebSocket Connected'); setWsStatus('connected'); @@ -73,91 +74,101 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => }, PING_INTERVAL); }); - socket.addEventListener('message', async (event) => { - if (!mountedRef.current) return; - console.log('Message received:', event.data); + socket.addEventListener('message', handleWebSocketMessage); - try { - const newWinner = JSON.parse(event.data); - const winnerData = { - client: newWinner.phone, - winner_no: newWinner.winner_no, - ticket: newWinner.ticket, - }; - - // Set initial animation text - setDisplayText(`${winnerData.winner_no}-nji ýeňiji saýlanýar`); - - // Start the sequence - setWinnerSelectingStatus('is-selecting'); - setPendingWinner(winnerData); - setCurrentNumber(winnerData.ticket); - - // Wait for slot counter animation - await new Promise((resolve) => setTimeout(resolve, SLOT_COUNTER_DURATION)); - - // Update text to show winner's phone - setDisplayText('The winner is'); - setWinnerText(winnerData.client); - setWinnerSelectingStatus('selected'); - - setIsConfettiActive(true); - setWinners((prev) => [...prev, winnerData]); - - // Reset everything after 5 seconds - setTimeout(() => { - if (mountedRef.current) { - setIsConfettiActive(false); - // setIsSlotCounterAnimating(false); - setWinnerSelectingStatus('not-selected'); - setPendingWinner(null); - setDisplayText('...'); // Reset text - setWinnerText(''); - } - }, 5000); - } catch (error) { - console.error('Error processing message:', error); - setIsSlotCounterAnimating(false); - setPendingWinner(null); - setDisplayText('...'); // Reset text on error - } - }); - - socket.addEventListener('error', (error) => { - if (!mountedRef.current) return; - console.error('WebSocket Error:', error); + socket.addEventListener('error', () => { + console.error('WebSocket Error'); setWsStatus('error'); + reconnectWebSocket(); }); socket.addEventListener('close', () => { - if (!mountedRef.current) return; console.log('WebSocket Closed'); setWsStatus('error'); - - if (pingIntervalRef.current) { - clearInterval(pingIntervalRef.current); - } + reconnectWebSocket(); }); } catch (error) { console.error('Error creating WebSocket:', error); setWsStatus('error'); + reconnectWebSocket(); } }; - // Initial connection + const reconnectWebSocket = () => { + if (reconnectTimeoutRef.current) return; // Prevent multiple reconnect attempts + + reconnectTimeoutRef.current = setTimeout(() => { + console.log('Reconnecting WebSocket...'); + setupWebSocket(); + reconnectTimeoutRef.current = null; + }, RECONNECT_INTERVAL); + }; + setupWebSocket(); return () => { mountedRef.current = false; + if (pingIntervalRef.current) { clearInterval(pingIntervalRef.current); } + + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + } + if (wsRef.current) { wsRef.current.close(); + wsRef.current = null; } }; }, []); + // WebSocket Message Handler + const handleWebSocketMessage = async (event: MessageEvent) => { + if (!mountedRef.current) return; + + try { + const newWinner = JSON.parse(event.data); + if (!newWinner || !newWinner.phone || !newWinner.winner_no || !newWinner.ticket) { + throw new Error('Invalid data format received'); + } + + const winnerData = { + client: newWinner.phone, + winner_no: newWinner.winner_no, + ticket: newWinner.ticket, + }; + + setDisplayText(`${winnerData.winner_no}-nji ýeňiji saýlanýar`); + setWinnerSelectingStatus('is-selecting'); + setPendingWinner(winnerData); + setCurrentNumber(winnerData.ticket); + + await new Promise((resolve) => setTimeout(resolve, SLOT_COUNTER_DURATION)); + + setDisplayText('The winner is'); + setWinnerText(winnerData.client); + setWinnerSelectingStatus('selected'); + setIsConfettiActive(true); + + setWinners((prev) => [...prev, winnerData]); + + setTimeout(() => { + if (mountedRef.current) { + setIsConfettiActive(false); + setWinnerSelectingStatus('not-selected'); + setPendingWinner(null); + setDisplayText('...'); + setWinnerText(''); + } + }, 10000); + } catch (error) { + console.error('Error processing message:', error); + setDisplayText('Error processing winner'); + } + }; + return (
{wsStatus === 'error' && ( @@ -190,29 +201,30 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
-
- {winnerSelectingStatus === 'not-selected' || - winnerSelectingStatus === 'is-selecting' ? ( - - ) : ( -
+ +
+ {winnerSelectingStatus === 'not-selected' || + winnerSelectingStatus === 'is-selecting' ? ( - {winnerText && ( + ) : ( +
- )} -
- )} -
- + {winnerText && ( + + )} +
+ )} +
+