From a8b29e562e63bd1500a30094cb9eca42c0543a56 Mon Sep 17 00:00:00 2001 From: Kakabay <2kakabayashyrberdyew@gmail.com> Date: Mon, 6 Jan 2025 00:17:39 +0500 Subject: [PATCH] websocket fix --- components/lottery/LotteryWinnersSection.tsx | 74 ++++------------- hooks/useWebSocketLottery.ts | 87 ++++++++++++++++---- next.config.js | 29 +++---- 3 files changed, 99 insertions(+), 91 deletions(-) diff --git a/components/lottery/LotteryWinnersSection.tsx b/components/lottery/LotteryWinnersSection.tsx index 54d76f3..cb161bf 100644 --- a/components/lottery/LotteryWinnersSection.tsx +++ b/components/lottery/LotteryWinnersSection.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect } from "react"; import { useLotteryAuth } from "@/store/useLotteryAuth"; import { LotteryWinnerDataSimplified } from "@/typings/lottery/lottery.types"; import LotteryWinnersList from "./winners/LotteryWinnersList"; @@ -10,14 +10,12 @@ import { useWebsocketLottery } from "@/hooks/useWebSocketLottery"; const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=0506"; const SLOT_COUNTER_DURATION = 20000; -const RECONNECT_INTERVAL = 5000; const LotteryWinnersSection = ({ lotteryStatus, }: { lotteryStatus: string; }) => { - // UI States const [winners, setWinners] = useState([]); const [currentNumber, setCurrentNumber] = useState(); const [isConfettiActive, setIsConfettiActive] = useState(false); @@ -33,8 +31,7 @@ const LotteryWinnersSection = ({ const [winnerText, setWinnerText] = useState(""); // WebSocket Hook - const { wsStatus } = useWebsocketLottery(WEBSOCKET_URL); - const wsRef = useRef(null); + const { wsStatus, subscribeToMessages } = useWebsocketLottery(WEBSOCKET_URL); useEffect(() => { if (lotteryData?.data.winners) { @@ -51,11 +48,7 @@ const LotteryWinnersSection = ({ }, [lotteryData]); useEffect(() => { - const handleWebSocketMessage = async (event: MessageEvent) => { - if (!event?.data) return; - - console.log("📩 Message received:", event.data); - + const unsubscribe = subscribeToMessages((event) => { const newWinner = JSON.parse(event.data); if ( !newWinner || @@ -67,8 +60,6 @@ const LotteryWinnersSection = ({ return; } - console.log("🎉 New winner selected:", newWinner); - const winnerData = { client: newWinner.phone, winner_no: newWinner.winner_no, @@ -80,16 +71,13 @@ const LotteryWinnersSection = ({ 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(() => { + setDisplayText("The winner is"); + setWinnerText(winnerData.client); + setWinnerSelectingStatus("selected"); + setIsConfettiActive(true); + setWinners((prev) => [...prev, winnerData]); + }, SLOT_COUNTER_DURATION); setTimeout(() => { setIsConfettiActive(false); @@ -98,44 +86,12 @@ const LotteryWinnersSection = ({ setDisplayText("..."); setWinnerText(""); }, 10000); + }); + + return () => { + unsubscribe(); }; - - if (wsStatus === "connected" && !wsRef.current) { - console.log("✅ WebSocket connected"); - wsRef.current = new WebSocket(WEBSOCKET_URL); - wsRef.current.addEventListener("message", handleWebSocketMessage); - - wsRef.current.addEventListener("error", (error) => { - console.error("❌ WebSocket error:", error); - }); - - wsRef.current.addEventListener("close", (event) => { - console.log("❌ WebSocket closed:", event); - }); - - // Set up ping to keep connection alive - const pingInterval = setInterval(() => { - if (wsRef.current?.readyState === WebSocket.OPEN) { - console.log("📤 Sending ping"); - wsRef.current.send(JSON.stringify({ type: "ping" })); - } - }, RECONNECT_INTERVAL); - - // Cleanup on unmount - return () => { - console.log("🔌 Cleaning up WebSocket connection"); - if (wsRef.current) { - wsRef.current.close(); - wsRef.current = null; - } - clearInterval(pingInterval); - }; - } else if (wsStatus === "connecting") { - console.log("🔄 WebSocket connecting..."); - } else if (wsStatus === "error") { - console.log("❗ WebSocket error detected"); - } - }, [wsStatus]); + }, [subscribeToMessages]); return (
diff --git a/hooks/useWebSocketLottery.ts b/hooks/useWebSocketLottery.ts index b6adae6..ca44011 100644 --- a/hooks/useWebSocketLottery.ts +++ b/hooks/useWebSocketLottery.ts @@ -1,45 +1,96 @@ import { useState, useEffect, useRef } from "react"; -export const useWebsocketLottery = ( - url: string, - reconnectInterval: number = 20000 -) => { +export const useWebsocketLottery = (url: string) => { const [wsStatus, setWsStatus] = useState< - "connecting" | "connected" | "error" + "connecting" | "connected" | "error" | "closed" >("connecting"); const wsRef = useRef(null); const reconnectTimeoutRef = useRef(null); + const messageListeners = useRef<((event: MessageEvent) => void)[]>([]); useEffect(() => { + let isMounted = true; + const setupWebSocket = () => { + if (!isMounted) return; + + console.log("🔄 Connecting to WebSocket..."); const socket = new WebSocket(url); wsRef.current = socket; - socket.addEventListener("open", () => setWsStatus("connected")); - socket.addEventListener("error", () => setWsStatus("error")); - socket.addEventListener("close", () => reconnectWebSocket()); + socket.onopen = () => { + if (!isMounted) return; + + console.log("✅ WebSocket connected"); + setWsStatus("connected"); + if (reconnectTimeoutRef.current) + clearTimeout(reconnectTimeoutRef.current); + }; + + socket.onmessage = (event) => { + if (!isMounted) return; + + console.log("📩 Message received:", event.data); + messageListeners.current.forEach((listener) => listener(event)); + }; + + socket.onerror = () => { + if (!isMounted) return; + + console.error("❌ WebSocket error"); + setWsStatus("error"); + }; + + socket.onclose = () => { + if (!isMounted) return; + + console.log("❌ WebSocket closed"); + setWsStatus("closed"); + reconnectWebSocket(); + }; }; const reconnectWebSocket = () => { + if (!isMounted) return; + + console.log("🔄 Reconnecting to WebSocket..."); reconnectTimeoutRef.current = setTimeout(() => { setupWebSocket(); - }, reconnectInterval); + }, 5000); }; setupWebSocket(); return () => { - if (reconnectTimeoutRef.current) - clearTimeout(reconnectTimeoutRef.current); - if (wsRef.current) wsRef.current.close(); - }; - }, [url, reconnectInterval]); + console.log("🔌 Cleaning up WebSocket connection..."); + isMounted = false; - const sendMessage = (message: string) => { - if (wsRef.current?.readyState === WebSocket.OPEN) { - wsRef.current.send(message); + if (wsRef.current) { + wsRef.current.close(); + wsRef.current = null; + } + + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + } + }; + }, [url]); + + const sendPing = () => { + if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { + console.log("📤 Sending ping"); + wsRef.current.send(JSON.stringify({ type: "ping" })); } }; - return { wsStatus, sendMessage }; + const subscribeToMessages = (listener: (event: MessageEvent) => void) => { + messageListeners.current.push(listener); + return () => { + messageListeners.current = messageListeners.current.filter( + (l) => l !== listener + ); + }; + }; + + return { wsStatus, sendPing, subscribeToMessages }; }; diff --git a/next.config.js b/next.config.js index d04ab19..78e2f0a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,31 +1,32 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - output: 'standalone', + output: "standalone", + reactStrictMode: false, images: { - domains: ['turkmentv.gov.tm', 'smstv.gov.tm', 'turkmenistaninfo.gov.tm'], + domains: ["turkmentv.gov.tm", "smstv.gov.tm", "turkmenistaninfo.gov.tm"], remotePatterns: [ { - protocol: 'https', - hostname: 'turkmentv.gov.tm', - port: '', + protocol: "https", + hostname: "turkmentv.gov.tm", + port: "", // pathname: '/account123/**', }, { - protocol: 'https', - hostname: 'sms.turkmentv.gov.tm', - port: '', + protocol: "https", + hostname: "sms.turkmentv.gov.tm", + port: "", // pathname: '/account123/**', }, { - protocol: 'https', - hostname: 'smstv.gov.tm', - port: '', + protocol: "https", + hostname: "smstv.gov.tm", + port: "", }, { - protocol: 'https', - hostname: 'turkmenistaninfo.gov.tm', - port: '', + protocol: "https", + hostname: "turkmenistaninfo.gov.tm", + port: "", }, ], },