mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge branch 'main' of https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir into feature/qr-code-url-generation
This commit is contained in:
commit
ef192eb784
4 changed files with 116 additions and 47 deletions
|
|
@ -51,9 +51,8 @@ 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)}`);
|
||||
setAnswers(questions ? Array(questions.length).fill({} as AnswerSubmissionToBackendType) : []);
|
||||
}, [questions]);
|
||||
|
||||
|
||||
|
|
@ -76,6 +75,7 @@ const JoinRoom: React.FC = () => {
|
|||
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
||||
setQuizMode('teacher');
|
||||
setIsWaitingForTeacher(true);
|
||||
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
||||
setQuestions(questions);
|
||||
// wait for next-question
|
||||
});
|
||||
|
|
@ -84,6 +84,7 @@ const JoinRoom: React.FC = () => {
|
|||
|
||||
setQuizMode('student');
|
||||
setIsWaitingForTeacher(false);
|
||||
setQuestions([]); // clear out from last time (in case quiz is repeated)
|
||||
setQuestions(questions);
|
||||
setQuestion(questions[0]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,11 +45,43 @@ const ManageRoom: React.FC = () => {
|
|||
const [quizMode, setQuizMode] = useState<'teacher' | 'student'>('teacher');
|
||||
const [connectingError, setConnectingError] = useState<string>('');
|
||||
const [currentQuestion, setCurrentQuestion] = useState<QuestionType | undefined>(undefined);
|
||||
const [quizStarted, setQuizStarted] = useState(false);
|
||||
const [formattedRoomName, setFormattedRoomName] = useState("");
|
||||
const [quizStarted, setQuizStarted] = useState<boolean>(false);
|
||||
const [formattedRoomName, setFormattedRoomName] = useState('');
|
||||
const [newlyConnectedUser, setNewlyConnectedUser] = useState<StudentType | null>(null);
|
||||
const roomUrl = `${window.location.origin}/student/join-room?roomName=${roomName}`;
|
||||
const [showQrModal, setShowQrModal] = useState(false);
|
||||
|
||||
// Handle the newly connected user in useEffect, because it needs state info
|
||||
// not available in the socket.on() callback
|
||||
useEffect(() => {
|
||||
if (newlyConnectedUser) {
|
||||
console.log(`Handling newly connected user: ${newlyConnectedUser.name}`);
|
||||
setStudents((prevStudents) => [...prevStudents, newlyConnectedUser]);
|
||||
|
||||
// only send nextQuestion if the quiz has started
|
||||
if (!quizStarted) {
|
||||
console.log(`!quizStarted: returning.... `);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: Number(currentQuestion?.question.id) - 1,
|
||||
isLaunch: true // started late
|
||||
});
|
||||
} else if (quizMode === 'student') {
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||
} else {
|
||||
console.error('Invalid quiz mode:', quizMode);
|
||||
}
|
||||
|
||||
// Reset the newly connected user state
|
||||
setNewlyConnectedUser(null);
|
||||
}
|
||||
}, [newlyConnectedUser]);
|
||||
|
||||
useEffect(() => {
|
||||
const verifyLogin = async () => {
|
||||
if (!ApiService.isLoggedIn()) {
|
||||
|
|
@ -61,6 +93,15 @@ const ManageRoom: React.FC = () => {
|
|||
verifyLogin();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomName) {
|
||||
console.error('Room name is missing!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Joining room: ${roomName}`);
|
||||
}, [roomName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomName || !quizId) {
|
||||
window.alert(
|
||||
|
|
@ -77,15 +118,6 @@ const ManageRoom: React.FC = () => {
|
|||
};
|
||||
}, [roomName, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomName) {
|
||||
console.error('Room name is missing!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Joining room: ${roomName}`);
|
||||
}, [roomName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (quizId) {
|
||||
const fetchQuiz = async () => {
|
||||
|
|
@ -130,6 +162,17 @@ const ManageRoom: React.FC = () => {
|
|||
const roomNameUpper = roomName.toUpperCase();
|
||||
setFormattedRoomName(roomNameUpper);
|
||||
console.log(`Creating WebSocket room named ${roomNameUpper}`);
|
||||
|
||||
/**
|
||||
* ATTENTION: Lire les variables d'état dans
|
||||
* les .on() n'est pas une bonne pratique.
|
||||
* Les valeurs sont celles au moment de la création
|
||||
* de la fonction et non au moment de l'exécution.
|
||||
* Il faut utiliser des refs pour les valeurs qui
|
||||
* changent fréquemment. Sinon, utiliser un trigger
|
||||
* de useEffect pour mettre déclencher un traitement
|
||||
* (voir user-joined plus bas).
|
||||
*/
|
||||
socket.on('connect', () => {
|
||||
webSocketService.createRoom(roomNameUpper);
|
||||
});
|
||||
|
|
@ -144,23 +187,9 @@ const ManageRoom: React.FC = () => {
|
|||
});
|
||||
|
||||
socket.on('user-joined', (student: StudentType) => {
|
||||
console.log(`Student joined: name = ${student.name}, id = ${student.id}, quizMode = ${quizMode}, quizStarted = ${quizStarted}`);
|
||||
|
||||
setStudents((prevStudents) => [...prevStudents, student]);
|
||||
|
||||
// only send nextQuestion if the quiz has started
|
||||
if (!quizStarted) return;
|
||||
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion(
|
||||
{roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: Number(currentQuestion?.question.id) - 1,
|
||||
isLaunch: false});
|
||||
} else if (quizMode === 'student') {
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||
}
|
||||
setNewlyConnectedUser(student);
|
||||
});
|
||||
|
||||
socket.on('join-failure', (message) => {
|
||||
setConnectingError(message);
|
||||
setSocket(null);
|
||||
|
|
@ -175,7 +204,6 @@ const ManageRoom: React.FC = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (socket) {
|
||||
console.log(`Listening for submit-answer-room in room ${formattedRoomName}`);
|
||||
socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => {
|
||||
|
|
@ -249,10 +277,12 @@ const ManageRoom: React.FC = () => {
|
|||
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
||||
|
||||
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: nextQuestionIndex,
|
||||
isLaunch: false});
|
||||
isLaunch: false
|
||||
});
|
||||
};
|
||||
|
||||
const previousQuestion = () => {
|
||||
|
|
@ -295,7 +325,12 @@ const ManageRoom: React.FC = () => {
|
|||
}
|
||||
|
||||
setCurrentQuestion(quizQuestions[0]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: 0,
|
||||
isLaunch: true
|
||||
});
|
||||
};
|
||||
|
||||
const launchStudentMode = () => {
|
||||
|
|
@ -311,21 +346,19 @@ const ManageRoom: React.FC = () => {
|
|||
};
|
||||
|
||||
const launchQuiz = () => {
|
||||
setQuizStarted(true);
|
||||
if (!socket || !formattedRoomName || !quiz?.content || quiz?.content.length === 0) {
|
||||
// TODO: This error happens when token expires! Need to handle it properly
|
||||
console.log(
|
||||
`Error launching quiz. socket: ${socket}, roomName: ${formattedRoomName}, quiz: ${quiz}`
|
||||
);
|
||||
setQuizStarted(true);
|
||||
|
||||
return;
|
||||
}
|
||||
console.log(`Launching quiz in ${quizMode} mode...`);
|
||||
switch (quizMode) {
|
||||
case 'student':
|
||||
setQuizStarted(true);
|
||||
return launchStudentMode();
|
||||
case 'teacher':
|
||||
setQuizStarted(true);
|
||||
return launchTeacherMode();
|
||||
}
|
||||
};
|
||||
|
|
@ -334,7 +367,12 @@ const ManageRoom: React.FC = () => {
|
|||
if (quiz?.content && quizQuestions) {
|
||||
setCurrentQuestion(quizQuestions[questionIndex]);
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
|
||||
webSocketService.nextQuestion({
|
||||
roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex,
|
||||
isLaunch: false
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -473,7 +511,6 @@ const ManageRoom: React.FC = () => {
|
|||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<div className="roomHeader">
|
||||
<DisconnectButton
|
||||
onReturn={handleReturn}
|
||||
|
|
@ -533,7 +570,6 @@ const ManageRoom: React.FC = () => {
|
|||
<QuestionDisplay
|
||||
showAnswer={false}
|
||||
question={currentQuestion?.question as Question}
|
||||
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,4 +127,11 @@ async function start() {
|
|||
});
|
||||
}
|
||||
|
||||
// Graceful shutdown on SIGINT (Ctrl+C)
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('Shutting down...');
|
||||
await db.closeConnection();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
start();
|
||||
|
|
|
|||
|
|
@ -1,28 +1,53 @@
|
|||
const { MongoClient } = require('mongodb');
|
||||
const dotenv = require('dotenv')
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
class DBConnection {
|
||||
|
||||
constructor() {
|
||||
this.mongoURI = process.env.MONGO_URI;
|
||||
this.databaseName = process.env.MONGO_DATABASE;
|
||||
this.client = null;
|
||||
this.connection = null;
|
||||
}
|
||||
|
||||
// Connect to the database, but don't reconnect if already connected
|
||||
async connect() {
|
||||
const client = new MongoClient(this.mongoURI);
|
||||
this.connection = await client.connect();
|
||||
if (this.connection) {
|
||||
console.log('Using existing MongoDB connection');
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the MongoClient only if the connection does not exist
|
||||
this.client = new MongoClient(this.mongoURI);
|
||||
await this.client.connect();
|
||||
this.connection = this.client.db(this.databaseName);
|
||||
console.log('MongoDB connected');
|
||||
return this.connection;
|
||||
} catch (error) {
|
||||
console.error('MongoDB connection error:', error);
|
||||
throw new Error('Failed to connect to MongoDB');
|
||||
}
|
||||
}
|
||||
|
||||
// Return the current database connection
|
||||
getConnection() {
|
||||
if (!this.connection) {
|
||||
throw new Error('Connexion MongoDB non établie');
|
||||
throw new Error('MongoDB connection not established');
|
||||
}
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
// Close the MongoDB connection gracefully
|
||||
async closeConnection() {
|
||||
if (this.client) {
|
||||
await this.client.close();
|
||||
console.log('MongoDB connection closed');
|
||||
}
|
||||
return this.connection.db(this.databaseName);
|
||||
}
|
||||
}
|
||||
|
||||
// Exporting the singleton instance
|
||||
const instance = new DBConnection();
|
||||
module.exports = instance;
|
||||
Loading…
Reference in a new issue