Quiz page socket added and X winner answer fix
This commit is contained in:
parent
9c9a9f3779
commit
827c6f7897
|
|
@ -133,9 +133,13 @@ const page = ({ params }: IParams) => {
|
|||
/>
|
||||
) : null}
|
||||
|
||||
{data?.data.id && quizFinished ? (
|
||||
<QuizWinnerTable quizId={data?.data.id} quizFinished={quizFinished} />
|
||||
) : null}
|
||||
{data?.data.id && (
|
||||
<QuizWinnerTable
|
||||
smsNumber={data.data.sms_number}
|
||||
quizId={data?.data.id}
|
||||
quizFinished={quizFinished}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</QuizProvider>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -94,9 +94,16 @@ const page = () => {
|
|||
/>
|
||||
) : null}
|
||||
|
||||
{data?.data.id && quizFinished ? (
|
||||
{/* {data?.data.id && quizFinished ? (
|
||||
<QuizWinnerTable quizId={data?.data.id} quizFinished={quizFinished} />
|
||||
) : null}
|
||||
) : null} */}
|
||||
{data?.data.id && (
|
||||
<QuizWinnerTable
|
||||
smsNumber={data.data.sms_number}
|
||||
quizId={data?.data.id}
|
||||
quizFinished={quizFinished}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</QuizProvider>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ const QuizQuestionList = ({
|
|||
const { quizSearchData } = useContext(QuizContext).quizSearchContext;
|
||||
const { setQuestionsData } = useContext(QuizContext).quizQuestionsContext;
|
||||
|
||||
const [smsNumber, setSmsNumber] = useState<string>();
|
||||
const [socket, setSocket] = useState<WebSocket | null>(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
// Queries.getQuizQuestions().then((res) => setData(res));
|
||||
// data?.data.questions.map((question) =>
|
||||
|
|
@ -56,34 +60,59 @@ 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),
|
||||
);
|
||||
});
|
||||
|
||||
// const isActive = data?.data.questions.some(
|
||||
// (question) => question.status === 'active' || question.status === 'new',
|
||||
// );
|
||||
|
||||
// data.data.questions.map((question) =>
|
||||
// res.data.questions.map((question) =>
|
||||
// question.status === 'active' || question.status === 'new'
|
||||
// ? setQuizFinished(false)
|
||||
// : setQuizFinished(true),
|
||||
// );
|
||||
}, 60000);
|
||||
return () => clearInterval(interval);
|
||||
} else {
|
||||
const interval = setInterval(() => {
|
||||
Queries.getQuiz(id).then((res) => {
|
||||
// });
|
||||
|
||||
// // 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);
|
||||
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);
|
||||
|
||||
// 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'
|
||||
|
|
@ -91,13 +120,9 @@ const QuizQuestionList = ({
|
|||
: setQuizFinished(true),
|
||||
);
|
||||
});
|
||||
}, 60000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [quizFinished]);
|
||||
|
||||
console.log(data);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[40px] md:gap-[160px]">
|
||||
{data && !quizSearchData ? (
|
||||
|
|
|
|||
|
|
@ -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<IQuizQuestions>();
|
||||
const [winnersData, setWinnersData] = useState<IQuizQuestionsWinners>();
|
||||
const { questionsData } = useContext(QuizContext).quizQuestionsContext;
|
||||
|
||||
console.log('Questions:', questionsData);
|
||||
|
||||
const [socket, setSocket] = useState<WebSocket | null>(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (quizFinished) {
|
||||
// Queries.getQuizQuestions().then((res) => {
|
||||
// setQuestionsData(res);
|
||||
// });
|
||||
Queries.getQuizWinners(quizId).then((res) => {
|
||||
setWinnersData(res);
|
||||
console.log(res);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
// const { data, error, isFetching } = useQuery({
|
||||
// queryKey: ['quiz_questions_winners'],
|
||||
// queryFn: () => Queries.getQuizWinners(quizId),
|
||||
// keepPreviousData: true,
|
||||
// });
|
||||
}, [quizId]);
|
||||
|
||||
// if (isFetching) return <Loader />;
|
||||
// if (error) return <h1>{JSON.stringify(error)}</h1>;
|
||||
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 ? (
|
||||
<div className="flex flex-col justify-center items-center gap-[60px]">
|
||||
|
|
@ -41,7 +195,10 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
<h2 className="text-textBlack text-[28px] text-center md:text-left md:text-[32px] font-semibold">
|
||||
Bäsleşigiň jemi
|
||||
</h2>
|
||||
|
||||
{/* Desktop table */}
|
||||
<div className="table-desktop hidden sm:flex flex-col bg-fillTableHead rounded-[25px] shadow-quizButton overflow-hidden max-w-[1000px] w-full">
|
||||
{/* Table Head */}
|
||||
<div className="flex border-b border-fillTableStrokeTableHead">
|
||||
{winnersData?.data[0].client_id ? (
|
||||
<div className="text-center flex justify-center items-center text-base text-textBlack leading-[125%] font-semibold max-w-[54px] w-[100%] pl-6 pr-3 py-5">
|
||||
|
|
@ -73,6 +230,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Table Body */}
|
||||
<div className="">
|
||||
{winnersData?.data.map((winner, id) => (
|
||||
<div
|
||||
|
|
@ -93,8 +251,9 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
{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 (
|
||||
<span
|
||||
key={v4()}
|
||||
|
|
@ -137,7 +296,9 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile table */}
|
||||
<div className="sm:hidden flex flex-col bg-fillTableHead rounded-[13px] shadow-quizButton overflow-hidden max-w-[1000px] w-full">
|
||||
{/* Table Head */}
|
||||
<div className="flex border-b border-fillTableStrokeTableHead p-2 gap-[8px]">
|
||||
{winnersData?.data[0].client_id ? (
|
||||
<div className="text-center flex items-center text-xs text-textBlack leading-[125%] font-semibold max-w-[14px] w-[100%]">
|
||||
|
|
@ -163,6 +324,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Table Body */}
|
||||
<div className="">
|
||||
{winnersData?.data.map((winner, id) => (
|
||||
<div
|
||||
|
|
@ -240,6 +402,8 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rules block */}
|
||||
<div className="flex flex-col gap-[20px] p-5 border border-strokeLightGray1 rounded-[25px] max-w-[1000px] w-full items-center justify-center">
|
||||
<h3 className="text-[26px] text-textBlack font-semibold leading-[124%]">
|
||||
Belgileriň düşündirilişi
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export interface Data {
|
|||
title: string;
|
||||
date: string;
|
||||
banner: string;
|
||||
sms_number: string;
|
||||
banner_mobile: string;
|
||||
description: string;
|
||||
rules: Note[];
|
||||
|
|
|
|||
Loading…
Reference in New Issue