Stockage du jeton d'authentification dans le store local, permettant l'utilisation de hooks
This commit is contained in:
parent
72862da3a6
commit
363dfa5c74
@ -1,16 +1,16 @@
|
|||||||
import * as SecureStore from '@/utils/SecureStore'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
import { useRouter } from 'expo-router'
|
import { useRouter } from 'expo-router'
|
||||||
import { FAB, List, Surface } from 'react-native-paper'
|
import { FAB, List, Surface } from 'react-native-paper'
|
||||||
|
|
||||||
export default function HistoryScreen() {
|
export default function HistoryScreen() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const isLoggedIn = SecureStore.getItem("apiToken") !== null
|
const auth = useAuth()
|
||||||
return (
|
return (
|
||||||
<Surface
|
<Surface
|
||||||
style={{ flex: 1 }}>
|
style={{ flex: 1 }}>
|
||||||
<List.Item
|
<List.Item
|
||||||
title="Connexion au serveur"
|
title="Connexion au serveur"
|
||||||
description={isLoggedIn ? "Vous êtes déjà connecté⋅e" : "Vous n'êtes pas connecté⋅e"}
|
description={auth.loggedIn ? "Vous êtes déjà connecté⋅e" : "Vous n'êtes pas connecté⋅e"}
|
||||||
right={() => <FAB icon="login" size="small" onPress={() => router.navigate('/login')} />}
|
right={() => <FAB icon="login" size="small" onPress={() => router.navigate('/login')} />}
|
||||||
onPress={() => router.navigate('/login')} />
|
onPress={() => router.navigate('/login')} />
|
||||||
</Surface>
|
</Surface>
|
||||||
|
@ -1,42 +1,33 @@
|
|||||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
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 { useColorScheme } from '@/hooks/useColorScheme'
|
||||||
import { StatusBar } from 'expo-status-bar'
|
import { StatusBar } from 'expo-status-bar'
|
||||||
import { Provider as StoreProvider } from 'react-redux'
|
import { Provider as StoreProvider } from 'react-redux'
|
||||||
import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper'
|
import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper'
|
||||||
import store from '@/utils/store'
|
import store from '@/utils/store'
|
||||||
import * as SecureStore from '@/utils/SecureStore'
|
|
||||||
import { useStartBackgroundFetchServiceEffect } from '@/utils/background'
|
import { useStartBackgroundFetchServiceEffect } from '@/utils/background'
|
||||||
import { useStartGeolocationServiceEffect } from '@/utils/geolocation'
|
import { useStartGeolocationServiceEffect } from '@/utils/geolocation'
|
||||||
import { useEffect } from 'react'
|
import LoginProvider from '@/components/LoginProvider'
|
||||||
import { useRouteInfo } from 'expo-router/build/hooks'
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
useStartGeolocationServiceEffect()
|
useStartGeolocationServiceEffect()
|
||||||
useStartBackgroundFetchServiceEffect()
|
useStartBackgroundFetchServiceEffect()
|
||||||
const colorScheme = useColorScheme()
|
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 (
|
return (
|
||||||
<StoreProvider store={store}>
|
<StoreProvider store={store}>
|
||||||
<PaperProvider theme={colorScheme === 'dark' ? MD3DarkTheme : MD3LightTheme}>
|
<LoginProvider loginRedirect={'/login'}>
|
||||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
<PaperProvider theme={colorScheme === 'dark' ? MD3DarkTheme : MD3LightTheme}>
|
||||||
<Stack>
|
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack>
|
||||||
<Stack.Screen name="login" options={{ headerShown: false }} />
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="+not-found" />
|
<Stack.Screen name="login" options={{ headerShown: false }} />
|
||||||
</Stack>
|
<Stack.Screen name="+not-found" />
|
||||||
<StatusBar style="auto" />
|
</Stack>
|
||||||
</ThemeProvider>
|
<StatusBar style="auto" />
|
||||||
</PaperProvider>
|
</ThemeProvider>
|
||||||
|
</PaperProvider>
|
||||||
|
</LoginProvider>
|
||||||
</StoreProvider>
|
</StoreProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useAuth, useAuthLogin, useAuthLogout } from "@/hooks/useAuth"
|
||||||
import * as SecureStore from "@/utils/SecureStore"
|
import * as SecureStore from "@/utils/SecureStore"
|
||||||
import { useRouter } from "expo-router"
|
import { useRouter } from "expo-router"
|
||||||
import { useRef, useState } from "react"
|
import { useRef, useState } from "react"
|
||||||
@ -6,9 +7,14 @@ import { Appbar, Button, Dialog, Portal, Surface, Text, TextInput } from "react-
|
|||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter()
|
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 [loggingIn, setLoggingIn] = useState(false)
|
||||||
const [name, setName] = useState(SecureStore.getItem('apiName') ?? "")
|
const [name, setName] = useState(auth.name ?? "")
|
||||||
const [password, setPassword] = useState("")
|
const [password, setPassword] = useState("")
|
||||||
const [errorDialogVisible, setErrorDialogVisible] = useState(false)
|
const [errorDialogVisible, setErrorDialogVisible] = useState(false)
|
||||||
const [errorTitle, setErrorTitle] = useState("")
|
const [errorTitle, setErrorTitle] = useState("")
|
||||||
@ -45,6 +51,7 @@ export default function Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLoggingIn(false)
|
setLoggingIn(false)
|
||||||
|
authLogin({ name: name, token: resp.accessToken })
|
||||||
SecureStore.setItem("apiName", name)
|
SecureStore.setItem("apiName", name)
|
||||||
if (Platform.OS !== "web") {
|
if (Platform.OS !== "web") {
|
||||||
// Le stockage navigateur n'est pas sûr, on évite de stocker un mot de passe à l'intérieur
|
// 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() {
|
async function logout() {
|
||||||
|
authLogout()
|
||||||
await SecureStore.deleteItemAsync("apiName")
|
await SecureStore.deleteItemAsync("apiName")
|
||||||
await SecureStore.deleteItemAsync("apiPassword")
|
await SecureStore.deleteItemAsync("apiPassword")
|
||||||
await SecureStore.deleteItemAsync("apiToken")
|
await SecureStore.deleteItemAsync("apiToken")
|
||||||
setIsLoggedIn(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
25
client/components/LoginProvider.tsx
Normal file
25
client/components/LoginProvider.tsx
Normal file
@ -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}
|
||||||
|
</>
|
||||||
|
}
|
12
client/hooks/useAuth.ts
Normal file
12
client/hooks/useAuth.ts
Normal file
@ -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())
|
||||||
|
}
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from "./useStore"
|
|||||||
import { setLocation } from "@/utils/features/location/locationSlice"
|
import { setLocation } from "@/utils/features/location/locationSlice"
|
||||||
|
|
||||||
export const useLocation = () => useAppSelector((state) => state.location.location)
|
export const useLocation = () => useAppSelector((state) => state.location.location)
|
||||||
export const useSetLocation = () => (location: LocationObject) => {
|
export const useSetLocation = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
dispatch(setLocation(location))
|
return (location: LocationObject) => dispatch(setLocation(location))
|
||||||
}
|
}
|
||||||
|
40
client/utils/features/location/authSlice.ts
Normal file
40
client/utils/features/location/authSlice.ts
Normal file
@ -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<AuthPayload>) => {
|
||||||
|
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
|
@ -1,8 +1,10 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
|
import authReducer from './features/location/authSlice'
|
||||||
import locationReducer from './features/location/locationSlice'
|
import locationReducer from './features/location/locationSlice'
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
auth: authReducer,
|
||||||
location: locationReducer,
|
location: locationReducer,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user