From 827c6f78972494047aca02a00b80eaa46a904f6d Mon Sep 17 00:00:00 2001 From: Kakabay <2kakabayashyrberdyew@gmail.com> Date: Mon, 9 Sep 2024 16:27:10 +0500 Subject: [PATCH] Quiz page socket added and X winner answer fix --- app/(main)/quiz/[quiz_id]/page.tsx | 10 +- app/(main)/quiz/active/page.tsx | 11 +- components/quiz/QuizQuestionList.tsx | 93 +++++++----- components/quiz/QuizWinnerTable.tsx | 204 ++++++++++++++++++++++++--- models/quizQuestions.model.ts | 1 + 5 files changed, 260 insertions(+), 59 deletions(-) diff --git a/app/(main)/quiz/[quiz_id]/page.tsx b/app/(main)/quiz/[quiz_id]/page.tsx index c534ad7..7f825c5 100644 --- a/app/(main)/quiz/[quiz_id]/page.tsx +++ b/app/(main)/quiz/[quiz_id]/page.tsx @@ -133,9 +133,13 @@ const page = ({ params }: IParams) => { /> ) : null} - {data?.data.id && quizFinished ? ( - - ) : null} + {data?.data.id && ( + + )} diff --git a/app/(main)/quiz/active/page.tsx b/app/(main)/quiz/active/page.tsx index d707a28..64c79b7 100644 --- a/app/(main)/quiz/active/page.tsx +++ b/app/(main)/quiz/active/page.tsx @@ -94,9 +94,16 @@ const page = () => { /> ) : null} - {data?.data.id && quizFinished ? ( + {/* {data?.data.id && quizFinished ? ( - ) : null} + ) : null} */} + {data?.data.id && ( + + )} diff --git a/components/quiz/QuizQuestionList.tsx b/components/quiz/QuizQuestionList.tsx index 0af3b1a..954e0f5 100644 --- a/components/quiz/QuizQuestionList.tsx +++ b/components/quiz/QuizQuestionList.tsx @@ -25,6 +25,10 @@ const QuizQuestionList = ({ const { quizSearchData } = useContext(QuizContext).quizSearchContext; const { setQuestionsData } = useContext(QuizContext).quizQuestionsContext; + const [smsNumber, setSmsNumber] = useState(); + const [socket, setSocket] = useState(null); + const [isConnected, setIsConnected] = useState(false); + // useEffect(() => { // Queries.getQuizQuestions().then((res) => setData(res)); // data?.data.questions.map((question) => @@ -56,48 +60,69 @@ const QuizQuestionList = ({ // ); if (!dynamic && quizFinished === false) { - const interval = setInterval(() => { - Queries.getQuizQuestions().then((res) => { - setData(res); - setQuestionsData(res.data.questions); + // const interval = setInterval(() => { + // Queries.getQuizQuestions().then((res) => { + // setData(res); + // setQuestionsData(res.data.questions); - res.data.questions.map((question) => - question.status === 'active' || question.status === 'new' - ? setQuizFinished(false) - : setQuizFinished(true), - ); - }); + // res.data.questions.map((question) => + // question.status === 'active' || question.status === 'new' + // ? setQuizFinished(false) + // : setQuizFinished(true), + // ); + // }); - // const isActive = data?.data.questions.some( - // (question) => question.status === 'active' || question.status === 'new', - // ); + // // const isActive = data?.data.questions.some( + // // (question) => question.status === 'active' || question.status === 'new', + // // ); - // data.data.questions.map((question) => - // question.status === 'active' || question.status === 'new' - // ? setQuizFinished(false) - // : setQuizFinished(true), - // ); - }, 60000); - return () => clearInterval(interval); + // // data.data.questions.map((question) => + // // question.status === 'active' || question.status === 'new' + // // ? setQuizFinished(false) + // // : setQuizFinished(true), + // // ); + // }, 60000); + // return () => clearInterval(interval); + Queries.getQuizQuestions().then((res) => { + setData(res); + setQuestionsData(res.data.questions); + setSmsNumber(res.data.sms_number); + + res.data.questions.map((question) => + question.status === 'active' || question.status === 'new' + ? setQuizFinished(false) + : setQuizFinished(true), + ); + }); } else { - const interval = setInterval(() => { - Queries.getQuiz(id).then((res) => { - setData(res); - setQuestionsData(res.data.questions); + // const interval = setInterval(() => { + // Queries.getQuiz(id).then((res) => { + // setData(res); + // setQuestionsData(res.data.questions); - res.data.questions.map((question) => - question.status === 'active' || question.status === 'new' - ? setQuizFinished(false) - : setQuizFinished(true), - ); - }); - }, 60000); - return () => clearInterval(interval); + // res.data.questions.map((question) => + // question.status === 'active' || question.status === 'new' + // ? setQuizFinished(false) + // : setQuizFinished(true), + // ); + // }); + // }, 60000); + // return () => clearInterval(interval); + + Queries.getQuiz(id).then((res) => { + setData(res); + setQuestionsData(res.data.questions); + setSmsNumber(res.data.sms_number); + + res.data.questions.map((question) => + question.status === 'active' || question.status === 'new' + ? setQuizFinished(false) + : setQuizFinished(true), + ); + }); } }, [quizFinished]); - console.log(data); - return (
{data && !quizSearchData ? ( diff --git a/components/quiz/QuizWinnerTable.tsx b/components/quiz/QuizWinnerTable.tsx index 6ed34c1..aaff457 100644 --- a/components/quiz/QuizWinnerTable.tsx +++ b/components/quiz/QuizWinnerTable.tsx @@ -2,38 +2,192 @@ import { Queries } from '@/api/queries'; import { v4 } from 'uuid'; -import { useState, useEffect, useContext } from 'react'; +import { useState, useEffect, useContext, Dispatch, SetStateAction } from 'react'; import { IQuizQuestionsWinners } from '@/models/quizQuestionsWinners.model'; import QuizContext from '@/context/QuizContext'; +import { IQuizQuestions } from '@/models/quizQuestions.model'; + +interface Message { + answer: string; + score: number; + date: string; + serial_number: number; + serial_number_for_correct: number; + starred_src: string; + quiz_id: number; + question_id: number; +} + +interface Winner { + total_score_of_client: string; + correct_answers_time: string; + client_id: number; + client: { + id: number; + phone: string; + answers: { + id: number; + question_id: number; + score: number; + serial_number_for_correct: number; + client_id: number; + }[]; + }; +} interface IProps { quizId: number; quizFinished: boolean; + smsNumber: string; } -const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => { +const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => { // const [questionsData, setQuestionsData] = useState(); const [winnersData, setWinnersData] = useState(); const { questionsData } = useContext(QuizContext).quizQuestionsContext; - useEffect(() => { - if (quizFinished) { - // Queries.getQuizQuestions().then((res) => { - // setQuestionsData(res); - // }); - Queries.getQuizWinners(quizId).then((res) => { - setWinnersData(res); - }); - } - }, []); - // const { data, error, isFetching } = useQuery({ - // queryKey: ['quiz_questions_winners'], - // queryFn: () => Queries.getQuizWinners(quizId), - // keepPreviousData: true, - // }); + console.log('Questions:', questionsData); - // if (isFetching) return ; - // if (error) return

{JSON.stringify(error)}

; + const [socket, setSocket] = useState(null); + const [isConnected, setIsConnected] = useState(false); + + useEffect(() => { + Queries.getQuizWinners(quizId).then((res) => { + setWinnersData(res); + console.log(res); + }); + }, [quizId]); + + useEffect(() => { + let socket: WebSocket | null = null; + let reconnectTimeout: NodeJS.Timeout | null = null; + let pingInterval: NodeJS.Timeout | null = null; + + const connectWebSocket = () => { + try { + socket = new WebSocket(`wss://sms.turkmentv.gov.tm/ws/quiz?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 { + console.log('Message received from WebSocket:', event.data); + const message = JSON.parse(event.data); + handleOnMessage(message); + } catch (error) { + console.error('Error processing message:', error); + } + }; + + socket.onerror = (error) => { + console.error('WebSocket error:', error); + }; + + socket.onclose = () => { + console.log('WebSocket is closed'); + setIsConnected(false); + + if (pingInterval) { + clearInterval(pingInterval); + } + + if (!reconnectTimeout) { + reconnectTimeout = setTimeout(() => { + console.log('Attempting to reconnect WebSocket...'); + connectWebSocket(); + }, 5000); // Reconnect after 5 seconds + } + }; + } catch (error) { + console.error('WebSocket connection error:', error); + } + }; + + if (smsNumber && winnersData) { + connectWebSocket(); + } + + return () => { + if (socket) { + socket.close(); + } + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + } + if (pingInterval) { + clearInterval(pingInterval); + } + }; + }, [smsNumber]); + + // Function to handle incoming WebSocket message and update winnersData + const handleOnMessage = (message: Message) => { + if (!winnersData) { + console.error('winnersData is undefined'); + return; + } + + console.log('updating winnersData'); + + // Update the winnersData by matching phone with starred_src from the message + setWinnersData((prevWinnersData) => { + if (!prevWinnersData) { + return prevWinnersData; + } + + return { + ...prevWinnersData, + data: prevWinnersData.data.map((winner) => { + if (winner.client.phone === message.starred_src) { + const updatedAnswers = [ + ...winner.client.answers, + { + id: message.question_id, + question_id: message.question_id, + score: message.score, + serial_number_for_correct: message.serial_number_for_correct, + client_id: winner.client.id, + }, + ]; + + // Calculate the new correct_answers_time by summing serial_number_for_correct + const updatedCorrectAnswersTime = updatedAnswers + .reduce((sum, answer) => sum + answer.serial_number_for_correct, 0) + .toString(); + + return { + ...winner, + client: { + ...winner.client, + answers: updatedAnswers, + }, + total_score_of_client: ( + parseInt(winner.total_score_of_client) + message.score + ).toString(), + correct_answers_time: updatedCorrectAnswersTime, // Update correct_answers_time + }; + } + return winner; + }), + }; + }); + + console.log('winnersData is updated'); + }; return winnersData?.data.length !== 0 ? (
@@ -41,7 +195,10 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {

Bäsleşigiň jemi

+ + {/* Desktop table */}
+ {/* Table Head */}
{winnersData?.data[0].client_id ? (
@@ -73,6 +230,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => { ) : null}
+ {/* Table Body */}
{winnersData?.data.map((winner, id) => (
{ {questionsData ? questionsData.map((question) => { const matchingAnswer = winner.client.answers.find( - (answer) => answer.question_id === question.id, + (answer) => answer.question_id === question.id && answer.score !== 0, ); + return ( {
+ {/* Mobile table */}
+ {/* Table Head */}
{winnersData?.data[0].client_id ? (
@@ -163,6 +324,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => { ) : null}
+ {/* Table Body */}
{winnersData?.data.map((winner, id) => (
{
+ + {/* Rules block */}

Belgileriň düşündirilişi diff --git a/models/quizQuestions.model.ts b/models/quizQuestions.model.ts index 5f40cd6..467f40d 100644 --- a/models/quizQuestions.model.ts +++ b/models/quizQuestions.model.ts @@ -8,6 +8,7 @@ export interface Data { title: string; date: string; banner: string; + sms_number: string; banner_mobile: string; description: string; rules: Note[];