Skip to content
Snippets Groups Projects
Commit 60f1bda0 authored by GOEPP THOMAS's avatar GOEPP THOMAS
Browse files

refactor: playing quiz with SSE

parent e034448c
Branches
1 merge request!136Feature/138 refacto plaing quiz sse
Pipeline #325349 passed with stages
in 15 seconds
export type CustomEventType = "userJoined" | "nextQuestion" | "generateRun" | "gameStarted" | "answerRevealed"
import {Answer} from "../Answer";
export interface AnswerSSEResponse {
id: number;
text: string;
type:string;
categoryId: number;
quizId: string;
order: number;
answers: Answer[];
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import BlueButton from "../../../components/buttons/BlueButton";
import WhiteButton from "../../../components/buttons/WhiteButton";
import ModalInvitePlayer from "../../../components/Multiplayer/ModalInvitePlayer";
import EventSource from "react-native-sse";
import {CustomEventType} from "../../../models/CustomEventType";
interface Props {
navigation: NavigationProp<any>;
route: RouteProp<RoutePropsType, "Lobby">;
......@@ -26,7 +27,10 @@ type RoutePropsType = {
};
};
type CustomEventType = "userJoined" | "nextQuestion"
interface GenerateRunData {
runId: string; // ou number selon votre cas
// autres propriétés si nécessaire
}
export default function Lobby({navigation, route}: Props) {
......@@ -37,8 +41,9 @@ export default function Lobby({navigation, route}: Props) {
const [roomIdCreated, setRoomIdCreated] = useState<string | undefined>(roomId);
const [runId, setRunId] = useState<string>("");
const [showModal, setShowModal] = useState<boolean>(false);
const [esUseState, setEsUseState] = useState<EventSource<CustomEventType>>();
const {createParty} = useMultiService();
const {createParty, generateRun, startParty} = useMultiService();
const userInvite = { id: 0, username: 'Invite', email: 'invite@email.com'}
let goodId = roomId;
......@@ -55,9 +60,24 @@ export default function Lobby({navigation, route}: Props) {
setRoomIdCreated(response);
}
console.log("User try to joined party with ID:", goodId);
console.log(`https://klebert-host.com:33037/party/join/${goodId}`)
const es = new EventSource<CustomEventType>(`https://klebert-host.com:33037/party/join/${goodId}`);
setEsUseState(es);
es.addEventListener("open", async (event) => {
console.log("open event");
const responseGenerate = await generateRun();
if(HttpError.isHttpError(responseGenerate)) {
console.log("zzzzzzzzz");
return;
}
setRunId(responseGenerate);
});
es.addEventListener("gameStarted", async () => {
console.log("Game started event data:");
navigation.navigate("PlayingQuizMultiMode", {runId: runId, socket: es, roomId: roomId});
});
es.addEventListener("userJoined", (event) => {
console.log("User joined event data:", event.data);
......@@ -70,15 +90,18 @@ export default function Lobby({navigation, route}: Props) {
const filteredNewUsers = newUsers.filter((user) => !ids.has(user.id));
return [...prevPlayers, ...filteredNewUsers];
});
});
es.addEventListener("error", (err) => {
console.error("Error with SSE:", err);
console.log("Error with SSE:", err);
});
return () => es.close();
useEffect(() => {
return () => {
es.close();
};
}, []);
};
useEffect(() => {
......@@ -111,8 +134,8 @@ export default function Lobby({navigation, route}: Props) {
navigation.navigate("TabNavigator");
};
const onPlayPressed = () => {
navigation.navigate("PlayingQuizMultiMode", {runId: runId, socket: socket, roomId: roomIdCreated});
const onPlayPressed = async () => {
await startParty();
};
const onCancelPressed = () => {
......
......@@ -10,44 +10,55 @@ import PlayingQuizMultiModeBody from "./PlayingQuizMultiModeBody";
import {Socket} from "socket.io-client";
import {QuestionResponse} from "../../../../models/Response/QuestionResponse";
import {RunInformations} from "../../../../models/RunInformations";
import EventSource from "react-native-sse";
import {CustomEventType} from "../../../../models/CustomEventType";
import {AnswerSSEResponse} from "../../../../models/Response/AnswerSSEResponse";
import {useMultiService} from "../../../../services/MultiService";
type RoutePropsType = {
PlayingQuizMultiMode: {
runId: string;
socket: Socket;
es: EventSource<CustomEventType>;
roomId: string;
isHost: boolean;
};
};
enum QuizState {
ANSWERING= "Answering",
LOADING= "Loading",
SHOWING_RESULTS= "ShowingResults",
}
interface Props {
route: RouteProp<RoutePropsType, "PlayingQuizMultiMode">;
navigation: NavigationProp<any>;
}
export default function PlayingQuizMultiMode({route, navigation}:Props) {
const {runId, socket, roomId} = route.params;
const { getQuizInformations, getRunsInfo } = useQuizService();
const {runId, es, roomId, isHost} = route.params;
const {getQuizInformations, getRunsInfo} = useQuizService();
const [quizState, setQuizState] = useState(QuizState.ANSWERING);
const [quizInformations, setQuizInformations] = useState<QuizInformations | undefined>(undefined);
const [runInformations, setRunInformations] = useState<RunInformations>();
const [actualQuestion, setActualQuestion] = useState<QuestionResponse | undefined>(undefined);
const [actualQuestion, setActualQuestion] = useState<Question | undefined>(undefined);
const [score, setScore] = useState(0);
const {nextQuestion} = useMultiService();
const initSocket = async () => {
socket.on("gameStarted", (data) => {
console.log("Game started:", data);
});
socket.on("newQuestion", (data) => {
const question: QuestionResponse = data as QuestionResponse
const initES = async () => {
es.addEventListener("nextQuestion", async (event) => {
if (!event.data) return;
const question: Question = JSON.parse(event.data);
setActualQuestion(question);
// console.log(JSON.stringify(question, null, 2)); // Beautifie le JSON avec 2 espaces d'indentation
setQuizState(QuizState.ANSWERING);
});
}
}
const fetchRuns = async () => {
if(runId === undefined) return;
if (runId === undefined) return;
const runFetched = await getRunsInfo(runId);
if (runFetched instanceof HttpError) {
console.log("Error fetching runs info", runFetched);
......@@ -63,22 +74,24 @@ export default function PlayingQuizMultiMode({route, navigation}:Props) {
};
useEffect(() => {
console.log("PlayingQuizMultiMode starting");
initSocket();
initES();
fetchRuns();
setTimeout(() => {
socket.emit("startGame")
},500);
setTimeout(() => {
socket.emit("nextQuestion");
},2000);
console.log("fetching first question");
nextQuestion();
}, []);
return (
<TemplateDuo
childrenHeader={<PlayingQuizMultiModeHeader quizInformations={quizInformations} runId={runId} actualQuestion={actualQuestion} score={score} navigation={navigation}></PlayingQuizMultiModeHeader>}
childrenBody={<PlayingQuizMultiModeBody runId={runId} actualQuestion={actualQuestion} socket={socket} fetchActualQuestion={()=>{}} roomId={roomId} navigation={navigation}></PlayingQuizMultiModeBody>}
childrenHeader={<PlayingQuizMultiModeHeader quizInformations={quizInformations} runId={runId}
actualQuestion={actualQuestion} score={score}
navigation={navigation}></PlayingQuizMultiModeHeader>}
childrenBody={<PlayingQuizMultiModeBody runId={runId} actualQuestion={actualQuestion} es={es}
fetchActualQuestion={() => {
}} roomId={roomId} isHost={isHost}
navigation={navigation}
quizState={quizState}
setQuizState={setQuizState}>
</PlayingQuizMultiModeBody>}
/>
);
}
\ No newline at end of file
}
......@@ -13,14 +13,21 @@ import {QuestionResponse} from "../../../../models/Response/QuestionResponse";
import {RunInformations} from "../../../../models/RunInformations";
import {Socket} from "socket.io-client";
import {AnswerWsResponse} from "../../../../models/Response/AnswerWsResponse";
import EventSource from "react-native-sse";
import {CustomEventType} from "../../../../models/CustomEventType";
import {AnswerSSEResponse} from "../../../../models/Response/AnswerSSEResponse";
import {useMultiService} from "../../../../services/MultiService";
interface Props {
runId?: string;
actualQuestion?: QuestionResponse;
actualQuestion?: Question;
fetchActualQuestion: () => void;
socket: Socket;
es: EventSource<CustomEventType>;
roomId: string;
isHost: boolean;
quizState: QuizState;
setQuizState: (quizState: QuizState) => void;
navigation: NavigationProp<any>;
}
......@@ -67,14 +74,14 @@ const getStyleOfAnswerButton = (answerId: number, selectedAnswerIndex: number |
return styles.answerButton;
}
export default function PlayingQuizMultiModeBody({runId, actualQuestion, fetchActualQuestion, roomId, socket, navigation }: Props) {
export default function PlayingQuizMultiModeBody({runId, actualQuestion, fetchActualQuestion, roomId, es,isHost, quizState,setQuizState, navigation }: Props) {
const [selectedAnswerId, setSelectedAnswerId] = useState<number | null>(null);
const [quizState, setQuizState] = useState(QuizState.ANSWERING);
const [correctAnswerId, setCorrectAnswerId] = useState<number | null>(null);
const [isLastQuestion, setIsLastQuestion] = useState(false);
const [quizInformations, setQuizInformations] = useState<QuizInformations>();
const [runInformations, setRunInformations] = useState<RunInformations>();
const {answerQuestion, getRunsInfo, getQuizInformations} = useQuizService();
const {getRunsInfo, getQuizInformations} = useQuizService();
const {revealAnswer, answerQuestion, nextQuestion} = useMultiService();
const getCorrectAnswer = (answers: Answer[]): Answer => {
const correctAnswer = answers.find((answer) => answer.isCorrect);
if (!correctAnswer) return answers[0];
......@@ -83,15 +90,17 @@ export default function PlayingQuizMultiModeBody({runId, actualQuestion, fetchAc
const initSocket = async () => {
console.log("init socket in body");
socket.on("answersRevealed", (data: AnswerWsResponse) => {
const correctAnswerIdFetched = getCorrectAnswer(data.answers).id;
console.log("correctAnswerIdFetched", correctAnswerIdFetched);
setCorrectAnswerId(correctAnswerIdFetched);
es.addEventListener("answerRevealed", (event) => {
if(!event.data) return;
const answers: AnswerSSEResponse = JSON.parse(event.data);
setCorrectAnswerId(getCorrectAnswer(answers.answers).id);
setQuizState(QuizState.SHOWING_RESULTS);
});
// socket.on("score", (data) => {
// console.log("Score:", data);
// });
es.addEventListener("nextQuestion", (event) => {
});
}
useEffect(() => {
......@@ -104,22 +113,22 @@ export default function PlayingQuizMultiModeBody({runId, actualQuestion, fetchAc
if(selectedAnswerId === null || actualQuestion === undefined) return;
setQuizState(QuizState.LOADING);
socket.emit("submitAnswer", {
roomId,
runId,
questionId: actualQuestion.question.id,
answerId: selectedAnswerId,
});
await answerQuestion(actualQuestion.id, selectedAnswerId);
if(isHost)
{
revealAnswer();
}
setTimeout(() => {
socket.emit("revealAnswer", {runId: runId});
},500);
};
const onContinue = () => {
socket.emit("nextQuestion");
setQuizState(QuizState.ANSWERING);
setCorrectAnswerId(null);
setSelectedAnswerId(null);
if(isHost)
{
nextQuestion();
setQuizState(QuizState.ANSWERING);
setCorrectAnswerId(null);
setSelectedAnswerId(null);
}
}
const onAnsweredButtonClicked = (answerId: number) => {
......@@ -133,7 +142,7 @@ export default function PlayingQuizMultiModeBody({runId, actualQuestion, fetchAc
<View style={styles.buttonContainer}>
{actualQuestion ? (
<>
{actualQuestion.question.answers.map((answer, index) => (
{actualQuestion.answers.map((answer, index) => (
<AnswerButton
key={index}
text={answer.text}
......
......@@ -12,7 +12,7 @@ import {QuestionResponse} from "../../../../models/Response/QuestionResponse";
interface Props {
quizInformations?: QuizInformations;
runId?: string;
actualQuestion?: QuestionResponse;
actualQuestion?: Question;
score: number;
navigation: NavigationProp<any>
}
......@@ -42,7 +42,7 @@ export default function PlayingQuizMultiModeHeader({quizInformations, runId, act
<View style={styles.questionAndScoreContainer}>
<View style={styles.InformationsContainer}>
<Text style={TextsStyles.titleText}>{t("app.screens.question.question")}</Text>
<Text style={TextsStyles.titleText}>{actualQuestion ? actualQuestion.question.order + 1 :"?"}/{quizInformations ? quizInformations.questionCount : "?"}</Text>
<Text style={TextsStyles.titleText}>{actualQuestion ? actualQuestion.order + 1 :"?"}/{quizInformations ? quizInformations.questionCount : "?"}</Text>
</View>
<View style={styles.InformationsContainer}>
<Text style={TextsStyles.titleText}>{t("app.screens.question.score")}</Text>
......@@ -50,7 +50,7 @@ export default function PlayingQuizMultiModeHeader({quizInformations, runId, act
</View>
</View>
<View style={styles.QuizQuestionContainer}>
<Text style={TextsStyles.subtitleText}>{actualQuestion ? actualQuestion.question.text : "Loading..."}</Text>
<Text style={TextsStyles.subtitleText}>{actualQuestion ? actualQuestion.text : "Loading..."}</Text>
</View>
<ConfirmModal visible={isConfirmModalVisible} onClose={()=>setIsConfirmModalVisible(false)} onConfirm={handleConfirmModalConfirm}/>
......
......@@ -5,7 +5,7 @@ import HttpError from "./HttpError";
export const useMultiService = () => {
const url = "https://klebert-host.com:33037"
const createParty = async (quizId: string) : Promise<string | HttpError> => {
const createParty = async (quizId: string) : Promise<string | HttpError> => {
console.log("Create party with ID :", quizId);
console.log(url+"/party");
const requestBody = {
......@@ -29,8 +29,113 @@ export const useMultiService = () => {
}
}
const generateRun = async () : Promise<string | HttpError> => {
console.log("generateRun");
try {
const response = await axios.get(`${url}/party/generateRun`, {
withCredentials: true,
});
console.log("Generate run done");
return response.data.runId;
} catch (error: any) {
console.log("Error while generate run:", error);
if (error.response) {
return new HttpError(error.response.status, error.response.data?.message || "HTTP error");
} else {
return new HttpError(500, "Unexpected error: " + error.message);
}
}
};
const revealAnswer = async () : Promise<boolean | HttpError> => {
console.log("revealAnswer");
try {
const response = await axios.get(`${url}/party/revealAnswer`, {
withCredentials: true,
});
console.log("revealAnswer done");
return true;
} catch (error: any) {
console.log("Error while reveal Answer:", error);
if (error.response) {
return new HttpError(error.response.status, error.response.data?.message || "HTTP error");
} else {
return new HttpError(500, "Unexpected error: " + error.message);
}
}
};
const answerQuestion = async (questionId: number, answerId: number) : Promise<boolean | HttpError> => {
console.log("answerQuestion");
const requestBody = {
questionId: questionId,
answerId: answerId,
};
try {
const response = await axios.post(`${url}/party/answer`, requestBody,{
withCredentials: true,
});
console.log("answerQuestion done");
return true;
} catch (error: any) {
console.log("Error while reveal Answer:", error);
if (error.response) {
return new HttpError(error.response.status, error.response.data?.message || "HTTP error");
} else {
return new HttpError(500, "Unexpected error: " + error.message);
}
}
};
const nextQuestion = async () : Promise<boolean | HttpError> => {
console.log("nextQuestion");
try {
const response = await axios.get(`${url}/party/nextQuestion`, {
withCredentials: true,
});
console.log("nextQuestion done");
return true;
} catch (error: any) {
console.log("Error while nextQuestion:", error);
if (error.response) {
return new HttpError(error.response.status, error.response.data?.message || "HTTP error");
} else {
return new HttpError(500, "Unexpected error: " + error.message);
}
}
};
const startParty = async () : Promise<boolean | HttpError> => {
console.log("startParty");
try {
const response = await axios.get(`${url}/party/start`, {
withCredentials: true,
});
console.log("startParty done");
return true;
} catch (error: any) {
console.log("Error while startParty:", error);
if (error.response) {
return new HttpError(error.response.status, error.response.data?.message || "HTTP error");
} else {
return new HttpError(500, "Unexpected error: " + error.message);
}
}
};
return {
createParty: createParty,
generateRun: generateRun,
revealAnswer: revealAnswer,
nextQuestion: nextQuestion,
answerQuestion: answerQuestion,
startParty: startParty
}
}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment