"use client"; import React, { useContext, useEffect, useState } from "react"; import GradientTitle from "./GradientTitle"; import ParticipantCard from "./ParticipantCard"; import { v4 } from "uuid"; import { IAllVotes, VotingItem } from "@/models/allVotes.model"; import { Queries } from "@/api/queries"; import Loader from "../Loader"; import VoteContext from "@/context/VoteContext"; import PageBage from "./PageBage"; import Image from "next/image"; import { useMediaQuery } from "usehooks-ts"; import Countdown from "./Countdown"; import Link from "next/link"; import Confetti from "../common/Confetti"; import { useRouter, useSearchParams } from "next/navigation"; interface IParams { vote_id?: string; all?: boolean; } interface ISocketMessage { voting_id: number; voting_item_id: number; client_id: number; message: string; date: string; } const ParticipantsList = ({ vote_id, all }: IParams) => { const searchParams = useSearchParams(); const router = useRouter(); const [data, setData] = useState(); const [participantsData, setParticipantsData] = useState([]); const [voteStatus, setVoteStatus] = useState(); const [eventStatus, setEventStatus] = useState("Not started"); const [manualClose, setManualClose] = useState(false); // Track manual closure const [winnersCount, setWinnersCount] = useState(0); // States realted to web socket const [smsNumber, setSmsNumber] = useState(null); const [socket, setSocket] = useState(null); const [isConnected, setIsConnected] = useState(false); const mobile = useMediaQuery("(max-width: 768px)"); const { setVoteDescription } = useContext(VoteContext).voteDescriptionContext; useEffect(() => { const id = searchParams.get("d"); if (id) { Queries.getVoteByUUID(id).then((res) => { setData(res); setParticipantsData(res.data.voting_items); setVoteDescription(res.data.description); setVoteStatus(res.data.status); setSmsNumber(res.data.sms_number); }); } else if (vote_id) { Queries.getVote(vote_id).then((res) => { setData(res); setParticipantsData(res.data.voting_items); setVoteDescription(res.data.description); setVoteStatus(res.data.status); setSmsNumber(res.data.sms_number); }); } else if (all) { Queries.getAllVotes().then((res) => { setData(res); setParticipantsData([...res.data.voting_items]); setVoteDescription(res.data.description); setVoteStatus(res.data.status); setSmsNumber(res.data.sms_number); }); } else { router.push("/vote/active"); } if (participantsData) { winnersCountHandle(participantsData); } }, []); useEffect(() => { let socket: WebSocket | null = null; let reconnectTimeout: NodeJS.Timeout | null = null; let pingInterval: NodeJS.Timeout | null = null; const connectWebSocket = () => { try { // Only connect if manualClose is false if (!manualClose) { socket = new WebSocket( `wss://sms.turkmentv.gov.tm/ws/voting?dst=${smsNumber}` ); setSocket(socket); socket.onopen = () => { console.log("WebSocket is connected"); setIsConnected(true); pingInterval = setInterval(() => { if (socket?.readyState === WebSocket.OPEN) { try { socket.send(JSON.stringify({ type: "ping" })); } catch (error) { console.error("Error sending ping:", error); } } }, 25000); // Ping every 25 seconds }; socket.onmessage = (event) => { try { const message = JSON.parse(event.data); handleWebSocketMessage(message); } catch (error) { console.error("Error processing message:", error); } }; socket.onerror = (error) => { console.error("WebSocket error:", error); if (!manualClose && !reconnectTimeout) { reconnectTimeout = setTimeout(() => { console.log("Attempting to reconnect WebSocket after error..."); connectWebSocket(); }, 5000); // Reconnect after 5 seconds } }; socket.onclose = () => { console.log("WebSocket is closed"); setIsConnected(false); if (pingInterval) { clearInterval(pingInterval); } // Clean up resources on close if (reconnectTimeout) { clearTimeout(reconnectTimeout); } }; } } catch (error) { console.error("WebSocket connection error:", error); } }; // WebSocket connection only if eventStatus is 'Started' if (smsNumber && eventStatus === "Started" && !manualClose) { connectWebSocket(); } return () => { if (socket) { setManualClose(true); // Mark it as a manual close socket.close(); } if (reconnectTimeout) { clearTimeout(reconnectTimeout); } if (pingInterval) { clearInterval(pingInterval); } }; }, [smsNumber, eventStatus, manualClose]); // Add manualClose to dependencies const handleWebSocketMessage = (message: ISocketMessage) => { setParticipantsData((prevVotingItems) => { if (!prevVotingItems) return []; // Update the corresponding voting item const updatedItems = prevVotingItems.map((item, index) => item.id === message.voting_item_id ? { ...item, votes_count: item.votes_count + 1 } : item ); // Sort the updated items array by votes_count in descending order return updatedItems.sort((a, b) => b.votes_count - a.votes_count); }); }; const addVotes = () => { setParticipantsData((prevVotingItems) => { if (!prevVotingItems) return []; // Update the corresponding voting item const updatedItems = prevVotingItems.map((item, index) => index === 1 ? { ...item, votes_count: item.votes_count + 1 } : item ); console.log("votes updated"); console.log(updatedItems.sort((a, b) => b.votes_count - a.votes_count)); // Sort the updated items array by votes_count in descending order return updatedItems.sort((a, b) => b.votes_count - a.votes_count); }); }; const winnersCountHandle = (winners: VotingItem[]) => { let count = 0; winners.map((winner) => { if ( winner.votes_percents === 100 && winner.votes_count === winners[0].votes_count ) { count++; setWinnersCount(count); } }); return count; }; const hasVotes = participantsData.some((item) => item.votes_count > 0); if (data) { if (!data?.data) { return (
); } return (
{data.data.description ? ( ) : null} {eventStatus === "Finished" && } {data.data.banner ? (
{mobile ? ( {data.data.title} ) : ( {data.data.title} )}
) : null}
{/* {data.data.title ? : null} */} {data.data.ends_at && data.data.starts_at ? ( ) : null}
{winnersCount > 1 ? ( ) : null} {participantsData && participantsData[0].votes_count > 0 ? (
{participantsData.map((participant, index) => participant.votes_count === participantsData[0].votes_count ? ( participant.url ? ( ) : ( ) ) : null )}
) : null} {winnersCount > 1 ? (
) : null}
{participantsData ? participantsData.map((participant, index) => !hasVotes ? ( participant.url ? ( ) : ( ) ) : ( participant.votes_count !== participantsData[0].votes_count && (participant.url ? ( ) : ( )) ) ) : null}
); } else { return (
); } }; export default ParticipantsList;