diff --git a/assets/stars.png b/assets/stars.png new file mode 100644 index 0000000000000000000000000000000000000000..09c430621cb58984d1b88f1e46e5d71c2b9a3284 Binary files /dev/null and b/assets/stars.png differ diff --git a/components/PlayingQuiz/EndQuiz/EndQuizListPlayer.tsx b/components/PlayingQuiz/EndQuiz/EndQuizListPlayer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a2378cb446f3f22237a3fb64c61e8618cb88b1b9 --- /dev/null +++ b/components/PlayingQuiz/EndQuiz/EndQuizListPlayer.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import { FlatList, View, Text, StyleSheet, Image } from "react-native"; +import UserModel from "../../../models/UserModel"; + +interface Props { + users: UserModel[]; + maxScore: number; +} + +export default function EndQuizListPlayer({ users, maxScore }: Props) { + const renderUser = ({ item, index }: { item: UserModel; index: number }) => ( + <View style={[styles.userContainer, index === 0 && styles.firstPlace]}> + <Image + source={require("../../../assets/ProfilBaseImage.png")} // Replace with actual profile picture if available + style={styles.avatar} + /> + <View style={styles.infoContainer}> + <Text style={[styles.username, index === 0 && styles.firstPlaceText]}> + {item.username.toUpperCase()} + </Text> + <Text style={styles.score}> + {10 + index}/{maxScore} {/* Adjust score logic as needed */} + </Text> + </View> + </View> + ); + + return ( + <View style={styles.container}> + <FlatList + data={users} + renderItem={renderUser} + keyExtractor={(item) => item.id.toString()} + showsVerticalScrollIndicator={false} + /> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + width: "100%", + padding: 20, + backgroundColor: "#fff", + borderRadius: 20, + }, + userContainer: { + flexDirection: "row", + alignItems: "center", + backgroundColor: "#f9f9f9", + borderRadius: 10, + padding: 15, + marginBottom: 10, + shadowColor: "#000", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 3, + elevation: 2, + }, + firstPlace: { + // backgroundColor: "#ffe700", + // shadowColor: "#ff9900", + // shadowOpacity: 0.3, + }, + avatar: { + width: 40, + height: 40, + borderRadius: 25, + marginRight: 15, + }, + infoContainer: { + flex: 1, + }, + username: { + fontSize: 18, + fontWeight: "bold", + color: "#333", + }, + firstPlaceText: { + // color: "#ff9900", + }, + score: { + fontSize: 16, + color: "#666", + }, +}); diff --git a/components/PlayingQuiz/EndQuiz/UserScore.tsx b/components/PlayingQuiz/EndQuiz/UserScore.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d57489ca4e833af884ab88b7610f6b1ff2d7a3ca --- /dev/null +++ b/components/PlayingQuiz/EndQuiz/UserScore.tsx @@ -0,0 +1,79 @@ +import UserModel from "../../../models/UserModel"; +import { View, Text, StyleSheet, Image } from "react-native"; + +interface Props { + user: UserModel; + score: number; + maxScore: number; + isFirst: boolean; +} + +export default function UserScore({ user, score, maxScore, isFirst }: Props) { + + function shortenString(input: string): string { + if (input.length <= 5) { + return input; // Si la chaÎne est dÊjà de 4 caractères ou moins, on la retourne telle quelle + } + return `${input.slice(0, 5)}...`; // Garde les 4 premiers caractères et ajoute "..." + } + + return ( + <View style={styles.container}> + <View style={[styles.avatarContainer, isFirst && styles.firstAvatar]}> + <Image + source={require('../../../assets/ProfilBaseImage.png')} // URL de l'avatar ou placeholder + style={styles.avatar} + /> + </View> + <Text style={styles.userName}>{shortenString(user.username.toUpperCase())}</Text> + <Text style={styles.userScore}> + {score}/{maxScore} + </Text> + </View> + ); +} + + + +const styles = StyleSheet.create({ + container: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "space-around", + marginHorizontal: 10, + }, + avatarContainer: { + width: 70, + height: 70, + borderRadius: 50, + backgroundColor: "#d9e3f0", // Fond gris clair + alignItems: "center", + justifyContent: "center", + }, + firstAvatar: { + width: 90, + height: 90, + // borderWidth: 3, // Ajoute une bordure pour le premier utilisateur + // borderColor: "#FFD700", // Couleur or pour marquer le premier + }, + avatar: { + width: "100%", + height: "100%", + resizeMode: "cover", + }, + userName: { + fontSize: 16, + fontWeight: "bold", + color: "#000", + marginTop: 5, + textAlign: "center", + }, + userScore: { + fontSize: 14, + fontWeight: "500", + color: "#333", + textAlign: "center", + marginTop: 5, + }, +}); diff --git a/routes/StackNavigator.tsx b/routes/StackNavigator.tsx index 86ab70bcabc6d6e1105e0fbc05266bda348c041d..b2d92cf8ad470d3874d34e4bf50d52ffcc09f8fa 100644 --- a/routes/StackNavigator.tsx +++ b/routes/StackNavigator.tsx @@ -13,6 +13,7 @@ import Lobby from "../screens/Multiplayer/Lobby/Lobby"; import PlayQuiz from "../screens/Home/PlayQuiz/PlayQuiz"; import MyQuizzes from "../screens/Home/MyQuizzes/MyQuizzes"; import InformationsOfQuiz from "../screens/Community/InformationsOfQuiz/InformationsOfQuiz"; +import EndQuizMulti from "../screens/PlayingQuiz/EndQuizMulti/EndQuizMulti"; import MultiInformationsOfQuiz from "../screens/Multiplayer/MultiplayerCommunity/InformationsOfQuiz/MultiInformationsOfQuiz"; import InformationsOfRuns from "../screens/Home/MyQuizzes/InformationsOfRuns"; @@ -28,6 +29,7 @@ export default function StackNavigator() { <Stack.Screen name="PlayingQuiz" component={PlayingQuiz} options={{ headerShown: false }}/> <Stack.Screen name="PlayQuiz" component={PlayQuiz} options={{ headerShown: false }}/> <Stack.Screen name="EndQuiz" component={EndQuiz} options={{ headerShown: false }}/> + <Stack.Screen name="EndQuizMulti" component={EndQuizMulti} options={{ headerShown: false }}/> <Stack.Screen name="MyQuizzes" component={MyQuizzes} options={{ headerShown: false }}/> <Stack.Screen name="InformationsOfQuiz" component={InformationsOfQuiz} options={{ headerShown: false }}/> <Stack.Screen name="InformationsOfRuns" component={InformationsOfRuns} options={{ headerShown: false }}/> diff --git a/screens/Home/Home.tsx b/screens/Home/Home.tsx index dc3b6386c593af56bdff59db656ae4ea5f1f281b..1369070d1e5c849fb7e13757b625c59773b17d18 100644 --- a/screens/Home/Home.tsx +++ b/screens/Home/Home.tsx @@ -3,12 +3,35 @@ import TemplateMono from "../../templates/TemplateMono"; import HomeChild from "./HomeChild"; import TemplateMenu from "../../templates/TemplateMenu"; import {NavigationProp} from "@react-navigation/native"; +import {Quiz} from "../../models/Quiz"; interface Props { navigation: NavigationProp<any>; } export default function Home({navigation}: Props) { + const debugQuiz: Quiz = { + id: "debug-quiz-001", + name: "Debug Quiz", + description: "This is a basic quiz for debugging purposes.", + score: 0, + questionIndex: 0, + questionCount: 0, + categoryId: 1, + difficultyId: 1, + authorId: 1, + category: { + id: 1, + name: "General Knowledge", + }, + difficulty: { + id: 1, + name: "Easy", + }, + questions: [], // Tableau de questions vide + }; + + navigation.navigate("EndQuizMulti", {quiz: debugQuiz}); return ( <View style={styles.containerGlobal}> <TemplateMenu navigation={navigation} headerNavigation={false} buttonGoBack={false}> diff --git a/screens/PlayingQuiz/EndQuiz/EndQuiz.tsx b/screens/PlayingQuiz/EndQuiz/EndQuiz.tsx index 0547db053c1a3a1ad16a19114186a7b829620c31..bb73a9b5f86249c4efcdbbad26605a727d144c7f 100644 --- a/screens/PlayingQuiz/EndQuiz/EndQuiz.tsx +++ b/screens/PlayingQuiz/EndQuiz/EndQuiz.tsx @@ -37,78 +37,3 @@ const styles = StyleSheet.create({ flexDirection: 'column', }, }); - - - -// import DefaultButton from "../../components/DefaultButton"; -// import { TextsStyles } from "../../styles/TextsStyles"; -// import { ButtonsStyles } from "../../styles/ButtonsStyles"; -// import {useTranslation} from "react-i18next"; -// import QuizModel from "../../models/QuizModel"; -// import {NavigationProp, RouteProp} from "@react-navigation/native"; - -// type RoutePropsType = { -// EndQuiz: { -// quiz: QuizModel; -// }; -// }; - -// interface Props { -// navigation: NavigationProp<any>; -// route: RouteProp<RoutePropsType, "EndQuiz">; -// } - -// export default function EndQuiz({ navigation, route }: Props) { -// const { quiz } = route.params; -// const {t} = useTranslation(); -// const handleButtonRePlayPressed = () => { -// quiz.nbActualQuestion = 1; -// quiz.score = 0; -// navigation.reset({ -// index: 0, -// routes: [ -// { -// name: "PlayingQuiz", -// params: {quiz: quiz}, -// }, -// ] -// }); -// } - -// const handleButtonHomePressed = () => { -// navigation.navigate('Home'); -// } - -// return ( -// <View style={styles.container}> -// <Image source={require('../../assets/FondTemplate.png')} style={styles.image}/> -// <Text style={TextsStyles.titleText}>{t("app.screens.endQuiz.score")} : {quiz.score}</Text> -// <Text style={TextsStyles.subtitleText}>{((quiz.score/quiz.nbQuestions)*100 > 50 ) ? t("app.screens.endQuiz.lose") : t("app.screens.endQuiz.win")}</Text> -// <View style={styles.buttonContainer}> -// <DefaultButton text={t("app.screens.endQuiz.replay")} handleButtonPressed={handleButtonRePlayPressed} buttonStyle={ButtonsStyles.defaultButton}></DefaultButton> -// <DefaultButton text={t("app.screens.endQuiz.goHome")} handleButtonPressed={handleButtonHomePressed}></DefaultButton> -// </View> -// </View> -// ) -// } - -// const styles = StyleSheet.create({ -// container: { -// display: 'flex', -// width: '100%', -// height: '100%', -// alignItems: 'center', -// justifyContent: 'center', -// gap: 10, -// }, -// buttonContainer: { -// width: '80%', -// margin: '10%', -// gap: 20 -// }, -// image: { -// width: '100%', -// height: '100%', -// position: 'absolute', -// }, -// }); \ No newline at end of file diff --git a/screens/PlayingQuiz/EndQuizMulti/EndQuizMulti.tsx b/screens/PlayingQuiz/EndQuizMulti/EndQuizMulti.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1945637e5a85c3473285e25902d0c195fcd5c9a5 --- /dev/null +++ b/screens/PlayingQuiz/EndQuizMulti/EndQuizMulti.tsx @@ -0,0 +1,41 @@ +import {NavigationProp, RouteProp} from "@react-navigation/native"; +import { View, StyleSheet, Image, Text } from "react-native"; +import TemplateMenu from "../../../templates/TemplateMenu"; +import {Quiz} from "../../../models/Quiz"; +import EndQuizChild from "../EndQuiz/EndQuizChild"; +import EndQuizMultiChild from "./EndQuizMultiChild"; + +type RoutePropsType = { + EndQuizChild: { + quiz: Quiz; + }; +}; +interface Props { + route: RouteProp<RoutePropsType, "EndQuizChild">; + navigation: NavigationProp<any>; +} + +export default function EndQuizMulti({route,navigation}: Props) { + const { quiz } = route.params; + return ( + <View style={styles.containerGlobal}> + <TemplateMenu navigation={navigation} headerNavigation={false} buttonGoBack={false}> + <View> + <EndQuizMultiChild navigation={navigation} quiz={quiz}/> + </View> + </TemplateMenu> + </View> + ); +} + +const styles = StyleSheet.create({ + containerGlobal: { + display: 'flex', + width: '100%', + height: '100%', + flexDirection: 'column', + }, +}); + + + diff --git a/screens/PlayingQuiz/EndQuizMulti/EndQuizMultiChild.tsx b/screens/PlayingQuiz/EndQuizMulti/EndQuizMultiChild.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3ba9918c7243b1430d078be01e61f408597d5504 --- /dev/null +++ b/screens/PlayingQuiz/EndQuizMulti/EndQuizMultiChild.tsx @@ -0,0 +1,156 @@ +import { View, Image, Text, StyleSheet } from "react-native"; +import DefaultButton from "../../../components/buttons/DefaultButton"; +import { NavigationProp, RouteProp } from "@react-navigation/native"; +import {Quiz} from "../../../models/Quiz"; +import {useQuizService} from "../../../services/QuizService"; +import HttpError from "../../../services/HttpError"; +import UserScore from "../../../components/PlayingQuiz/EndQuiz/UserScore"; +import UserModel from "../../../models/UserModel"; +import EndQuizListPlayer from "../../../components/PlayingQuiz/EndQuiz/EndQuizListPlayer"; + + + +interface Props { + navigation: NavigationProp<any>; + quiz: Quiz; +} + +export default function EndQuizMultiChild({ navigation, quiz }: Props) { + + const {restartQuiz, remainingQuiz} = useQuizService() + + const handleRestartPress = async () => { + const isRestarted = await restartQuiz(quiz.id); + if(HttpError.isHttpError(isRestarted)){ + console.log(isRestarted.message); + return + } + if(!isRestarted) { + return; + } + const quizRestarted = await remainingQuiz(quiz.id); + if(HttpError.isHttpError(quizRestarted)){ + console.log(quizRestarted.message); + return + } + + navigation.reset({ + index: 0, + routes: [ + { + name: "PlayingQuiz", + params: {quizRecovered: quizRestarted}, + }, + ] + }); + }; + + const handleBackToMenu = () => { + navigation.navigate('TabNavigator'); + }; + const user = new UserModel(10,"Thomassss", "Thomas"); + + return ( + <View style={styles.container}> + <View style={styles.containerHeader}> + <Image source={require('../../../assets/TitleApp.png')}/> + </View> + <View style={styles.containerBody}> + <Image + source={require('../../../assets/stars.png')} // URL de l'avatar ou placeholder + style={styles.stars} + /> + <View style={styles.usersContainer}> + <UserScore user={user} score={10} maxScore={quiz.questionCount} isFirst={false}/> + <UserScore user={user} score={10} maxScore={quiz.questionCount} isFirst={true}/> + <UserScore user={user} score={10} maxScore={quiz.questionCount} isFirst={false}/> + </View> + <EndQuizListPlayer users={[new UserModel(1, "user1", "user1@email.com"), new UserModel(2, "user2", "user2@email.com")]} maxScore={quiz.questionCount}/> + + <DefaultButton text="RESTART" handleButtonPressed={handleRestartPress} buttonStyle={styles.whiteButton} buttonText={styles.whiteButtonText}></DefaultButton> + <DefaultButton text="BACK TO MENU" handleButtonPressed={handleBackToMenu} buttonStyle={styles.blueButton} buttonText={styles.blueButtonText}></DefaultButton> + </View> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + height: '100%', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + }, + usersContainer: { + display: 'flex', + width: '100%', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-around', + }, + containerHeader: { + height: '10%', + width: '100%', + alignItems: 'center', + }, + containerBody: { + height: '80%', + width: '90%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around', + backgroundColor: '#F3F3F3', + borderRadius: 40, + padding: '5%', + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + elevation: 5, + }, + TextBodyTitle: { + textAlign: 'center', + fontSize: 24, + fontWeight: 'bold', + }, + TextBody: { + textAlign: 'center', + fontSize: 22, + fontWeight: 'bold', + }, + whiteButton: { + height: '12%', + width: '80%', + backgroundColor: '#FFFFFF', + borderColor: '#2B73FE', + borderWidth: 4, + borderRadius: 10, + padding: 0 + }, + whiteButtonText: { + fontSize: 24, + fontWeight: 'bold', + color: '#2B73FE', + }, + blueButton: { + height: '12%', + width: '80%', + backgroundColor: '#2B73FE', + borderRadius: 10, + padding: 0 + }, + blueButtonText: { + fontSize: 24, + fontWeight: 'bold', + color: '#FFFFFF', + }, + stars: { + resizeMode: "cover", + }, +}); \ No newline at end of file