Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x | import { RacingSansOne_400Regular } from '@expo-google-fonts/racing-sans-one'; import { NavigationContainer } from '@react-navigation/native'; import axios, { AxiosRequestConfig } from 'axios'; import { useFonts } from 'expo-font'; import { getItemAsync } from 'expo-secure-store'; import { observer } from 'mobx-react'; import React, { useEffect, useRef, useState } from 'react'; import { AppState } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { QueryClient, QueryClientProvider } from 'react-query'; import { Splash } from '../components/Splash'; import { API_URL, reportAxiosError } from '../globals'; import { useAuth } from '../hooks/useAuth'; import UnauthorizedNavigator from '../navigation/UnauthorizedNavigator'; import AuthorizedNavigator from '../navigation/AuthorizedNavigator'; import { authStore } from '../stores/AuthStore'; export enum TokenState { CheckingToken, ValidToken, InvalidToken } const queryClient = new QueryClient(); /** * Sub-root component of the app. Contains all global providers (NavigationContainer and SafeAreaProvider for React Navigation, QueryClientProvider for react-query) and is responsible for restricting unauthenticated users to the Intro screen by listening to {@link AuthStore}'s accessToken value. * @component */ const Atlas : React.FC = () => { /** * Ref that keeps track of the app's state (opened or closed) */ const appState = useRef(AppState.currentState); /** * Flag that is switched on when the app is checking for tokens in the keystore and in memory. When true, "Logging you in.." and a spinner will be displayed to the user. */ const [checkingToken, setCheckingToken] = useState<boolean>(true); const { refreshAccessToken } = useAuth(); const [fontsLoaded, error] = useFonts({ RacingSansOne_400Regular }); /** * Checks if there is an access token available in {@link AuthStore}, then checks if that access token is valid by calling the API. * If the response is valid, the access token will be stored in memory, otherwise the user will be directed to intro screen. */ const checkToken = async () => { // check both the mobx store and secure storage for the token let currentAccessToken = authStore.accessToken; if (!currentAccessToken) { currentAccessToken = await getItemAsync('accessToken'); } if (currentAccessToken) { // check to see if the token is valid by making test call const requestConfig: AxiosRequestConfig = { method: 'GET', url: API_URL + "/api/me/", headers: { "Authorization": "Bearer " + currentAccessToken } }; try { await axios(requestConfig); await authStore.setAccessTokenAsync(currentAccessToken); } catch (error) { // check if access token can be refreshed if (error.response.status == 401) { try { await refreshAccessToken(); // update authorization header w/ new token await axios({...requestConfig, headers: { "Authorization": "Bearer " + authStore.accessToken }}); } catch (error) { await authStore.setAccessTokenAsync(null); } } // something went wrong with the api call, log error and delete access token reportAxiosError('Something went wrong when retrieving an access token', error) await authStore.setAccessTokenAsync(null); } } else { // no access token was found, user will be taken to login await authStore.setAccessTokenAsync(null); } setCheckingToken(false); } useEffect(() => { /** * useEffect hook that is responsible for registering an appState "change" handler that will call {@linkcode checkToken} each time the app is opened or closed on the device. * @memberOf Atlas */ function registerAppStateChangeHandler() { AppState.addEventListener("change", (appState: string) => { if (appState == 'active') { checkToken(); } }); return () => { AppState.removeEventListener("change", (appState: string) => { if (appState == 'active') { checkToken(); } }); }; } registerAppStateChangeHandler(); }, []); useEffect(() => { /** * Calls {@linkcode checkToken} when a change to the access token stored in {@link AuthStore} is detected. * @memberOf Atlas */ const checkTokenOnAccessTokenChange = () => { checkToken() } checkTokenOnAccessTokenChange() }, [authStore.accessToken]); return ( <SafeAreaProvider> <NavigationContainer> {checkingToken ? <Splash/> : <QueryClientProvider client={queryClient}> {authStore.accessToken ? <AuthorizedNavigator /> : <UnauthorizedNavigator /> } </QueryClientProvider> } </NavigationContainer> </SafeAreaProvider> ); } export default observer(Atlas); |