From eb81d4bf02013072dc94e414c9970cf305e782fa Mon Sep 17 00:00:00 2001 From: G Date: Fri, 5 Sep 2025 15:32:04 +0000 Subject: [PATCH] feat: implemented a functional login screen leveraging redux store to automatically redirect to the home screen once successfully logged in. --- src/features/auth/api.ts | 4 ++-- src/features/auth/components/LoginForm.tsx | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/features/auth/slice.ts | 9 ++++++++- src/navigations/AppMainStackNavigator.tsx | 5 +++-- src/screens/UserLoginScreen.tsx | 114 ++++++++++++------------------------------------------------------------------------------------------------------ 5 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 src/features/auth/components/LoginForm.tsx diff --git a/src/features/auth/api.ts b/src/features/auth/api.ts index 2239919..ebe5ec1 100644 --- a/src/features/auth/api.ts +++ b/src/features/auth/api.ts @@ -1,5 +1,5 @@ import { axiosInstance } from "@/axios"; -import type { LoginPayload, Token } from "./types"; +import type { LoginPayload, Token, UserData } from "./types"; export const login = (data: LoginPayload) => { return axiosInstance.post("/login/token/", data); @@ -7,5 +7,5 @@ export const login = (data: LoginPayload) => { // On this one you just provide the bear token and its should return the userData. export const getUserData = () => { - return axiosInstance.get("/login/token/"); + return axiosInstance.get("/user-info/"); }; diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx new file mode 100644 index 0000000..0b2ae80 --- /dev/null +++ b/src/features/auth/components/LoginForm.tsx @@ -0,0 +1,98 @@ +import { asp as g } from "@asp/asp"; +import * as Button from "@components/ButtonNew"; +import * as Input from "@components/InputNew"; +import * as Modal from "@components/Modal"; +import { MaterialIcons } from "@expo/vector-icons"; +import Feather from "@expo/vector-icons/Feather"; +import { useMutation } from "@tanstack/react-query"; +import type { AxiosError } from "axios"; +import { useRef, useState } from "react"; +import { Text, View } from "react-native"; +import { useDispatch } from "react-redux"; +import { getUserData, login } from "../api"; +import { setToken, setUserData } from "../slice"; + +export const LoginForm = () => { + const dispatch = useDispatch(); + + const usernameRef = useRef(""); + const passwordRef = useRef(""); + + const loginMutation = useMutation({ + mutationFn: () => login({ username: usernameRef.current, password: passwordRef.current }), + onSuccess: (data) => { + dispatch(setToken(data.data)); + useDataMutation.mutate(); + }, + onError: (error: AxiosError) => { + setError(JSON.stringify(error.response?.data) || error.message); + }, + }); + + const useDataMutation = useMutation({ + mutationFn: getUserData, + onSuccess: (data) => { + dispatch(setUserData(data.data)); + }, + }); + + const [error, setError] = useState(""); + return ( + + Connexion + Bienvenue, vous nous avez manqué ! + + + Addresse e-mail + + + (usernameRef.current = v)} /> + + + + + Mot de passe + + + (passwordRef.current = v)} /> + + + + loginMutation.mutate()} + > + Se connecter + + + Créer un compte + + + {/* MODAL */} + + + + {error} + + setError("")}> + OK + + + + + + ); +}; diff --git a/src/features/auth/slice.ts b/src/features/auth/slice.ts index 9eceeb4..aa5484a 100644 --- a/src/features/auth/slice.ts +++ b/src/features/auth/slice.ts @@ -18,6 +18,13 @@ export const authSlice = createSlice({ name: "auth", initialState: initialState, reducers: { + setToken: (state, action: PayloadAction) => { + state.token = action.payload; + }, + setUserData: (state, action: PayloadAction) => { + state.user = action.payload; + state.isAuthenticated = true; + }, login: (state, action: PayloadAction) => { state.isAuthenticated = true; state.user = action.payload.user; @@ -29,5 +36,5 @@ export const authSlice = createSlice({ }, }); -export const { login, logout } = authSlice.actions; +export const { login, logout, setToken, setUserData } = authSlice.actions; export const authReducer = authSlice.reducer; diff --git a/src/navigations/AppMainStackNavigator.tsx b/src/navigations/AppMainStackNavigator.tsx index c767f93..6b4c0ad 100644 --- a/src/navigations/AppMainStackNavigator.tsx +++ b/src/navigations/AppMainStackNavigator.tsx @@ -1,4 +1,3 @@ -import { useUserAuthenticationContext } from "@/contexts/UserAuthenticationContext"; import { LOG } from "@logger"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import HomeUserNotLoggedIn from "@screens/HomeUserNotLoggedIn"; @@ -6,6 +5,8 @@ import PaymentResultScreen from "@screens/PaymentResultScreen"; import UserLoginScreen from "@screens/UserLoginScreen"; import WaveQrCodePaymentScreen from "@screens/WaveQrCodePaymentScreen"; import { memo } from "react"; +import { useSelector } from "react-redux"; +import type { RootState } from "@/redux"; import { AppBottomTabsNavigator } from "./AppBottomTabsNavigator"; import type { ImainStackNavigator } from "./Types"; @@ -50,6 +51,6 @@ const AppMainStackNavigator: React.FC = ({ isAuthen export default memo(AppMainStackNavigator); export const AppMainStackNavigatorAuthWrapper = () => { - const { isAuthenticated } = useUserAuthenticationContext(); + const isAuthenticated = useSelector((state: RootState) => state.auth.isAuthenticated); return ; }; diff --git a/src/screens/UserLoginScreen.tsx b/src/screens/UserLoginScreen.tsx index bbd31de..0a9ec6d 100644 --- a/src/screens/UserLoginScreen.tsx +++ b/src/screens/UserLoginScreen.tsx @@ -1,112 +1,22 @@ -import { useUserAuthenticationContext } from "@/contexts/UserAuthenticationContext"; -import type { MainStackScreenComponentProps } from "@/navigations/Types"; -import Button from "@components/Button"; -import InputWithTopLabel from "@components/InputWithTopLabel"; -import Box from "@components/bases/Box"; -import WrapperWithDefaultBeasyBackgroundAndSafeAreaFull from "@components/wrappers/WrapperWithDefaultBeasyBackgroundAndSafeAreaFull"; -import { Fontisto } from "@expo/vector-icons"; +import { asp as g } from "@asp/asp"; import { LOG } from "@logger"; -import Card from "@re-card"; -import Text from "@re-text"; -import { useCallback, useState } from "react"; -import { TouchableOpacity } from "react-native"; -import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { ImageBackground, View } from "react-native"; +import { LoginForm } from "@/features/auth/components/LoginForm"; +import type { MainStackScreenComponentProps } from "@/navigations/Types"; const log = LOG.extend("UserLoginScreen"); const UserLoginScreen: MainStackScreenComponentProps<"userLoginScreen"> = ({ navigation }) => { log.debug("UserLoginScreen"); - const { login, isAuthenticating } = useUserAuthenticationContext(); - - // TODO : Remove default value for email and password - const [email, setEmail] = useState("admin"); - const [password, setPassword] = useState("admin"); - const insets = useSafeAreaInsets(); - - const submit = useCallback(() => { - login(email, password); - }, [email, password, login]); return ( - - - - - navigation.goBack()}> - - - - - Mot de passe oublie ? - - - - - - - - - Connexion - - Bienvenue, vous nous avez manqué ! - - - - setEmail(email)} - /> - setPassword(text)} - /> - - - -