diff --git a/models/User.ts b/models/User.ts index e5724209d950ea550795501a4db5c281d9542af3..506d8f03dd326eb6485bf2005f23c8e4887e6da5 100644 --- a/models/User.ts +++ b/models/User.ts @@ -9,4 +9,10 @@ export interface User { email: string; username: string; stats?: Stats; +} + +export enum UserConnectionState { + LOADING = "Loading", + NOT_CONNECTED = "NotConnected", + CONNECTED = "Connected", } \ No newline at end of file diff --git a/screens/Multiplayer/MultiplayerChild.tsx b/screens/Multiplayer/MultiplayerChild.tsx index be9843aa37131b0174cb7eb3a998a9b3fab0470e..f7eee12a124a37b0f0b756c894c4af19a7f43195 100644 --- a/screens/Multiplayer/MultiplayerChild.tsx +++ b/screens/Multiplayer/MultiplayerChild.tsx @@ -1,18 +1,20 @@ -import { View, StyleSheet, Image, Modal, TextInput } from "react-native"; -import DefaultButton from "../../components/DefaultButton"; +import {Image, StyleSheet, Text, View} from "react-native"; import {useTranslation} from "react-i18next"; -import { useState } from "react"; -import { TextsStyles } from "../../styles/TextsStyles"; +import React, {useEffect, useState} from "react"; import ModalJoinQuiz from "../../components/ModalJoinQuiz"; import MenuButton from "../../components/buttons/MenuButton"; import {NavigationProp} from "@react-navigation/native"; -import {useQuizService} from "../../services/QuizService"; import HttpError from "../../services/HttpError"; +import {useUserService} from "../../services/UserService"; +import {UserConnectionState} from "../../models/User"; +import LoadingIndicator from "../../components/loading/LoadingIndicator"; +import Connection from "../Profil/Connection/Connection"; interface Props { navigation: NavigationProp<any>; } + /** * HomeChild : Enfant de l'écran Home, il contient le menu principal * @param navigation - navigation @@ -20,6 +22,22 @@ interface Props { export default function MultiplayerChild({navigation}: Props) { const [modalVisible, setModalVisible] = useState(false); const {t} = useTranslation(); + const [userConnectionState, setUserConnectionState] = useState(UserConnectionState.LOADING); + const { getInformationsUser, logoutUser } = useUserService(); + + const getUser = async () => { + setUserConnectionState(UserConnectionState.LOADING); + const user = await getInformationsUser(); + if(user instanceof HttpError){ + setUserConnectionState(UserConnectionState.NOT_CONNECTED); + return; + } + setUserConnectionState(UserConnectionState.CONNECTED); + } + + useEffect(() => { + getUser(); + }, []); const handleButtonCommunityPressed = async () => { navigation.navigate("MultiplayerCommunity"); @@ -35,15 +53,24 @@ export default function MultiplayerChild({navigation}: Props) { return ( <View style={styles.containerGlobal}> - <View style={styles.imageContainer}> - <Image source={require('../../assets/TitleApp.png')} style={styles.image} /> - </View> - <View style={styles.buttonContainer}> - <MenuButton text={"COMMUNITY"} handleButtonPressed={handleButtonCommunityPressed}/> - <MenuButton text={"PLAY AN ONLINE QUIZ"} handleButtonPressed={handleOnlineQuizPressed}/> - <MenuButton text={"ONGOING QUIZZES"} handleButtonPressed={handleButtonOngoingQuizzesPressed}/> - </View> - <ModalJoinQuiz modalVisible={modalVisible} setModalVisible={setModalVisible} navigation={navigation}/> + {userConnectionState === UserConnectionState.LOADING && ( + <LoadingIndicator/> + )} + {userConnectionState === UserConnectionState.CONNECTED && ( + <> + <View style={styles.imageContainer}> + <Image source={require('../../assets/TitleApp.png')} style={styles.image} /> + </View> + <View style={styles.buttonContainer}> + <MenuButton text={"COMMUNITY"} handleButtonPressed={handleButtonCommunityPressed}/> + <MenuButton text={"PLAY AN ONLINE QUIZ"} handleButtonPressed={handleOnlineQuizPressed}/> + <MenuButton text={"ONGOING QUIZZES"} handleButtonPressed={handleButtonOngoingQuizzesPressed}/> + </View> + </> + )} + {userConnectionState === UserConnectionState.NOT_CONNECTED && ( + <Connection title={"Multiplayer"} navigation={navigation}/> + )} </View> ); } @@ -58,6 +85,13 @@ const styles = StyleSheet.create({ }, image: { }, + title: { + fontSize: 40, + fontWeight: "bold", + color: "#00b3f4", + marginBottom: 10, + alignSelf: "center", // Centre uniquement le titre + }, button: { borderRadius: 40, height: '35%', diff --git a/screens/Profil/Connection/Connection.tsx b/screens/Profil/Connection/Connection.tsx new file mode 100644 index 0000000000000000000000000000000000000000..34e5023ac2687f3f774f2b6cff98ed649c212375 --- /dev/null +++ b/screens/Profil/Connection/Connection.tsx @@ -0,0 +1,72 @@ +import {Keyboard, StyleSheet, Text, TouchableOpacity, TouchableWithoutFeedback, View} from "react-native"; +import ProfilSelectMode from "../ProfilSelectMode"; +import React, {useState} from "react"; +import {ProfilSelectModeType} from "../../../models/SelectModeType"; +import {NavigationProp} from "@react-navigation/native"; +import ConnectionLogin from "./ConnectionLogin"; +import ConnectionSignup from "./ConnectionSignup"; + +interface Props { + title: string; + navigation: NavigationProp<any>; +} + +export default function Connection({title, navigation}: Props) { + const [mode, setMode] = useState<ProfilSelectModeType>("login"); + + return ( + <TouchableWithoutFeedback onPress={Keyboard.dismiss}> + <View style={styles.container}> + <Text style={styles.title}>{title}</Text> + <View style={styles.containerModal}> + <ProfilSelectMode mode={mode} setMode={setMode} /> + {mode === "login" ? ( + <ConnectionLogin navigation={navigation} /> + ) : ( + <ConnectionSignup navigation={navigation} /> + )} + </View> + </View> + </TouchableWithoutFeedback> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "100%", + }, + title: { + fontSize: 40, + fontWeight: "bold", + color: "#00b3f4", + marginBottom: 10, + alignSelf: "center", // Centre uniquement le titre + }, + backgroundShadow: { + height: "100%", + width: "100%", + backgroundColor: "#000000", + opacity: 0.55, + position: "absolute", + }, + containerModal: { + height: 600, + width: "90%", + backgroundColor: "#D9D9D9", + borderRadius: 40, + padding: '7%', + }, + closeButton: { + position: "absolute", + top: '1%', + right: '1%', + zIndex: 1, + backgroundColor: "white", + borderRadius: 10, + margin: '5%', + }, +}); \ No newline at end of file diff --git a/screens/Profil/Modals/ProfilModalLogin.tsx b/screens/Profil/Connection/ConnectionLogin.tsx similarity index 92% rename from screens/Profil/Modals/ProfilModalLogin.tsx rename to screens/Profil/Connection/ConnectionLogin.tsx index 41c3553478ccab26cac44f51d9bb3a81214d85ce..0dcd82493f9d1224c74afc5da444637a4f69ae1e 100644 --- a/screens/Profil/Modals/ProfilModalLogin.tsx +++ b/screens/Profil/Connection/ConnectionLogin.tsx @@ -8,9 +8,8 @@ import BlueButton from "../../../components/buttons/BlueButton"; interface Props { navigation: NavigationProp<any>; - closeModal: () => void; } -export default function ProfilModalLogin({navigation, closeModal}: Props) { +export default function ConnectionLogin({navigation}: Props) { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState<string | null>(null); @@ -27,7 +26,6 @@ export default function ProfilModalLogin({navigation, closeModal}: Props) { return; } setError(null); - closeModal(); navigation.reset({ routes: [{name: "TabNavigator"}], }); @@ -54,6 +52,7 @@ const styles = StyleSheet.create({ justifyContent: 'flex-start', rowGap: '7%', marginTop: "3%", + marginBottom: "auto", // Espacement entre le texte d'erreur et les inputs }, container:{ height: 475, @@ -80,7 +79,6 @@ const styles = StyleSheet.create({ errorText: { color: 'red', fontSize: 16, - marginBottom: 10, // Espacement entre le texte d'erreur et les inputs textAlign: 'center', // Centrer le message d'erreur }, }); diff --git a/screens/Profil/Modals/ProfilModalSignup.tsx b/screens/Profil/Connection/ConnectionSignup.tsx similarity index 93% rename from screens/Profil/Modals/ProfilModalSignup.tsx rename to screens/Profil/Connection/ConnectionSignup.tsx index 124a42e99625ac03bcc0797f59d641027a527402..542012f28b24c0760ce4579e6c79f961dd0c06b1 100644 --- a/screens/Profil/Modals/ProfilModalSignup.tsx +++ b/screens/Profil/Connection/ConnectionSignup.tsx @@ -9,9 +9,8 @@ import BlueButton from "../../../components/buttons/BlueButton"; interface Props { navigation: NavigationProp<any>; - closeModal: () => void; } -export default function ProfilModalSignup({navigation, closeModal}: Props) { +export default function ConnectionSignup({navigation}: Props) { const [email, setEmail] = useState(""); const [username, setUsername] = useState(""); @@ -25,6 +24,10 @@ export default function ProfilModalSignup({navigation, closeModal}: Props) { setError("Please fill in all fields"); return; } + if(password.length < 8){ + setError("Password must be at least 8 characters long"); + return; + } if(password !== repeatPassword){ setError("Passwords do not match"); return; @@ -35,7 +38,6 @@ export default function ProfilModalSignup({navigation, closeModal}: Props) { return; } setError(null); - closeModal(); navigation.reset({ routes: [{name: "TabNavigator"}], }); @@ -62,13 +64,13 @@ const styles = StyleSheet.create({ display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', - rowGap: '7%', + rowGap: '5%', marginTop: "3%", }, container:{ height: 475, paddingTop: '5%', - isplay: 'flex', + display: 'flex', justifyContent: 'space-between' }, textInput: { diff --git a/screens/Profil/Modals/ProfilModal.tsx b/screens/Profil/Modals/ProfilModal.tsx deleted file mode 100644 index c24d3fcedf942926dd22b10a766ca9526365300e..0000000000000000000000000000000000000000 --- a/screens/Profil/Modals/ProfilModal.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from "react"; -import { - Modal, - View, - StyleSheet, - TextInput, - TouchableOpacity, - Keyboard, - TouchableWithoutFeedback, -} from "react-native"; -import ProfilSelectMode from "../ProfilSelectMode"; -import { ProfilSelectModeType } from "../../../models/SelectModeType"; -import ProfilModalLogin from "./ProfilModalLogin"; -import ProfilModalSignup from "./ProfilModalSignup"; -import Icon from "react-native-vector-icons/MaterialIcons"; -import {NavigationProp} from "@react-navigation/native"; - -interface Props { - modalVisible: boolean; - setModalVisible: (visible: boolean) => void; - mode: ProfilSelectModeType; - setMode: (mode: ProfilSelectModeType) => void; - navigation: NavigationProp<any>; -} - -export default function ProfilModal({ - modalVisible, - setModalVisible, - mode, - setMode, - navigation - }: Props) { - const closeModal = () => { - setModalVisible(false); - } - - return ( - <Modal - animationType="slide" - transparent={true} - visible={modalVisible} - onRequestClose={() => { - setModalVisible(!modalVisible); - }} - > - <TouchableWithoutFeedback onPress={Keyboard.dismiss}> - <View style={styles.container}> - <TouchableOpacity - style={styles.closeButton} - onPress={() => setModalVisible(false)} // Ferme la modal - > - <Icon name="close" size={50} color="#000" /> - </TouchableOpacity> - <View style={styles.backgroundShadow} /> - <View style={styles.containerModal}> - <ProfilSelectMode mode={mode} setMode={setMode} /> - {mode === "login" ? ( - <ProfilModalLogin navigation={navigation} closeModal={closeModal}/> - ) : ( - <ProfilModalSignup navigation={navigation} closeModal={closeModal}/> - )} - </View> - </View> - </TouchableWithoutFeedback> - </Modal> - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - }, - backgroundShadow: { - height: "100%", - width: "100%", - backgroundColor: "#000000", - opacity: 0.55, - position: "absolute", - }, - containerModal: { - height: 600, - width: "90%", - backgroundColor: "#D9D9D9", - borderRadius: 40, - padding: '7%', - }, - closeButton: { - position: "absolute", - top: '1%', - right: '1%', - zIndex: 1, - backgroundColor: "white", - borderRadius: 10, - margin: '5%', - }, -}); diff --git a/screens/Profil/ProfilChild.tsx b/screens/Profil/ProfilChild.tsx index dd89a53344e94e9a57ef8337c398679669fcdd47..bb601bf40937aeb53835e4b2c7bbff197d431d68 100644 --- a/screens/Profil/ProfilChild.tsx +++ b/screens/Profil/ProfilChild.tsx @@ -1,24 +1,20 @@ import { NavigationProp } from "@react-navigation/native"; import { View, StyleSheet, Image, Text, TouchableOpacity } from "react-native"; -import ProfilModal from "./Modals/ProfilModal"; import React, { useEffect, useState } from "react"; -import ProfilHeaderAccount from "./ProfilHeaderAccount"; import { ProfilSelectModeType } from "../../models/SelectModeType"; import { useUserService } from "../../services/UserService"; import HttpError from "../../services/HttpError"; import Icon from "react-native-vector-icons/MaterialIcons"; -import {User} from "../../models/User"; +import {User, UserConnectionState} from "../../models/User"; import StatsOfUsers from "../../components/Profil/StatsOfUsers"; +import LoadingIndicator from "../../components/loading/LoadingIndicator"; +import Connection from "./Connection/Connection"; interface Props { navigation: NavigationProp<any>; } -enum UserConnectionState { - LOADING = "Loading", - NOT_CONNECTED = "NotConnected", - CONNECTED = "Connected", -} + export default function ProfilChild({ navigation }: Props) { const [isLogged, setIsLogged] = useState(false); @@ -28,6 +24,7 @@ export default function ProfilChild({ navigation }: Props) { const [userConnectionState, setUserConnectionState] = useState(UserConnectionState.LOADING); const { getInformationsUser, logoutUser } = useUserService(); + const getUser = async () => { setUserConnectionState(UserConnectionState.LOADING); const user = await getInformationsUser(); @@ -42,7 +39,6 @@ export default function ProfilChild({ navigation }: Props) { useEffect(() => { getUser(); - }, []); // Pour s'adapter quand l'état de connexion change @@ -55,33 +51,32 @@ export default function ProfilChild({ navigation }: Props) { if (!(result instanceof HttpError)) { setUser(undefined); // Réinitialise l'utilisateur après la déconnexion } + navigation.reset({ + routes: [{name: "TabNavigator"}], + }); }; return ( <View style={styles.containerGlobal}> + {userConnectionState === UserConnectionState.LOADING && ( + <LoadingIndicator/> + )} + {userConnectionState === UserConnectionState.CONNECTED && ( + <> <View style={styles.containerPlayerInfos}> <Image source={require('../../assets/TitleApp.png')} style={styles.titleImage} /> <Image source={require('../../assets/ProfilBaseImage.png')} style={styles.profilImage} /> <Text style={styles.pseudoText}>{user ? user.username : "?"}</Text> </View> - - {!user && ( - <ProfilHeaderAccount setModalVisible={setModalVisible} setMode={setMode} /> - )} - <ProfilModal - modalVisible={modalVisible} - setModalVisible={setModalVisible} - mode={mode} - setMode={setMode} - navigation={navigation} - /> <StatsOfUsers user={user}/> - - {user && ( - <TouchableOpacity style={styles.logoutButton} onPress={handleLogout}> - <Icon name="logout" size={40} color="#FF0000" /> - </TouchableOpacity> - )} + <TouchableOpacity style={styles.logoutButton} onPress={handleLogout}> + <Icon name="logout" size={40} color="#FF0000" /> + </TouchableOpacity> + </> + )} + {userConnectionState === UserConnectionState.NOT_CONNECTED && ( + <Connection title={"Profil"} navigation={navigation} /> + )} </View> ); } @@ -99,6 +94,13 @@ const styles = StyleSheet.create({ display: "flex", alignItems: "center", }, + title: { + fontSize: 40, + fontWeight: "bold", + color: "#00b3f4", + marginBottom: 10, + alignSelf: "center", // Centre uniquement le titre + }, titleImage: { width: "45%", resizeMode: "contain", diff --git a/screens/Profil/ProfilHeaderAccount.tsx b/screens/Profil/ProfilHeaderAccount.tsx deleted file mode 100644 index 6afcc5dfe8e598fef8f3fdc0907ad93d29457ae7..0000000000000000000000000000000000000000 --- a/screens/Profil/ProfilHeaderAccount.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { View, Text, TouchableOpacity, StyleSheet } from "react-native"; -import { ProfilSelectModeType } from "../../models/SelectModeType"; - -interface Props { - setModalVisible: (visible:boolean) => void; - setMode: (mode: ProfilSelectModeType) => void; -} - -export default function ProfilHeaderAccount({ setModalVisible, setMode }: Props) { - const handleAccountPress = (mode:ProfilSelectModeType) => { - setMode(mode); - setModalVisible(true); - }; - - return ( - <View style={styles.containerHeader}> - <TouchableOpacity onPress={() => handleAccountPress('login')}> - <Text style={styles.buttonHeader}>LOGIN</Text> - </TouchableOpacity> - <Text style={styles.textHeader}>{' OR '}</Text> - <TouchableOpacity onPress={() => handleAccountPress('signup')}> - <Text style={styles.buttonHeader}>CREATE AN ACCOUNT</Text> - </TouchableOpacity> - </View> - ) -} - - -const styles = StyleSheet.create({ - containerHeader: { - backgroundColor: '#D9D9D9', - height: '10%', - width: '100%', - position: 'absolute', - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center' - }, - textHeader: { - color: '#000000', - fontSize: 20, - fontWeight: 'bold', - }, - buttonHeader: { - textDecorationLine: 'underline', - color: '#000000', - fontSize: 20, - fontWeight: 'bold', - }, -}); \ No newline at end of file