mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Plus besoin de local storage si on passe toutes les questions à TeacherModeQuiz aussi
Nécessite un nouveau message launch-teacher-mode
This commit is contained in:
parent
22482592cd
commit
ca1eb4737d
10 changed files with 157 additions and 72 deletions
|
|
@ -5,6 +5,7 @@ import { MemoryRouter } from 'react-router-dom';
|
||||||
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
|
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
|
||||||
import { BaseQuestion, parse } from 'gift-pegjs';
|
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||||
import { QuestionType } from 'src/Types/QuestionType';
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
|
|
||||||
const mockGiftQuestions = parse(
|
const mockGiftQuestions = parse(
|
||||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||||
|
|
@ -23,12 +24,13 @@ const mockDisconnectWebSocket = jest.fn();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Clear local storage before each test
|
// Clear local storage before each test
|
||||||
localStorage.clear();
|
// localStorage.clear();
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<StudentModeQuiz
|
<StudentModeQuiz
|
||||||
questions={mockQuestions}
|
questions={mockQuestions}
|
||||||
|
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
disconnectWebSocket={mockDisconnectWebSocket}
|
||||||
/>
|
/>
|
||||||
|
|
@ -118,4 +120,4 @@ describe('StudentModeQuiz', () => {
|
||||||
expect(screen.getByText('Sample Question 2')).toBeInTheDocument();
|
expect(screen.getByText('Sample Question 2')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Répondre')).toBeInTheDocument();
|
expect(screen.getByText('Répondre')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { BaseQuestion, MultipleChoiceQuestion, parse } from 'gift-pegjs';
|
||||||
import TeacherModeQuiz from 'src/components/TeacherModeQuiz/TeacherModeQuiz';
|
import TeacherModeQuiz from 'src/components/TeacherModeQuiz/TeacherModeQuiz';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { QuestionType } from 'src/Types/QuestionType';
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
|
|
||||||
const mockGiftQuestions = parse(
|
const mockGiftQuestions = parse(
|
||||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||||
|
|
@ -36,6 +37,7 @@ describe('TeacherModeQuiz', () => {
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<TeacherModeQuiz
|
<TeacherModeQuiz
|
||||||
questionInfos={{ question: mockQuestion }}
|
questionInfos={{ question: mockQuestion }}
|
||||||
|
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket} />
|
disconnectWebSocket={mockDisconnectWebSocket} />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
|
|
@ -80,6 +82,7 @@ describe('TeacherModeQuiz', () => {
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<TeacherModeQuiz
|
<TeacherModeQuiz
|
||||||
questionInfos={{ question: mockQuestion }}
|
questionInfos={{ question: mockQuestion }}
|
||||||
|
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
disconnectWebSocket={mockDisconnectWebSocket}
|
||||||
/>
|
/>
|
||||||
|
|
@ -94,6 +97,7 @@ describe('TeacherModeQuiz', () => {
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<TeacherModeQuiz
|
<TeacherModeQuiz
|
||||||
questionInfos={{ question: mockQuestion }}
|
questionInfos={{ question: mockQuestion }}
|
||||||
|
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
disconnectWebSocket={mockDisconnectWebSocket}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
//WebsocketService.test.tsx
|
//WebsocketService.test.tsx
|
||||||
|
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||||
import WebsocketService from '../../services/WebsocketService';
|
import WebsocketService from '../../services/WebsocketService';
|
||||||
import { io, Socket } from 'socket.io-client';
|
import { io, Socket } from 'socket.io-client';
|
||||||
import { ENV_VARIABLES } from 'src/constants';
|
import { ENV_VARIABLES } from 'src/constants';
|
||||||
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
|
||||||
jest.mock('socket.io-client');
|
jest.mock('socket.io-client');
|
||||||
|
|
||||||
|
|
@ -45,10 +47,16 @@ describe('WebSocketService', () => {
|
||||||
|
|
||||||
test('nextQuestion should emit next-question event with correct parameters', () => {
|
test('nextQuestion should emit next-question event with correct parameters', () => {
|
||||||
const roomName = 'testRoom';
|
const roomName = 'testRoom';
|
||||||
const question = { id: 1, text: 'Sample Question' };
|
const mockGiftQuestions = parse('A {T}');
|
||||||
|
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||||
|
if (question.type !== "Category")
|
||||||
|
question.id = (index + 1).toString();
|
||||||
|
const newMockQuestion = question;
|
||||||
|
return {question : newMockQuestion as BaseQuestion};
|
||||||
|
});
|
||||||
mockSocket = WebsocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
mockSocket = WebsocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
||||||
WebsocketService.nextQuestion(roomName, question);
|
WebsocketService.nextQuestion({roomName, questions: mockQuestions, questionIndex: 0, isLaunch: false});
|
||||||
|
const question = mockQuestions[0];
|
||||||
expect(mockSocket.emit).toHaveBeenCalledWith('next-question', { roomName, question });
|
expect(mockSocket.emit).toHaveBeenCalledWith('next-question', { roomName, question });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,36 +7,36 @@ import { Button } from '@mui/material';
|
||||||
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { Question } from 'gift-pegjs';
|
import { Question } from 'gift-pegjs';
|
||||||
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
|
|
||||||
interface StudentModeQuizProps {
|
interface StudentModeQuizProps {
|
||||||
questions: QuestionType[];
|
questions: QuestionType[];
|
||||||
|
answers: AnswerSubmissionToBackendType[];
|
||||||
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
||||||
disconnectWebSocket: () => void;
|
disconnectWebSocket: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||||
questions,
|
questions,
|
||||||
|
answers,
|
||||||
submitAnswer,
|
submitAnswer,
|
||||||
disconnectWebSocket
|
disconnectWebSocket
|
||||||
}) => {
|
}) => {
|
||||||
//Ajouter type AnswerQuestionType en remplacement de QuestionType
|
//Ajouter type AnswerQuestionType en remplacement de QuestionType
|
||||||
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
|
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
|
||||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||||
const [answer, setAnswer] = useState<string | number | boolean>('');
|
// const [answer, setAnswer] = useState<string | number | boolean>('');
|
||||||
|
|
||||||
|
|
||||||
const previousQuestion = () => {
|
const previousQuestion = () => {
|
||||||
setQuestion(questions[Number(questionInfos.question?.id) - 2]);
|
setQuestion(questions[Number(questionInfos.question?.id) - 2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAnswer(JSON.parse(localStorage.getItem(`Answer${questionInfos.question.id}`)||'null'));
|
const savedAnswer = answers[Number(questionInfos.question.id)-1]?.answer;
|
||||||
if (answer !== null) {
|
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
|
||||||
setIsAnswerSubmitted(true);
|
setIsAnswerSubmitted(savedAnswer !== undefined);
|
||||||
} else {
|
}, [questionInfos.question, answers]);
|
||||||
setIsAnswerSubmitted(false);
|
|
||||||
}
|
|
||||||
}, [questionInfos.question , answer]);
|
|
||||||
|
|
||||||
const nextQuestion = () => {
|
const nextQuestion = () => {
|
||||||
setQuestion(questions[Number(questionInfos.question?.id)]);
|
setQuestion(questions[Number(questionInfos.question?.id)]);
|
||||||
|
|
@ -73,7 +73,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question as Question}
|
question={questionInfos.question as Question}
|
||||||
showAnswer={isAnswerSubmitted}
|
showAnswer={isAnswerSubmitted}
|
||||||
answer={answer}
|
answer={answers[Number(questionInfos.question.id)-1]?.answer}
|
||||||
/>
|
/>
|
||||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,54 @@ import { QuestionType } from '../../Types/QuestionType';
|
||||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
||||||
import { Question } from 'gift-pegjs';
|
import { Question } from 'gift-pegjs';
|
||||||
|
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||||
|
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||||
|
// import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||||
|
|
||||||
interface TeacherModeQuizProps {
|
interface TeacherModeQuizProps {
|
||||||
questionInfos: QuestionType;
|
questionInfos: QuestionType;
|
||||||
|
answers: AnswerSubmissionToBackendType[];
|
||||||
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
||||||
disconnectWebSocket: () => void;
|
disconnectWebSocket: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
questionInfos,
|
questionInfos,
|
||||||
|
answers,
|
||||||
submitAnswer,
|
submitAnswer,
|
||||||
disconnectWebSocket
|
disconnectWebSocket
|
||||||
}) => {
|
}) => {
|
||||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||||
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
||||||
const [answer, setAnswer] = useState<string | number | boolean>();
|
const [answer, setAnswer] = useState<AnswerType>();
|
||||||
|
|
||||||
|
|
||||||
|
// arrive here the first time after waiting for next question
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(`TeacherModeQuiz: useEffect: answers: ${JSON.stringify(answers)}`);
|
||||||
|
console.log(`TeacherModeQuiz: useEffect: questionInfos.question.id: ${questionInfos.question.id} answer: ${answer}`);
|
||||||
|
const oldAnswer = answers[Number(questionInfos.question.id) -1 ]?.answer;
|
||||||
|
console.log(`TeacherModeQuiz: useEffect: oldAnswer: ${oldAnswer}`);
|
||||||
|
setAnswer(oldAnswer);
|
||||||
|
setIsFeedbackDialogOpen(false);
|
||||||
|
}, [questionInfos.question, answers]);
|
||||||
|
|
||||||
|
// handle showing the feedback dialog
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(`TeacherModeQuiz: useEffect: answer: ${answer}`);
|
||||||
|
setIsAnswerSubmitted(answer !== undefined);
|
||||||
|
setIsFeedbackDialogOpen(answer !== undefined);
|
||||||
|
}, [answer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Close the feedback dialog when the question changes
|
console.log(`TeacherModeQuiz: useEffect: isAnswerSubmitted: ${isAnswerSubmitted}`);
|
||||||
handleFeedbackDialogClose();
|
setIsFeedbackDialogOpen(isAnswerSubmitted);
|
||||||
setIsAnswerSubmitted(false);
|
}, [isAnswerSubmitted]);
|
||||||
setAnswer(JSON.parse(localStorage.getItem(`Answer${questionInfos.question.id}`)||'null'));
|
|
||||||
if (typeof answer !== "object" && typeof answer !== "undefined") {
|
|
||||||
setIsAnswerSubmitted(true);
|
|
||||||
setIsFeedbackDialogOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [questionInfos.question , answer]);
|
|
||||||
|
|
||||||
const handleOnSubmitAnswer = (answer: string | number | boolean) => {
|
const handleOnSubmitAnswer = (answer: string | number | boolean) => {
|
||||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||||
submitAnswer(answer, idQuestion);
|
submitAnswer(answer, idQuestion);
|
||||||
setAnswer(answer);
|
// setAnswer(answer);
|
||||||
setIsFeedbackDialogOpen(true);
|
setIsFeedbackDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -49,21 +64,21 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='room'>
|
<div className='room'>
|
||||||
<div className='roomHeader'>
|
<div className='roomHeader'>
|
||||||
|
|
||||||
<DisconnectButton
|
<DisconnectButton
|
||||||
onReturn={disconnectWebSocket}
|
onReturn={disconnectWebSocket}
|
||||||
message={`Êtes-vous sûr de vouloir quitter?`} />
|
message={`Êtes-vous sûr de vouloir quitter?`} />
|
||||||
|
|
||||||
<div className='centerTitle'>
|
|
||||||
<div className='title'>Question {questionInfos.question.id}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='dumb'></div>
|
|
||||||
|
|
||||||
|
<div className='centerTitle'>
|
||||||
|
<div className='title'>Question {questionInfos.question.id}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isAnswerSubmitted ? (
|
<div className='dumb'></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isAnswerSubmitted ? (
|
||||||
<div>
|
<div>
|
||||||
En attente pour la prochaine question...
|
En attente pour la prochaine question...
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,7 +86,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question as Question}
|
question={questionInfos.question as Question}
|
||||||
answer={answer}
|
answer={answer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -82,20 +97,20 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
<DialogTitle>Rétroaction</DialogTitle>
|
<DialogTitle>Rétroaction</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div style={{
|
<div style={{
|
||||||
wordWrap: 'break-word',
|
wordWrap: 'break-word',
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: 'pre-wrap',
|
||||||
maxHeight: '400px',
|
maxHeight: '400px',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px'}}
|
<div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px' }}
|
||||||
>Question : </div>
|
>Question : </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<QuestionComponent
|
<QuestionComponent
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
question={questionInfos.question as Question}
|
question={questionInfos.question as Question}
|
||||||
showAnswer={true}
|
showAnswer={true}
|
||||||
answer={answer}
|
answer={answer}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
@ -105,7 +120,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import LoginContainer from 'src/components/LoginContainer/LoginContainer'
|
||||||
|
|
||||||
import ApiService from '../../../services/ApiService'
|
import ApiService from '../../../services/ApiService'
|
||||||
|
|
||||||
|
export type AnswerType = string | number | boolean;
|
||||||
|
|
||||||
const JoinRoom: React.FC = () => {
|
const JoinRoom: React.FC = () => {
|
||||||
const [roomName, setRoomName] = useState('');
|
const [roomName, setRoomName] = useState('');
|
||||||
const [username, setUsername] = useState(ApiService.getUsername());
|
const [username, setUsername] = useState(ApiService.getUsername());
|
||||||
|
|
@ -25,6 +27,7 @@ const JoinRoom: React.FC = () => {
|
||||||
const [question, setQuestion] = useState<QuestionType>();
|
const [question, setQuestion] = useState<QuestionType>();
|
||||||
const [quizMode, setQuizMode] = useState<string>();
|
const [quizMode, setQuizMode] = useState<string>();
|
||||||
const [questions, setQuestions] = useState<QuestionType[]>([]);
|
const [questions, setQuestions] = useState<QuestionType[]>([]);
|
||||||
|
const [answers, setAnswers] = useState<AnswerSubmissionToBackendType[]>([]);
|
||||||
const [connectionError, setConnectionError] = useState<string>('');
|
const [connectionError, setConnectionError] = useState<string>('');
|
||||||
const [isConnecting, setIsConnecting] = useState<boolean>(false);
|
const [isConnecting, setIsConnecting] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
@ -35,6 +38,13 @@ const JoinRoom: React.FC = () => {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// init the answers array, one for each question
|
||||||
|
setAnswers(Array(questions.length).fill({} as AnswerSubmissionToBackendType));
|
||||||
|
console.log(`JoinRoom: useEffect: questions: ${JSON.stringify(questions)}`);
|
||||||
|
}, [questions]);
|
||||||
|
|
||||||
|
|
||||||
const handleCreateSocket = () => {
|
const handleCreateSocket = () => {
|
||||||
console.log(`JoinRoom: handleCreateSocket: ${ENV_VARIABLES.VITE_BACKEND_URL}`);
|
console.log(`JoinRoom: handleCreateSocket: ${ENV_VARIABLES.VITE_BACKEND_URL}`);
|
||||||
const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
||||||
|
|
@ -45,12 +55,18 @@ const JoinRoom: React.FC = () => {
|
||||||
console.log(`on(join-success): Successfully joined the room ${roomJoinedName}`);
|
console.log(`on(join-success): Successfully joined the room ${roomJoinedName}`);
|
||||||
});
|
});
|
||||||
socket.on('next-question', (question: QuestionType) => {
|
socket.on('next-question', (question: QuestionType) => {
|
||||||
console.log('on(next-question): Received next-question:', question);
|
console.log('JoinRoom: on(next-question): Received next-question:', question);
|
||||||
setQuizMode('teacher');
|
setQuizMode('teacher');
|
||||||
setIsWaitingForTeacher(false);
|
setIsWaitingForTeacher(false);
|
||||||
|
|
||||||
setQuestion(question);
|
setQuestion(question);
|
||||||
});
|
});
|
||||||
|
socket.on('launch-teacher-mode', (questions: QuestionType[]) => {
|
||||||
|
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
||||||
|
setQuizMode('teacher');
|
||||||
|
setIsWaitingForTeacher(true);
|
||||||
|
setQuestions(questions);
|
||||||
|
// wait for next-question
|
||||||
|
});
|
||||||
socket.on('launch-student-mode', (questions: QuestionType[]) => {
|
socket.on('launch-student-mode', (questions: QuestionType[]) => {
|
||||||
console.log('on(launch-student-mode): Received launch-student-mode:', questions);
|
console.log('on(launch-student-mode): Received launch-student-mode:', questions);
|
||||||
|
|
||||||
|
|
@ -84,7 +100,7 @@ const JoinRoom: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const disconnect = () => {
|
const disconnect = () => {
|
||||||
localStorage.clear();
|
// localStorage.clear();
|
||||||
webSocketService.disconnect();
|
webSocketService.disconnect();
|
||||||
setSocket(null);
|
setSocket(null);
|
||||||
setQuestion(undefined);
|
setQuestion(undefined);
|
||||||
|
|
@ -109,14 +125,22 @@ const JoinRoom: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmitAnswer = (answer: string | number | boolean, idQuestion: number) => {
|
const handleOnSubmitAnswer = (answer: AnswerType, idQuestion: number) => {
|
||||||
|
console.info(`JoinRoom: handleOnSubmitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
|
||||||
const answerData: AnswerSubmissionToBackendType = {
|
const answerData: AnswerSubmissionToBackendType = {
|
||||||
roomName: roomName,
|
roomName: roomName,
|
||||||
answer: answer,
|
answer: answer,
|
||||||
username: username,
|
username: username,
|
||||||
idQuestion: idQuestion
|
idQuestion: idQuestion
|
||||||
};
|
};
|
||||||
localStorage.setItem(`Answer${idQuestion}`, JSON.stringify(answer));
|
// localStorage.setItem(`Answer${idQuestion}`, JSON.stringify(answer));
|
||||||
|
setAnswers((prevAnswers) => {
|
||||||
|
console.log(`JoinRoom: handleOnSubmitAnswer: prevAnswers: ${JSON.stringify(prevAnswers)}`);
|
||||||
|
const newAnswers = [...prevAnswers]; // Create a copy of the previous answers array
|
||||||
|
newAnswers[idQuestion - 1] = answerData; // Update the specific answer
|
||||||
|
return newAnswers; // Return the new array
|
||||||
|
});
|
||||||
|
console.log(`JoinRoom: handleOnSubmitAnswer: answers: ${JSON.stringify(answers)}`);
|
||||||
webSocketService.submitAnswer(answerData);
|
webSocketService.submitAnswer(answerData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -154,6 +178,7 @@ const JoinRoom: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<StudentModeQuiz
|
<StudentModeQuiz
|
||||||
questions={questions}
|
questions={questions}
|
||||||
|
answers={answers}
|
||||||
submitAnswer={handleOnSubmitAnswer}
|
submitAnswer={handleOnSubmitAnswer}
|
||||||
disconnectWebSocket={disconnect}
|
disconnectWebSocket={disconnect}
|
||||||
/>
|
/>
|
||||||
|
|
@ -163,6 +188,7 @@ const JoinRoom: React.FC = () => {
|
||||||
question && (
|
question && (
|
||||||
<TeacherModeQuiz
|
<TeacherModeQuiz
|
||||||
questionInfos={question}
|
questionInfos={question}
|
||||||
|
answers={answers}
|
||||||
submitAnswer={handleOnSubmitAnswer}
|
submitAnswer={handleOnSubmitAnswer}
|
||||||
disconnectWebSocket={disconnect}
|
disconnectWebSocket={disconnect}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,11 @@ const ManageRoom: React.FC = () => {
|
||||||
if (!quizStarted) return;
|
if (!quizStarted) return;
|
||||||
|
|
||||||
if (quizMode === 'teacher') {
|
if (quizMode === 'teacher') {
|
||||||
webSocketService.nextQuestion(formattedRoomName, currentQuestion);
|
webSocketService.nextQuestion(
|
||||||
|
{roomName: formattedRoomName,
|
||||||
|
questions: quizQuestions,
|
||||||
|
questionIndex: Number(currentQuestion?.question.id) - 1,
|
||||||
|
isLaunch: false});
|
||||||
} else if (quizMode === 'student') {
|
} else if (quizMode === 'student') {
|
||||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +228,10 @@ const ManageRoom: React.FC = () => {
|
||||||
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
||||||
|
|
||||||
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
||||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[nextQuestionIndex]);
|
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||||
|
questions: quizQuestions,
|
||||||
|
questionIndex: nextQuestionIndex,
|
||||||
|
isLaunch: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
const previousQuestion = () => {
|
const previousQuestion = () => {
|
||||||
|
|
@ -234,7 +241,7 @@ const ManageRoom: React.FC = () => {
|
||||||
|
|
||||||
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
|
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
|
||||||
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
|
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
|
||||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[prevQuestionIndex]);
|
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
const initializeQuizQuestion = () => {
|
const initializeQuizQuestion = () => {
|
||||||
|
|
@ -262,7 +269,7 @@ const ManageRoom: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentQuestion(quizQuestions[0]);
|
setCurrentQuestion(quizQuestions[0]);
|
||||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[0]);
|
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
const launchStudentMode = () => {
|
const launchStudentMode = () => {
|
||||||
|
|
@ -300,9 +307,8 @@ const ManageRoom: React.FC = () => {
|
||||||
const showSelectedQuestion = (questionIndex: number) => {
|
const showSelectedQuestion = (questionIndex: number) => {
|
||||||
if (quiz?.content && quizQuestions) {
|
if (quiz?.content && quizQuestions) {
|
||||||
setCurrentQuestion(quizQuestions[questionIndex]);
|
setCurrentQuestion(quizQuestions[questionIndex]);
|
||||||
|
|
||||||
if (quizMode === 'teacher') {
|
if (quizMode === 'teacher') {
|
||||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[questionIndex]);
|
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { io, Socket } from 'socket.io-client';
|
import { io, Socket } from 'socket.io-client';
|
||||||
|
import { QuestionType } from 'src/Types/QuestionType';
|
||||||
|
|
||||||
// Must (manually) sync these types to server/socket/socket.js
|
// Must (manually) sync these types to server/socket/socket.js
|
||||||
|
|
||||||
|
|
@ -59,12 +60,19 @@ class WebSocketService {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
nextQuestion(roomName: string, question: unknown) {
|
nextQuestion(args: {roomName: string, questions: QuestionType[] | undefined, questionIndex: number, isLaunch: boolean}) {
|
||||||
console.log('WebsocketService: nextQuestion', roomName, question);
|
// deconstruct args
|
||||||
if (!question) {
|
const { roomName, questions, questionIndex, isLaunch } = args;
|
||||||
|
console.log('WebsocketService: nextQuestion', roomName, questions, questionIndex, isLaunch);
|
||||||
|
if (!questions || !questions[questionIndex]) {
|
||||||
throw new Error('WebsocketService: nextQuestion: question is null');
|
throw new Error('WebsocketService: nextQuestion: question is null');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
|
if (isLaunch) {
|
||||||
|
this.socket.emit('launch-teacher-mode', { roomName, questions });
|
||||||
|
}
|
||||||
|
const question = questions[questionIndex];
|
||||||
this.socket.emit('next-question', { roomName, question });
|
this.socket.emit('next-question', { roomName, question });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,17 +109,29 @@ describe("websocket server", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should send next question", (done) => {
|
test("should launch teacher mode", (done) => {
|
||||||
studentSocket.on("next-question", (question) => {
|
studentSocket.on("launch-teacher-mode", (questions) => {
|
||||||
expect(question).toEqual({ question: "question2" });
|
expect(questions).toEqual([
|
||||||
|
{ question: "question1" },
|
||||||
|
{ question: "question2" },
|
||||||
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
teacherSocket.emit("next-question", {
|
teacherSocket.emit("launch-teacher-mode", {
|
||||||
roomName: "ROOM1",
|
roomName: "ROOM1",
|
||||||
question: { question: "question2" },
|
questions: [{ question: "question1" }, { question: "question2" }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should send next question", (done) => {
|
||||||
|
studentSocket.on("next-question", ( question ) => {
|
||||||
|
expect(question).toBe("question2");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
teacherSocket.emit("next-question", { roomName: "ROOM1", question: 'question2'},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("should send answer", (done) => {
|
test("should send answer", (done) => {
|
||||||
teacherSocket.on("submit-answer-room", (answer) => {
|
teacherSocket.on("submit-answer-room", (answer) => {
|
||||||
expect(answer).toEqual({
|
expect(answer).toEqual({
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ const setupWebsocket = (io) => {
|
||||||
socket.to(roomName).emit("next-question", question);
|
socket.to(roomName).emit("next-question", question);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("launch-teacher-mode", ({ roomName, questions }) => {
|
||||||
|
socket.to(roomName).emit("launch-teacher-mode", questions);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("launch-student-mode", ({ roomName, questions }) => {
|
socket.on("launch-student-mode", ({ roomName, questions }) => {
|
||||||
socket.to(roomName).emit("launch-student-mode", questions);
|
socket.to(roomName).emit("launch-student-mode", questions);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue