'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 { useWindowSize } from 'react-use'; import Confetti from '../common/Confetti'; interface IParams { vote_id?: string; } interface ISocketMessage { voting_id: number; voting_item_id: number; client_id: number; message: string; date: string; } const ParticipantsList = ({ vote_id }: IParams) => { 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 { width, height } = useWindowSize(); const { setVoteDescription } = useContext(VoteContext).voteDescriptionContext; useEffect(() => { if (!vote_id) { 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 { 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); }); } 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;