From 363dfa5c745881c350ebd040da90d203cc438c70 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Tue, 10 Dec 2024 18:56:50 +0100 Subject: [PATCH] Stockage du jeton d'authentification dans le store local, permettant l'utilisation de hooks --- client/app/(tabs)/settings.tsx | 6 ++-- client/app/_layout.tsx | 37 ++++++++----------- client/app/login.tsx | 13 +++++-- client/components/LoginProvider.tsx | 25 +++++++++++++ client/hooks/useAuth.ts | 12 +++++++ client/hooks/useLocation.ts | 4 +-- client/utils/features/location/authSlice.ts | 40 +++++++++++++++++++++ client/utils/store.ts | 2 ++ 8 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 client/components/LoginProvider.tsx create mode 100644 client/hooks/useAuth.ts create mode 100644 client/utils/features/location/authSlice.ts diff --git a/client/app/(tabs)/settings.tsx b/client/app/(tabs)/settings.tsx index 498a790..e3de771 100644 --- a/client/app/(tabs)/settings.tsx +++ b/client/app/(tabs)/settings.tsx @@ -1,16 +1,16 @@ -import * as SecureStore from '@/utils/SecureStore' +import { useAuth } from '@/hooks/useAuth' import { useRouter } from 'expo-router' import { FAB, List, Surface } from 'react-native-paper' export default function HistoryScreen() { const router = useRouter() - const isLoggedIn = SecureStore.getItem("apiToken") !== null + const auth = useAuth() return ( router.navigate('/login')} />} onPress={() => router.navigate('/login')} /> diff --git a/client/app/_layout.tsx b/client/app/_layout.tsx index 61be9f8..fe93acd 100644 --- a/client/app/_layout.tsx +++ b/client/app/_layout.tsx @@ -1,42 +1,33 @@ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native' -import { Stack, useRouter } from "expo-router" +import { Stack } from "expo-router" import { useColorScheme } from '@/hooks/useColorScheme' import { StatusBar } from 'expo-status-bar' import { Provider as StoreProvider } from 'react-redux' import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper' import store from '@/utils/store' -import * as SecureStore from '@/utils/SecureStore' import { useStartBackgroundFetchServiceEffect } from '@/utils/background' import { useStartGeolocationServiceEffect } from '@/utils/geolocation' -import { useEffect } from 'react' -import { useRouteInfo } from 'expo-router/build/hooks' +import LoginProvider from '@/components/LoginProvider' export default function RootLayout() { useStartGeolocationServiceEffect() useStartBackgroundFetchServiceEffect() const colorScheme = useColorScheme() - const router = useRouter() - const route = useRouteInfo() - - // Si on est pas connecté⋅e, on reste sur la fenêtre de connexion - useEffect(() => { - const isLoggedIn = SecureStore.getItem("apiToken") !== null - if (!isLoggedIn && route.pathname !== "/login") - router.navigate("/login") - }, [route, router]) return ( - - - - - - - - - - + + + + + + + + + + + + ) } diff --git a/client/app/login.tsx b/client/app/login.tsx index 63859a8..f3a464c 100644 --- a/client/app/login.tsx +++ b/client/app/login.tsx @@ -1,3 +1,4 @@ +import { useAuth, useAuthLogin, useAuthLogout } from "@/hooks/useAuth" import * as SecureStore from "@/utils/SecureStore" import { useRouter } from "expo-router" import { useRef, useState } from "react" @@ -6,9 +7,14 @@ import { Appbar, Button, Dialog, Portal, Surface, Text, TextInput } from "react- export default function Login() { const router = useRouter() - const [isLoggedIn, setIsLoggedIn] = useState(SecureStore.getItem("apiToken") !== null) + + const auth = useAuth() + const authLogin = useAuthLogin() + const authLogout = useAuthLogout() + + const isLoggedIn = auth.loggedIn const [loggingIn, setLoggingIn] = useState(false) - const [name, setName] = useState(SecureStore.getItem('apiName') ?? "") + const [name, setName] = useState(auth.name ?? "") const [password, setPassword] = useState("") const [errorDialogVisible, setErrorDialogVisible] = useState(false) const [errorTitle, setErrorTitle] = useState("") @@ -45,6 +51,7 @@ export default function Login() { return } setLoggingIn(false) + authLogin({ name: name, token: resp.accessToken }) SecureStore.setItem("apiName", name) if (Platform.OS !== "web") { // Le stockage navigateur n'est pas sûr, on évite de stocker un mot de passe à l'intérieur @@ -58,10 +65,10 @@ export default function Login() { } async function logout() { + authLogout() await SecureStore.deleteItemAsync("apiName") await SecureStore.deleteItemAsync("apiPassword") await SecureStore.deleteItemAsync("apiToken") - setIsLoggedIn(false) } return ( diff --git a/client/components/LoginProvider.tsx b/client/components/LoginProvider.tsx new file mode 100644 index 0000000..40fb3f9 --- /dev/null +++ b/client/components/LoginProvider.tsx @@ -0,0 +1,25 @@ +import { useAuth } from "@/hooks/useAuth" +import { Href, useRouter } from "expo-router" +import { useRouteInfo } from "expo-router/build/hooks" +import { ReactNode, useEffect } from "react" + +type Props = { + loginRedirect: Href + children: ReactNode +} + +export default function LoginProvider({ loginRedirect, children }: Props) { + const router = useRouter() + const route = useRouteInfo() + + // Si on est pas connecté⋅e, on reste sur la fenêtre de connexion + const auth = useAuth() + useEffect(() => { + if (!auth.loggedIn && route.pathname !== loginRedirect) + router.navigate(loginRedirect) + }, [auth, route, router]) + + return <> + {children} + +} \ No newline at end of file diff --git a/client/hooks/useAuth.ts b/client/hooks/useAuth.ts new file mode 100644 index 0000000..0f414cd --- /dev/null +++ b/client/hooks/useAuth.ts @@ -0,0 +1,12 @@ +import { useAppDispatch, useAppSelector } from "./useStore" +import { AuthPayload, login, logout } from "@/utils/features/location/authSlice" + +export const useAuth = () => useAppSelector((state) => state.auth) +export const useAuthLogin = () => { + const dispath = useAppDispatch() + return (payload: AuthPayload) => dispath(login(payload)) +} +export const useAuthLogout = () => { + const dispatch = useAppDispatch() + return () => dispatch(logout()) +} diff --git a/client/hooks/useLocation.ts b/client/hooks/useLocation.ts index 240f852..856b708 100644 --- a/client/hooks/useLocation.ts +++ b/client/hooks/useLocation.ts @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from "./useStore" import { setLocation } from "@/utils/features/location/locationSlice" export const useLocation = () => useAppSelector((state) => state.location.location) -export const useSetLocation = () => (location: LocationObject) => { +export const useSetLocation = () => { const dispatch = useAppDispatch() - dispatch(setLocation(location)) + return (location: LocationObject) => dispatch(setLocation(location)) } diff --git a/client/utils/features/location/authSlice.ts b/client/utils/features/location/authSlice.ts new file mode 100644 index 0000000..9365c35 --- /dev/null +++ b/client/utils/features/location/authSlice.ts @@ -0,0 +1,40 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import * as SecureStore from '@/utils/SecureStore' + +interface AuthState { + loggedIn: boolean, + name: string | null, + token: string | null, +} + +export interface AuthPayload { + name: string | null, + token: string | null, +} + +const initialState: AuthState = { + loggedIn: SecureStore.getItem('apiName') !== null && SecureStore.getItem('apiToken') !== null, + name: SecureStore.getItem('apiName'), + token: SecureStore.getItem('apiToken'), +} + +export const authSlice = createSlice({ + name: 'auth', + initialState: initialState, + reducers: { + login: (state, action: PayloadAction) => { + state.loggedIn = true + state.name = action.payload.name + state.token = action.payload.token + }, + logout: (state) => { + state.loggedIn = false + state.name = null + state.token = null + } + }, +}) + +export const { login, logout } = authSlice.actions + +export default authSlice.reducer diff --git a/client/utils/store.ts b/client/utils/store.ts index 35fd24e..68f6892 100644 --- a/client/utils/store.ts +++ b/client/utils/store.ts @@ -1,8 +1,10 @@ import { configureStore } from '@reduxjs/toolkit' +import authReducer from './features/location/authSlice' import locationReducer from './features/location/locationSlice' const store = configureStore({ reducer: { + auth: authReducer, location: locationReducer, }, })