Skip to content

Commit

Permalink
fix(front): offline caching and error handling
Browse files Browse the repository at this point in the history
Hopefully helps with #287 and #288
  • Loading branch information
stdavis committed Nov 3, 2023
1 parent b6c2059 commit f6a59b8
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 30 deletions.
8 changes: 2 additions & 6 deletions src/front/auth/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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 (
Expand Down
24 changes: 11 additions & 13 deletions src/front/auth/providers/utahid.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ export default function useUtahIDProvider() {
accessToken.current &&
!isTokenExpired(jwt_decode(accessToken.current))
) {
console.log('returning cached token');

return prefix + accessToken.current;
}

Expand Down Expand Up @@ -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 = () => {
Expand Down
7 changes: 5 additions & 2 deletions src/front/screens/Report.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import NetInfo from '@react-native-community/netinfo';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import {
Datepicker,
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion src/front/services/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 15 additions & 8 deletions src/front/services/offline.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
};
Expand All @@ -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();
}
}, [
Expand All @@ -190,7 +197,7 @@ export function OfflineCacheContextProvider({ children }) {
mutation,
]);

React.useEffect(() => {
useEffect(() => {
const updateBadgeCount = async () => {
let badgeNumber = 0;
if (cachedSubmissionIds && cachedSubmissionIds.length > 0) {
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit f6a59b8

Please sign in to comment.