diff --git a/src/front/auth/context.js b/src/front/auth/context.js index b820fb11..437df9a9 100644 --- a/src/front/auth/context.js +++ b/src/front/auth/context.js @@ -96,7 +96,7 @@ export function AuthContextProvider({ children, onReady }) { React.useEffect(() => { if (authInfo === null || authInfo) { - console.log('authInfo', authInfo); + console.log('authInfo', JSON.stringify(authInfo, null, 2)); onReady(); } }, [authInfo, onReady]); @@ -277,11 +277,7 @@ export function AuthContextProvider({ children, onReady }) { throw new Error('user is not logged in!'); } - try { - return await currentProvider.getBearerToken(); - } catch (error) { - throwAsyncError(error); - } + return await currentProvider.getBearerToken(); }; return ( diff --git a/src/front/auth/providers/utahid.js b/src/front/auth/providers/utahid.js index b1b747b5..6eb597c9 100644 --- a/src/front/auth/providers/utahid.js +++ b/src/front/auth/providers/utahid.js @@ -139,6 +139,8 @@ export default function useUtahIDProvider() { accessToken.current && !isTokenExpired(jwt_decode(accessToken.current)) ) { + console.log('returning cached token'); + return prefix + accessToken.current; } @@ -183,21 +185,17 @@ export default function useUtahIDProvider() { return tokens?.accessToken; } - try { - const tokenResponse = await refreshAsync( - { - clientId: config.CLIENT_ID, - refreshToken: refreshToken.current, - }, - discovery, - ); + const tokenResponse = await refreshAsync( + { + clientId: config.CLIENT_ID, + refreshToken: refreshToken.current, + }, + discovery, + ); - setAccessToken(tokenResponse.accessToken); + setAccessToken(tokenResponse.accessToken); - return tokenResponse.accessToken; - } catch (error) { - throwAsyncError(error); - } + return tokenResponse.accessToken; }; const hasValidRefreshToken = () => { diff --git a/src/front/screens/Report.js b/src/front/screens/Report.js index e06e3e86..b2f8ab2a 100644 --- a/src/front/screens/Report.js +++ b/src/front/screens/Report.js @@ -1,3 +1,4 @@ +import NetInfo from '@react-native-community/netinfo'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Datepicker, @@ -93,7 +94,7 @@ const Report = ({ const windowDimensions = useWindowDimensions(); const theme = useTheme(); const [showMain, setShowMain] = React.useState(false); - const { isConnected, cacheReport } = useOfflineCache(); + const { cacheReport } = useOfflineCache(); const { postReport } = useAPI(); const dateIcon = getIcon({ @@ -136,7 +137,8 @@ const Report = ({ return; } - if (!isConnected) { + // do an explicit NetInfo.fetch() here so that I get the most reliable connection status + if (!(await NetInfo.fetch()).isInternetReachable) { await cacheReport(submitValues); onClose(true); @@ -148,6 +150,7 @@ const Report = ({ try { responseJson = await postReport(submitValues, reportType); } catch (error) { + console.log('error posting report, caching offline', error); await cacheReport(submitValues, error); onClose(true); diff --git a/src/front/services/api.js b/src/front/services/api.js index 229ef0bb..c27ba481 100644 --- a/src/front/services/api.js +++ b/src/front/services/api.js @@ -22,9 +22,17 @@ export function useAPI() { const { getBearerToken } = useAuth(); async function makeRequest(method, route, data, isFormData = false) { + let token; + try { + token = await getBearerToken(); + } catch (error) { + Sentry.Native.captureException(error); + throw error; + } + const options = { headers: { - Authorization: await getBearerToken(), + Authorization: token, [commonConfig.versionHeaderName]: commonConfig.apiVersion, }, method, diff --git a/src/front/services/offline.js b/src/front/services/offline.js index 93850b09..bc5a1252 100644 --- a/src/front/services/offline.js +++ b/src/front/services/offline.js @@ -15,7 +15,7 @@ import * as Notifications from 'expo-notifications'; import lodash from 'lodash'; import prettyBytes from 'pretty-bytes'; import propTypes from 'prop-types'; -import React from 'react'; +import { createContext, useContext, useEffect, useRef, useState } from 'react'; import { Alert, Platform } from 'react-native'; import * as Sentry from 'sentry-expo'; import useAuth from '../auth/context'; @@ -48,12 +48,13 @@ export async function getBaseMapCacheSize() { return prettyBytes(size || 0); } + export async function clearBaseMapCache() { await deleteAsync(tileCacheDirectory); await ensureDirectory(tileCacheDirectory); } -const OfflineCacheContext = React.createContext(); +const OfflineCacheContext = createContext(); export async function getOfflineSubmission(id, pickupIndex) { try { @@ -88,11 +89,11 @@ async function deleteOfflineSubmission(id) { export function OfflineCacheContextProvider({ children }) { const { isInternetReachable } = useNetInfo(); - const [cachedSubmissionIds, setCachedSubmissionIds] = React.useState([]); + const [cachedSubmissionIds, setCachedSubmissionIds] = useState([]); const { postReport, postRoute } = useAPI(); const { authInfo } = useAuth(); - React.useEffect(() => { + useEffect(() => { const giddyUp = async () => { const folderNames = await readDirectoryAsync(offlineDataStorageDirectory); @@ -160,7 +161,9 @@ export function OfflineCacheContextProvider({ children }) { Alert.alert('Offline Submission Error', lastError); } - setCachedSubmissionIds(failedSubmissionIds); + if (failedSubmissionIds.length !== cachedSubmissionIds.length) { + setCachedSubmissionIds(failedSubmissionIds); + } return lastError; }; @@ -174,13 +177,17 @@ export function OfflineCacheContextProvider({ children }) { }, }); - React.useEffect(() => { + // prevent endless retries... + const lastTry = useRef(); + useEffect(() => { if ( + (!lastTry?.current || lastTry.current < Date.now() - 1000 * 60 * 1) && isInternetReachable && authInfo?.user && cachedSubmissionIds.length > 0 && !mutation.isPending ) { + lastTry.current = Date.now(); mutation.mutate(); } }, [ @@ -190,7 +197,7 @@ export function OfflineCacheContextProvider({ children }) { mutation, ]); - React.useEffect(() => { + useEffect(() => { const updateBadgeCount = async () => { let badgeNumber = 0; if (cachedSubmissionIds && cachedSubmissionIds.length > 0) { @@ -318,7 +325,7 @@ OfflineCacheContextProvider.propTypes = { }; export function useOfflineCache() { - const context = React.useContext(OfflineCacheContext); + const context = useContext(OfflineCacheContext); if (!context) { throw new Error( 'useOfflineCache must be used within a OfflineCacheContextProvider component',