From d55c692bd383b24bb11475720bbf33078c5550e9 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet <arnaud.vergnet@mailo.com> Date: Thu, 13 May 2021 19:10:28 +0200 Subject: [PATCH] Improve request error handling --- src/components/Screens/RequestScreen.tsx | 43 ++++++++++-- src/screens/Amicale/Clubs/ClubListScreen.tsx | 2 +- .../Amicale/Equipment/EquipmentListScreen.tsx | 2 +- src/screens/Amicale/LoginScreen.tsx | 7 +- src/screens/Planex/GroupSelectionScreen.tsx | 69 +++++++++---------- .../Services/Proximo/ProximoListScreen.tsx | 8 +-- .../Services/Proximo/ProximoMainScreen.tsx | 8 +-- src/screens/Services/SelfMenuScreen.tsx | 39 ++++++----- 8 files changed, 100 insertions(+), 78 deletions(-) diff --git a/src/components/Screens/RequestScreen.tsx b/src/components/Screens/RequestScreen.tsx index 146ae19..5c0055d 100644 --- a/src/components/Screens/RequestScreen.tsx +++ b/src/components/Screens/RequestScreen.tsx @@ -1,10 +1,17 @@ import React, { useEffect, useRef } from 'react'; import ErrorView from './ErrorView'; import { useRequestLogic } from '../../utils/customHooks'; -import { useFocusEffect } from '@react-navigation/native'; +import { + useFocusEffect, + useNavigation, + useRoute, +} from '@react-navigation/native'; import BasicLoadingScreen from './BasicLoadingScreen'; import i18n from 'i18n-js'; import { API_REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests'; +import { StackNavigationProp } from '@react-navigation/stack'; +import { MainRoutes } from '../../navigation/MainNavigator'; +import ConnectionManager from '../../managers/ConnectionManager'; export type RequestScreenProps<T> = { request: () => Promise<T>; @@ -37,6 +44,8 @@ type Props<T> = RequestScreenProps<T>; const MIN_REFRESH_TIME = 5 * 1000; export default function RequestScreen<T>(props: Props<T>) { + const navigation = useNavigation<StackNavigationProp<any>>(); + const route = useRoute(); const refreshInterval = useRef<number>(); const [ loading, @@ -89,22 +98,42 @@ export default function RequestScreen<T>(props: Props<T>) { }, [props.cache, props.refreshOnFocus]) ); + const isErrorCritical = (e: API_REQUEST_CODES | undefined) => { + return e === API_REQUEST_CODES.BAD_TOKEN; + }; + + useEffect(() => { + if (isErrorCritical(code)) { + ConnectionManager.getInstance() + .disconnect() + .then(() => { + navigation.replace(MainRoutes.Login, { nextScreen: route.name }); + }); + } + }, [code, navigation, route]); + if (data === undefined && loading && props.showLoading !== false) { return <BasicLoadingScreen />; } else if ( data === undefined && - status !== REQUEST_STATUS.SUCCESS && + (status !== REQUEST_STATUS.SUCCESS || + (status === REQUEST_STATUS.SUCCESS && code !== undefined)) && props.showError !== false ) { return ( <ErrorView status={status} + code={code} loading={loading} - button={{ - icon: 'refresh', - text: i18n.t('general.retry'), - onPress: () => refreshData(), - }} + button={ + isErrorCritical(code) + ? undefined + : { + icon: 'refresh', + text: i18n.t('general.retry'), + onPress: () => refreshData(), + } + } /> ); } else { diff --git a/src/screens/Amicale/Clubs/ClubListScreen.tsx b/src/screens/Amicale/Clubs/ClubListScreen.tsx index 7bf8a76..c86ca82 100644 --- a/src/screens/Amicale/Clubs/ClubListScreen.tsx +++ b/src/screens/Amicale/Clubs/ClubListScreen.tsx @@ -156,7 +156,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> { this.categories = data?.categories; return [{ title: '', data: data.clubs }]; } else { - return [{ title: '', data: [] }]; + return []; } }; diff --git a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx index 7c51e7a..99ba202 100644 --- a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx +++ b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx @@ -136,7 +136,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> { } return [{ title: '', data: data.devices }]; } else { - return [{ title: '', data: [] }]; + return []; } }; diff --git a/src/screens/Amicale/LoginScreen.tsx b/src/screens/Amicale/LoginScreen.tsx index a6f513d..aec0509 100644 --- a/src/screens/Amicale/LoginScreen.tsx +++ b/src/screens/Amicale/LoginScreen.tsx @@ -110,10 +110,9 @@ class LoginScreen extends React.Component<Props, StateType> { this.onInputChange(false, value); }; props.navigation.addListener('focus', this.onScreenFocus); - // TODO remove this.state = { - email: 'vergnet@etud.insa-toulouse.fr', - password: 'IGtt25ùj', + email: '', + password: '', isEmailValidated: false, isPasswordValidated: false, loading: false, @@ -373,7 +372,7 @@ class LoginScreen extends React.Component<Props, StateType> { * Saves the screen to navigate to after a successful login if one was provided in navigation parameters */ handleNavigationParams() { - this.nextScreen = this.props.route.params.nextScreen; + this.nextScreen = this.props.route.params?.nextScreen; } /** diff --git a/src/screens/Planex/GroupSelectionScreen.tsx b/src/screens/Planex/GroupSelectionScreen.tsx index c4b70d1..0176bc6 100644 --- a/src/screens/Planex/GroupSelectionScreen.tsx +++ b/src/screens/Planex/GroupSelectionScreen.tsx @@ -121,12 +121,16 @@ function GroupSelectionScreen() { } | undefined ): Array<{ title: string; data: Array<PlanexGroupCategoryType> }> => { - return [ - { - title: '', - data: generateData(fetchedData), - }, - ]; + if (fetchedData) { + return [ + { + title: '', + data: generateData(fetchedData), + }, + ]; + } else { + return []; + } }; /** @@ -181,39 +185,34 @@ function GroupSelectionScreen() { * @returns {[]} */ const generateData = ( - fetchedData: PlanexGroupsType | undefined + fetchedData: PlanexGroupsType ): Array<PlanexGroupCategoryType> => { const data: Array<PlanexGroupCategoryType> = []; - - if (fetchedData) { - // Convert the object into an array - Object.values(fetchedData).forEach( - (category: PlanexGroupCategoryType) => { - const content: Array<PlanexGroupType> = []; - // Filter groups matching the search query - category.content.forEach((g: PlanexGroupType) => { - if (stringMatchQuery(g.name, currentSearchString)) { - content.push(g); - } - }); - // Only add categories with groups matching the query - if (content.length > 0) { - data.push({ - id: category.id, - name: category.name, - content: content, - }); - } + // Convert the object into an array + Object.values(fetchedData).forEach((category: PlanexGroupCategoryType) => { + const content: Array<PlanexGroupType> = []; + // Filter groups matching the search query + category.content.forEach((g: PlanexGroupType) => { + if (stringMatchQuery(g.name, currentSearchString)) { + content.push(g); } - ); - data.sort(sortName); - // Add the favorites at the top - data.unshift({ - name: i18n.t('screens.planex.favorites.title'), - id: 0, - content: favoriteGroups, }); - } + // Only add categories with groups matching the query + if (content.length > 0) { + data.push({ + id: category.id, + name: category.name, + content: content, + }); + } + }); + data.sort(sortName); + // Add the favorites at the top + data.unshift({ + name: i18n.t('screens.planex.favorites.title'), + id: 0, + content: favoriteGroups, + }); return data; }; diff --git a/src/screens/Services/Proximo/ProximoListScreen.tsx b/src/screens/Services/Proximo/ProximoListScreen.tsx index cdd4f90..367473c 100644 --- a/src/screens/Services/Proximo/ProximoListScreen.tsx +++ b/src/screens/Services/Proximo/ProximoListScreen.tsx @@ -349,13 +349,7 @@ function ProximoListScreen(props: Props) { }, ]; } else { - return [ - { - title: '', - data: [], - keyExtractor: keyExtractor, - }, - ]; + return []; } }; diff --git a/src/screens/Services/Proximo/ProximoMainScreen.tsx b/src/screens/Services/Proximo/ProximoMainScreen.tsx index b5aaca9..a770e75 100644 --- a/src/screens/Services/Proximo/ProximoMainScreen.tsx +++ b/src/screens/Services/Proximo/ProximoMainScreen.tsx @@ -239,13 +239,7 @@ function ProximoMainScreen() { }, ]; } else { - return [ - { - title: '', - data: [], - keyExtractor: getKeyExtractor, - }, - ]; + return []; } }; diff --git a/src/screens/Services/SelfMenuScreen.tsx b/src/screens/Services/SelfMenuScreen.tsx index 763aded..8a883aa 100644 --- a/src/screens/Services/SelfMenuScreen.tsx +++ b/src/screens/Services/SelfMenuScreen.tsx @@ -27,6 +27,7 @@ import WebSectionList from '../../components/Screens/WebSectionList'; import type { SectionListDataType } from '../../components/Screens/WebSectionList'; import Urls from '../../constants/Urls'; import { readData } from '../../utils/WebData'; +import { REQUEST_STATUS } from '../../utils/Requests'; type PropsType = { navigation: StackNavigationProp<any>; @@ -109,25 +110,31 @@ class SelfMenuScreen extends React.Component<PropsType> { * @return {[]} */ createDataset = ( - fetchedData: Array<RawRuMenuType> | undefined + fetchedData: Array<RawRuMenuType> | undefined, + _loading: boolean, + _lastRefreshDate: Date | undefined, + _refreshData: (newRequest?: () => Promise<Array<RawRuMenuType>>) => void, + status: REQUEST_STATUS ): SectionListDataType<RuFoodCategoryType> => { let result: SectionListDataType<RuFoodCategoryType> = []; - if (fetchedData == null || fetchedData.length === 0) { - result = [ - { - title: i18n.t('general.notAvailable'), - data: [], - keyExtractor: this.getKeyExtractor, - }, - ]; - } else { - fetchedData.forEach((item: RawRuMenuType) => { - result.push({ - title: DateManager.getInstance().getTranslatedDate(item.date), - data: item.meal[0].foodcategory, - keyExtractor: this.getKeyExtractor, + if (status === REQUEST_STATUS.SUCCESS) { + if (fetchedData == null || fetchedData.length === 0) { + result = [ + { + title: i18n.t('general.notAvailable'), + data: [], + keyExtractor: this.getKeyExtractor, + }, + ]; + } else { + fetchedData.forEach((item: RawRuMenuType) => { + result.push({ + title: DateManager.getInstance().getTranslatedDate(item.date), + data: item.meal[0].foodcategory, + keyExtractor: this.getKeyExtractor, + }); }); - }); + } } return result; };