Compare commits
11 Commits
318fb775a4
...
7f59cab5c5
| Author | SHA1 | Date |
|---|---|---|
|
|
7f59cab5c5 | |
|
|
5f15114f96 | |
|
|
bd79394c89 | |
|
|
496b0257b0 | |
|
|
39a9116001 | |
|
|
43bb74c47e | |
|
|
ecaaa6e2cc | |
|
|
376ffa651a | |
|
|
00332b4648 | |
|
|
209f536693 | |
|
|
cec4f87c26 |
|
|
@ -1,81 +1,112 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLotteryAuth } from '@/store/useLotteryAuth';
|
||||
import ProtectedRoute from '@/components/lottery/auth/ProtectedRoute';
|
||||
import LotteryHeader from '@/components/lottery/LotteryHeader';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
||||
import ProtectedRoute from "@/components/lottery/auth/ProtectedRoute";
|
||||
import LotteryHeader from "@/components/lottery/LotteryHeader";
|
||||
|
||||
import LotteryWinnersSection from '@/components/lottery/LotteryWinnersSection';
|
||||
import LotteryRulesSection from '@/components/lottery/rules/LotteryRulesSection';
|
||||
import LotteryCountDown from '@/components/lottery/countDown/LotteryCountDown';
|
||||
import LotteryCountDownAllert from '@/components/lottery/countDown/countDownAllert/LotteryCountDownAllert';
|
||||
import { LotteryWinnerDataSimplified } from '@/typings/lottery/lottery.types';
|
||||
import { Queries } from '@/api/queries';
|
||||
import Link from 'next/link';
|
||||
import LotteryWinnersSection from "@/components/lottery/LotteryWinnersSection";
|
||||
import LotteryRulesSection from "@/components/lottery/rules/LotteryRulesSection";
|
||||
import LotteryCountDown from "@/components/lottery/countDown/LotteryCountDown";
|
||||
import { Queries } from "@/api/queries";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Loader from "@/components/Loader";
|
||||
|
||||
const LotteryPage = () => {
|
||||
const { lotteryData, setAuth } = useLotteryAuth();
|
||||
const [status, setStatus] = useState<'not-started' | 'started' | 'ended'>('not-started');
|
||||
const [status, setStatus] = useState<"not-started" | "started" | "ended">(
|
||||
"not-started"
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
|
||||
// ✅ Fetch fresh data on page load
|
||||
useEffect(() => {
|
||||
const phone = localStorage.getItem('lotteryPhone');
|
||||
const code = localStorage.getItem('lotteryCode');
|
||||
const checkAuth = async () => {
|
||||
// ✅ Check credentials from localStorage
|
||||
const phone = localStorage.getItem("lotteryPhone");
|
||||
const code = localStorage.getItem("lotteryCode");
|
||||
|
||||
if (phone && code) {
|
||||
Queries.authenticateLottery(phone, code)
|
||||
.then((response) => {
|
||||
setAuth(response, phone, code);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to fetch lottery data:', err);
|
||||
});
|
||||
}
|
||||
}, [setAuth]);
|
||||
if (phone && code) {
|
||||
try {
|
||||
// ✅ Authenticate using stored credentials
|
||||
const response = await Queries.authenticateLottery(phone, code);
|
||||
|
||||
if (response.errorMessage) {
|
||||
// If authentication fails, redirect to the auth page
|
||||
console.log("redirecting form lottery/");
|
||||
|
||||
router.replace("/lottery/auth");
|
||||
} else {
|
||||
// ✅ Set the authenticated state
|
||||
setAuth(response, phone, code);
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Authentication failed:", err);
|
||||
router.replace("/lottery/auth");
|
||||
}
|
||||
} else {
|
||||
// Redirect to the auth page if no credentials are found
|
||||
router.replace("/lottery/auth");
|
||||
}
|
||||
};
|
||||
|
||||
checkAuth();
|
||||
}, [router, setAuth]);
|
||||
|
||||
if (isLoading && !lotteryData?.data) {
|
||||
<div className="flex w-full h-[90vh] justify-center items-center">
|
||||
<Loader />
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||
{lotteryData && (
|
||||
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
||||
<LotteryHeader
|
||||
title={lotteryData.data.title}
|
||||
description={lotteryData.data.description}
|
||||
image={lotteryData.data.image}
|
||||
smsCode={lotteryData.data.sms_code}
|
||||
startDate={lotteryData.data.start_time}
|
||||
/>
|
||||
{lotteryData?.data && (
|
||||
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||
{lotteryData && (
|
||||
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
||||
<LotteryHeader
|
||||
title={lotteryData.data.title}
|
||||
description={lotteryData.data.description}
|
||||
image={lotteryData.data.image}
|
||||
smsCode={lotteryData.data.sms_code}
|
||||
startDate={lotteryData.data.start_time}
|
||||
/>
|
||||
|
||||
{status === 'not-started' ? (
|
||||
<div className="container">
|
||||
<LotteryCountDown
|
||||
lotteryStatus={status}
|
||||
setLotteryStatus={setStatus}
|
||||
endDate={lotteryData.data.end_time}
|
||||
startDate={lotteryData.data.start_time}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<LotteryRulesSection />
|
||||
|
||||
<div className="flex flex-col gap-10">
|
||||
{lotteryData && (status === 'ended' || status === 'started') && (
|
||||
<LotteryWinnersSection lotteryStatus={status} />
|
||||
{status === "not-started" ? (
|
||||
<div className="container">
|
||||
<LotteryCountDown
|
||||
lotteryStatus={status}
|
||||
setLotteryStatus={setStatus}
|
||||
endDate={lotteryData.data.end_time}
|
||||
startDate={lotteryData.data.start_time}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full">
|
||||
<div className="container">
|
||||
<Link
|
||||
href="/lottery/auth"
|
||||
className="sm:text-textLarge sm:leading-textLarge text-[16px] rounded-full leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center border-2 border-lightPrimary hover:bg-lightPrimary font-medium text-lightPrimary hover:text-lightOnPrimary disabled:opacity-50 transition-all duration-300">
|
||||
Çykmak
|
||||
</Link>
|
||||
|
||||
<LotteryRulesSection />
|
||||
|
||||
<div className="flex flex-col gap-10">
|
||||
{lotteryData && (status === "ended" || status === "started") && (
|
||||
<LotteryWinnersSection lotteryStatus={status} />
|
||||
)}
|
||||
<div className="w-full">
|
||||
<div className="container">
|
||||
<Link
|
||||
href="/lottery/auth"
|
||||
className="sm:text-textLarge sm:leading-textLarge text-[16px] rounded-full leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center border-2 border-lightPrimary hover:bg-lightPrimary font-medium text-lightPrimary hover:text-lightOnPrimary disabled:opacity-50 transition-all duration-300"
|
||||
>
|
||||
Çykmak
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ProtectedRoute>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@ import { useLotteryAuth } from "@/store/useLotteryAuth";
|
|||
import { LotteryWinnerDataSimplified } from "@/typings/lottery/lottery.types";
|
||||
import LotteryWinnersList from "./winners/LotteryWinnersList";
|
||||
import LotterySlotCounter from "./slotCounter/LotterySlotCounter";
|
||||
import ReactConfetti from "react-confetti";
|
||||
import { useWindowSize } from "react-use";
|
||||
import AnimatedText from "@/components/common/AnimatedText";
|
||||
import { useWebsocketLottery } from "@/hooks/useWebSocketLottery";
|
||||
import Confetti from "../common/Confetti";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=0506";
|
||||
const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=";
|
||||
const SLOT_COUNTER_DURATION = 30000;
|
||||
|
||||
const LotteryWinnersSection = ({
|
||||
|
|
@ -36,13 +34,15 @@ const LotteryWinnersSection = ({
|
|||
>([]); // Queue for incoming WebSocket messages
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false); // Track if a message is being processed
|
||||
|
||||
const { wsStatus, subscribeToMessages } = useWebsocketLottery(WEBSOCKET_URL);
|
||||
const { wsStatus, subscribeToMessages } = useWebsocketLottery(
|
||||
`${WEBSOCKET_URL}${lotteryData?.data.sms_number}`
|
||||
);
|
||||
|
||||
// Simulate WebSocket messages for testing
|
||||
// Simulate WebSocket message for testing
|
||||
const simulateMessage = () => {
|
||||
const dummyWinner: LotteryWinnerDataSimplified = {
|
||||
client: `9936${Math.floor(10000000 + Math.random() * 90000000)}`,
|
||||
winner_no: winners.length + 1,
|
||||
phone: `9936${Math.floor(10000000 + Math.random() * 90000000)}`, // Generate random client number
|
||||
winner_no: winners.length + 1, // Increment winner number
|
||||
ticket: `${Math.floor(Math.random() * 99)
|
||||
.toString()
|
||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
||||
|
|
@ -53,17 +53,28 @@ const LotteryWinnersSection = ({
|
|||
.toString()
|
||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
||||
.toString()
|
||||
.padStart(2, "0")}`,
|
||||
.padStart(2, "0")}`, // Generate random ticket
|
||||
};
|
||||
|
||||
console.log("📩 Simulated Message:", dummyWinner); // Log the simulated message
|
||||
setMessageQueue((prevQueue) => [...prevQueue, dummyWinner]);
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// const interval = setInterval(() => {
|
||||
// simulateMessage();
|
||||
// }, 20000); // Trigger every 10 seconds
|
||||
|
||||
// return () => clearInterval(interval); // Clean up interval on unmount
|
||||
// }, []);
|
||||
|
||||
// Initialize winners from lottery data
|
||||
useEffect(() => {
|
||||
console.log("🎟️ Lottery Data:", lotteryData);
|
||||
|
||||
if (lotteryData && lotteryData.data.winners.length > 0) {
|
||||
const simplifiedWinners = lotteryData.data.winners.map((winner) => ({
|
||||
client: winner.client,
|
||||
phone: winner.client,
|
||||
winner_no: winner.winner_no,
|
||||
ticket: winner.ticket,
|
||||
}));
|
||||
|
|
@ -72,7 +83,7 @@ const LotteryWinnersSection = ({
|
|||
const lastWinner = simplifiedWinners[simplifiedWinners.length - 1];
|
||||
setWinnerSelectingStatus("selected");
|
||||
setTopText(`${lastWinner.winner_no}-nji ýeňiji`);
|
||||
setBottomText(lastWinner.client);
|
||||
setBottomText(lastWinner.phone);
|
||||
setIsConfettiActive(true);
|
||||
}
|
||||
}, [lotteryData]);
|
||||
|
|
@ -82,9 +93,13 @@ const LotteryWinnersSection = ({
|
|||
const unsubscribe = subscribeToMessages((event) => {
|
||||
try {
|
||||
const newWinner: LotteryWinnerDataSimplified = JSON.parse(event.data);
|
||||
console.log("📩 WebSocket Message Received:", newWinner); // Log the parsed message
|
||||
|
||||
// Add new message to the queue
|
||||
setMessageQueue((prevQueue) => [...prevQueue, newWinner]);
|
||||
setMessageQueue((prevQueue) => {
|
||||
console.log("📥 Adding to Queue:", newWinner);
|
||||
return [...prevQueue, newWinner];
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error parsing WebSocket message:", error);
|
||||
}
|
||||
|
|
@ -95,6 +110,8 @@ const LotteryWinnersSection = ({
|
|||
|
||||
// Process queue when a new message is added
|
||||
useEffect(() => {
|
||||
console.log("📋 Current Message Queue:", messageQueue);
|
||||
|
||||
if (!isProcessing && messageQueue.length > 0) {
|
||||
processQueue();
|
||||
}
|
||||
|
|
@ -107,6 +124,8 @@ const LotteryWinnersSection = ({
|
|||
setIsProcessing(true); // Lock processing
|
||||
const message = messageQueue[0]; // Get the first message in the queue
|
||||
|
||||
console.log("⚙️ Processing Message:", message); // Debug Log 4: Log the message being processed
|
||||
|
||||
try {
|
||||
await handleMessage(message); // Process the message
|
||||
} catch (error) {
|
||||
|
|
@ -119,6 +138,7 @@ const LotteryWinnersSection = ({
|
|||
|
||||
// Handle the logic for processing a single WebSocket message
|
||||
const handleMessage = async (winner: LotteryWinnerDataSimplified) => {
|
||||
console.log("⬇️ Updating Top and Bottom Text:", winner); // Debug Log 5: Log winner data before setting states
|
||||
setIsConfettiActive(false);
|
||||
setTopText(`${winner.winner_no}-nji ýeňiji saýlanýar`);
|
||||
setBottomText("...");
|
||||
|
|
@ -131,7 +151,9 @@ const LotteryWinnersSection = ({
|
|||
|
||||
// Finalize winner selection
|
||||
setTopText(`${winner.winner_no}-nji ýeňiji`);
|
||||
setBottomText(winner.client);
|
||||
setBottomText(winner.phone);
|
||||
console.log("⬇️ Finalized Bottom Text:", winner.phone); // Debug Log 6: Log the final bottomText update
|
||||
|
||||
setWinnerSelectingStatus("selected");
|
||||
setIsConfettiActive(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const LotteryAuthForm = () => {
|
|||
value={code}
|
||||
onChange={handleCodeChange}
|
||||
className="px-[16px] py-[12px] bg-lightPrimaryContainer rounded-[12px] outline-none text-lightOnSurfaceVariant text-textSmall leading-textSmall"
|
||||
placeholder="5-0105639808"
|
||||
placeholder="C5-0105639808"
|
||||
required
|
||||
id="code"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useLotteryAuth } from '@/store/useLotteryAuth';
|
||||
import { Queries } from '@/api/queries';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
||||
import { Queries } from "@/api/queries";
|
||||
|
||||
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||
const router = useRouter();
|
||||
const { isAuthenticated, setAuth } = useLotteryAuth();
|
||||
const { setAuth } = useLotteryAuth();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const checkAuth = async () => {
|
||||
// ✅ Check credentials from localStorage
|
||||
const phone = localStorage.getItem('lotteryPhone');
|
||||
const code = localStorage.getItem('lotteryCode');
|
||||
const phone = localStorage.getItem("lotteryPhone");
|
||||
const code = localStorage.getItem("lotteryCode");
|
||||
|
||||
if (phone && code) {
|
||||
try {
|
||||
|
|
@ -23,19 +23,20 @@ const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
|||
|
||||
if (response.errorMessage) {
|
||||
// If authentication fails, redirect to the auth page
|
||||
router.replace('/lottery/auth');
|
||||
console.log("redirecting form protected route");
|
||||
router.replace("/lottery/auth");
|
||||
} else {
|
||||
// ✅ Set the authenticated state
|
||||
setAuth(response, phone, code);
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Authentication failed:', err);
|
||||
router.replace('/lottery/auth');
|
||||
console.error("Authentication failed:", err);
|
||||
router.replace("/lottery/auth");
|
||||
}
|
||||
} else {
|
||||
// Redirect to the auth page if no credentials are found
|
||||
router.replace('/lottery/auth');
|
||||
router.replace("/lottery/auth");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import { LotteryWinnerData, LotteryWinnerDataSimplified } from '@/typings/lottery/lottery.types';
|
||||
import LotteryWinner from './LotteryWinner';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
LotteryWinnerData,
|
||||
LotteryWinnerDataSimplified,
|
||||
} from "@/typings/lottery/lottery.types";
|
||||
import LotteryWinner from "./LotteryWinner";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const LotteryWinnersList = ({ winners }: { winners: LotteryWinnerDataSimplified[] }) => {
|
||||
const LotteryWinnersList = ({
|
||||
winners,
|
||||
}: {
|
||||
winners: LotteryWinnerDataSimplified[];
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full max-w-[1028px]">
|
||||
<div className="flex flex-col gap-2 w-full pb-4 border-b border-[#CECCFF]">
|
||||
|
|
@ -11,11 +18,12 @@ const LotteryWinnersList = ({ winners }: { winners: LotteryWinnerDataSimplified[
|
|||
</div>
|
||||
<motion.div
|
||||
layout
|
||||
className="grid min-[1025px]:grid-cols-3 min-[700px]:grid-cols-2 grid-cols-1 gap-x-2 gap-y-4 w-full lottery-winner-list">
|
||||
className="grid min-[1025px]:grid-cols-3 min-[700px]:grid-cols-2 grid-cols-1 gap-x-2 gap-y-4 w-full lottery-winner-list"
|
||||
>
|
||||
{winners.map((item, index) => (
|
||||
<LotteryWinner
|
||||
key={v4()}
|
||||
phone={item.client}
|
||||
phone={item.phone}
|
||||
ticket={item.ticket}
|
||||
winnerNumber={item.winner_no}
|
||||
isNew={index === winners.length - 1}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useRef } from "react";
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
|
||||
export const useWebsocketLottery = (url: string) => {
|
||||
const [wsStatus, setWsStatus] = useState<
|
||||
|
|
@ -14,14 +14,15 @@ export const useWebsocketLottery = (url: string) => {
|
|||
const setupWebSocket = () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.log("🔄 Connecting to WebSocket...");
|
||||
console.log("🔄 [WebSocket] Connecting...");
|
||||
const socket = new WebSocket(url);
|
||||
wsRef.current = socket;
|
||||
|
||||
socket.onopen = () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.log("✅ WebSocket connected");
|
||||
console.log("✅ [WebSocket] Connected");
|
||||
console.log("🔗 [WebSocket URL]:", url);
|
||||
setWsStatus("connected");
|
||||
if (reconnectTimeoutRef.current)
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
|
|
@ -30,21 +31,21 @@ export const useWebsocketLottery = (url: string) => {
|
|||
socket.onmessage = (event) => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.log("📩 Message received:", event.data);
|
||||
console.log("📩 [WebSocket] Message received:", event.data);
|
||||
messageListeners.current.forEach((listener) => listener(event));
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.error("❌ WebSocket error");
|
||||
console.error("❌ [WebSocket] Error occurred");
|
||||
setWsStatus("error");
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.log("❌ WebSocket closed");
|
||||
console.log("❌ [WebSocket] Closed");
|
||||
setWsStatus("closed");
|
||||
reconnectWebSocket();
|
||||
};
|
||||
|
|
@ -53,7 +54,7 @@ export const useWebsocketLottery = (url: string) => {
|
|||
const reconnectWebSocket = () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
console.log("🔄 Reconnecting to WebSocket...");
|
||||
console.log("🔄 [WebSocket] Reconnecting in 5 seconds...");
|
||||
reconnectTimeoutRef.current = setTimeout(() => {
|
||||
setupWebSocket();
|
||||
}, 5000);
|
||||
|
|
@ -62,7 +63,7 @@ export const useWebsocketLottery = (url: string) => {
|
|||
setupWebSocket();
|
||||
|
||||
return () => {
|
||||
console.log("🔌 Cleaning up WebSocket connection...");
|
||||
console.log("🔌 [WebSocket] Cleaning up...");
|
||||
isMounted = false;
|
||||
|
||||
if (wsRef.current) {
|
||||
|
|
@ -76,21 +77,26 @@ export const useWebsocketLottery = (url: string) => {
|
|||
};
|
||||
}, [url]);
|
||||
|
||||
const sendPing = () => {
|
||||
const sendPing = useCallback(() => {
|
||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
||||
console.log("📤 Sending ping");
|
||||
console.log("📤 [WebSocket] Sending ping");
|
||||
wsRef.current.send(JSON.stringify({ type: "ping" }));
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const subscribeToMessages = (listener: (event: MessageEvent) => void) => {
|
||||
messageListeners.current.push(listener);
|
||||
return () => {
|
||||
messageListeners.current = messageListeners.current.filter(
|
||||
(l) => l !== listener
|
||||
);
|
||||
};
|
||||
};
|
||||
const subscribeToMessages = useCallback(
|
||||
(listener: (event: MessageEvent) => void) => {
|
||||
console.log("👂 [WebSocket] Subscribing to messages");
|
||||
messageListeners.current.push(listener);
|
||||
return () => {
|
||||
console.log("❌ [WebSocket] Unsubscribing from messages");
|
||||
messageListeners.current = messageListeners.current.filter(
|
||||
(l) => l !== listener
|
||||
);
|
||||
};
|
||||
},
|
||||
[] // Empty array ensures it's memoized
|
||||
);
|
||||
|
||||
return { wsStatus, sendPing, subscribeToMessages };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ export interface ILotteryWinner {
|
|||
}
|
||||
export interface ILotteryRule {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ILotteryData {
|
||||
|
|
@ -18,6 +17,7 @@ export interface ILotteryData {
|
|||
start_time: string;
|
||||
end_time: string;
|
||||
sms_code: string;
|
||||
sms_number: string;
|
||||
winners: ILotteryWinner[];
|
||||
rules: ILotteryRule[] | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ export interface LotteryResponse {
|
|||
}
|
||||
|
||||
export interface LotteryWinnerDataSimplified {
|
||||
client: string;
|
||||
phone: string;
|
||||
winner_no: number;
|
||||
ticket: string;
|
||||
}
|
||||
|
||||
export type LotteryStatus = 'not-started' | 'started' | 'ended';
|
||||
export type LotteryStatus = "not-started" | "started" | "ended";
|
||||
|
|
|
|||
Loading…
Reference in New Issue