Browse Source

Improve request error handling

Arnaud Vergnet 6 months ago
parent
commit
d55c692bd3

+ 36
- 7
src/components/Screens/RequestScreen.tsx View File

@@ -1,10 +1,17 @@
1 1
 import React, { useEffect, useRef } from 'react';
2 2
 import ErrorView from './ErrorView';
3 3
 import { useRequestLogic } from '../../utils/customHooks';
4
-import { useFocusEffect } from '@react-navigation/native';
4
+import {
5
+  useFocusEffect,
6
+  useNavigation,
7
+  useRoute,
8
+} from '@react-navigation/native';
5 9
 import BasicLoadingScreen from './BasicLoadingScreen';
6 10
 import i18n from 'i18n-js';
7 11
 import { API_REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
12
+import { StackNavigationProp } from '@react-navigation/stack';
13
+import { MainRoutes } from '../../navigation/MainNavigator';
14
+import ConnectionManager from '../../managers/ConnectionManager';
8 15
 
9 16
 export type RequestScreenProps<T> = {
10 17
   request: () => Promise<T>;
@@ -37,6 +44,8 @@ type Props<T> = RequestScreenProps<T>;
37 44
 const MIN_REFRESH_TIME = 5 * 1000;
38 45
 
39 46
 export default function RequestScreen<T>(props: Props<T>) {
47
+  const navigation = useNavigation<StackNavigationProp<any>>();
48
+  const route = useRoute();
40 49
   const refreshInterval = useRef<number>();
41 50
   const [
42 51
     loading,
@@ -89,22 +98,42 @@ export default function RequestScreen<T>(props: Props<T>) {
89 98
     }, [props.cache, props.refreshOnFocus])
90 99
   );
91 100
 
101
+  const isErrorCritical = (e: API_REQUEST_CODES | undefined) => {
102
+    return e === API_REQUEST_CODES.BAD_TOKEN;
103
+  };
104
+
105
+  useEffect(() => {
106
+    if (isErrorCritical(code)) {
107
+      ConnectionManager.getInstance()
108
+        .disconnect()
109
+        .then(() => {
110
+          navigation.replace(MainRoutes.Login, { nextScreen: route.name });
111
+        });
112
+    }
113
+  }, [code, navigation, route]);
114
+
92 115
   if (data === undefined && loading && props.showLoading !== false) {
93 116
     return <BasicLoadingScreen />;
94 117
   } else if (
95 118
     data === undefined &&
96
-    status !== REQUEST_STATUS.SUCCESS &&
119
+    (status !== REQUEST_STATUS.SUCCESS ||
120
+      (status === REQUEST_STATUS.SUCCESS && code !== undefined)) &&
97 121
     props.showError !== false
98 122
   ) {
99 123
     return (
100 124
       <ErrorView
101 125
         status={status}
126
+        code={code}
102 127
         loading={loading}
103
-        button={{
104
-          icon: 'refresh',
105
-          text: i18n.t('general.retry'),
106
-          onPress: () => refreshData(),
107
-        }}
128
+        button={
129
+          isErrorCritical(code)
130
+            ? undefined
131
+            : {
132
+                icon: 'refresh',
133
+                text: i18n.t('general.retry'),
134
+                onPress: () => refreshData(),
135
+              }
136
+        }
108 137
       />
109 138
     );
110 139
   } else {

+ 1
- 1
src/screens/Amicale/Clubs/ClubListScreen.tsx View File

@@ -156,7 +156,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
156 156
       this.categories = data?.categories;
157 157
       return [{ title: '', data: data.clubs }];
158 158
     } else {
159
-      return [{ title: '', data: [] }];
159
+      return [];
160 160
     }
161 161
   };
162 162
 

+ 1
- 1
src/screens/Amicale/Equipment/EquipmentListScreen.tsx View File

@@ -136,7 +136,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
136 136
       }
137 137
       return [{ title: '', data: data.devices }];
138 138
     } else {
139
-      return [{ title: '', data: [] }];
139
+      return [];
140 140
     }
141 141
   };
142 142
 

+ 3
- 4
src/screens/Amicale/LoginScreen.tsx View File

@@ -110,10 +110,9 @@ class LoginScreen extends React.Component<Props, StateType> {
110 110
       this.onInputChange(false, value);
111 111
     };
112 112
     props.navigation.addListener('focus', this.onScreenFocus);
113
-    // TODO remove
114 113
     this.state = {
115
-      email: 'vergnet@etud.insa-toulouse.fr',
116
-      password: 'IGtt25ùj',
114
+      email: '',
115
+      password: '',
117 116
       isEmailValidated: false,
118 117
       isPasswordValidated: false,
119 118
       loading: false,
@@ -373,7 +372,7 @@ class LoginScreen extends React.Component<Props, StateType> {
373 372
    * Saves the screen to navigate to after a successful login if one was provided in navigation parameters
374 373
    */
375 374
   handleNavigationParams() {
376
-    this.nextScreen = this.props.route.params.nextScreen;
375
+    this.nextScreen = this.props.route.params?.nextScreen;
377 376
   }
378 377
 
379 378
   /**

+ 34
- 35
src/screens/Planex/GroupSelectionScreen.tsx View File

@@ -121,12 +121,16 @@ function GroupSelectionScreen() {
121 121
         }
122 122
       | undefined
123 123
   ): Array<{ title: string; data: Array<PlanexGroupCategoryType> }> => {
124
-    return [
125
-      {
126
-        title: '',
127
-        data: generateData(fetchedData),
128
-      },
129
-    ];
124
+    if (fetchedData) {
125
+      return [
126
+        {
127
+          title: '',
128
+          data: generateData(fetchedData),
129
+        },
130
+      ];
131
+    } else {
132
+      return [];
133
+    }
130 134
   };
131 135
 
132 136
   /**
@@ -181,39 +185,34 @@ function GroupSelectionScreen() {
181 185
    * @returns {[]}
182 186
    */
183 187
   const generateData = (
184
-    fetchedData: PlanexGroupsType | undefined
188
+    fetchedData: PlanexGroupsType
185 189
   ): Array<PlanexGroupCategoryType> => {
186 190
     const data: Array<PlanexGroupCategoryType> = [];
187
-
188
-    if (fetchedData) {
189
-      // Convert the object into an array
190
-      Object.values(fetchedData).forEach(
191
-        (category: PlanexGroupCategoryType) => {
192
-          const content: Array<PlanexGroupType> = [];
193
-          // Filter groups matching the search query
194
-          category.content.forEach((g: PlanexGroupType) => {
195
-            if (stringMatchQuery(g.name, currentSearchString)) {
196
-              content.push(g);
197
-            }
198
-          });
199
-          // Only add categories with groups matching the query
200
-          if (content.length > 0) {
201
-            data.push({
202
-              id: category.id,
203
-              name: category.name,
204
-              content: content,
205
-            });
206
-          }
191
+    // Convert the object into an array
192
+    Object.values(fetchedData).forEach((category: PlanexGroupCategoryType) => {
193
+      const content: Array<PlanexGroupType> = [];
194
+      // Filter groups matching the search query
195
+      category.content.forEach((g: PlanexGroupType) => {
196
+        if (stringMatchQuery(g.name, currentSearchString)) {
197
+          content.push(g);
207 198
         }
208
-      );
209
-      data.sort(sortName);
210
-      // Add the favorites at the top
211
-      data.unshift({
212
-        name: i18n.t('screens.planex.favorites.title'),
213
-        id: 0,
214
-        content: favoriteGroups,
215 199
       });
216
-    }
200
+      // Only add categories with groups matching the query
201
+      if (content.length > 0) {
202
+        data.push({
203
+          id: category.id,
204
+          name: category.name,
205
+          content: content,
206
+        });
207
+      }
208
+    });
209
+    data.sort(sortName);
210
+    // Add the favorites at the top
211
+    data.unshift({
212
+      name: i18n.t('screens.planex.favorites.title'),
213
+      id: 0,
214
+      content: favoriteGroups,
215
+    });
217 216
     return data;
218 217
   };
219 218
 

+ 1
- 7
src/screens/Services/Proximo/ProximoListScreen.tsx View File

@@ -349,13 +349,7 @@ function ProximoListScreen(props: Props) {
349 349
         },
350 350
       ];
351 351
     } else {
352
-      return [
353
-        {
354
-          title: '',
355
-          data: [],
356
-          keyExtractor: keyExtractor,
357
-        },
358
-      ];
352
+      return [];
359 353
     }
360 354
   };
361 355
 

+ 1
- 7
src/screens/Services/Proximo/ProximoMainScreen.tsx View File

@@ -239,13 +239,7 @@ function ProximoMainScreen() {
239 239
         },
240 240
       ];
241 241
     } else {
242
-      return [
243
-        {
244
-          title: '',
245
-          data: [],
246
-          keyExtractor: getKeyExtractor,
247
-        },
248
-      ];
242
+      return [];
249 243
     }
250 244
   };
251 245
 

+ 23
- 16
src/screens/Services/SelfMenuScreen.tsx View File

@@ -27,6 +27,7 @@ import WebSectionList from '../../components/Screens/WebSectionList';
27 27
 import type { SectionListDataType } from '../../components/Screens/WebSectionList';
28 28
 import Urls from '../../constants/Urls';
29 29
 import { readData } from '../../utils/WebData';
30
+import { REQUEST_STATUS } from '../../utils/Requests';
30 31
 
31 32
 type PropsType = {
32 33
   navigation: StackNavigationProp<any>;
@@ -109,25 +110,31 @@ class SelfMenuScreen extends React.Component<PropsType> {
109 110
    * @return {[]}
110 111
    */
111 112
   createDataset = (
112
-    fetchedData: Array<RawRuMenuType> | undefined
113
+    fetchedData: Array<RawRuMenuType> | undefined,
114
+    _loading: boolean,
115
+    _lastRefreshDate: Date | undefined,
116
+    _refreshData: (newRequest?: () => Promise<Array<RawRuMenuType>>) => void,
117
+    status: REQUEST_STATUS
113 118
   ): SectionListDataType<RuFoodCategoryType> => {
114 119
     let result: SectionListDataType<RuFoodCategoryType> = [];
115
-    if (fetchedData == null || fetchedData.length === 0) {
116
-      result = [
117
-        {
118
-          title: i18n.t('general.notAvailable'),
119
-          data: [],
120
-          keyExtractor: this.getKeyExtractor,
121
-        },
122
-      ];
123
-    } else {
124
-      fetchedData.forEach((item: RawRuMenuType) => {
125
-        result.push({
126
-          title: DateManager.getInstance().getTranslatedDate(item.date),
127
-          data: item.meal[0].foodcategory,
128
-          keyExtractor: this.getKeyExtractor,
120
+    if (status === REQUEST_STATUS.SUCCESS) {
121
+      if (fetchedData == null || fetchedData.length === 0) {
122
+        result = [
123
+          {
124
+            title: i18n.t('general.notAvailable'),
125
+            data: [],
126
+            keyExtractor: this.getKeyExtractor,
127
+          },
128
+        ];
129
+      } else {
130
+        fetchedData.forEach((item: RawRuMenuType) => {
131
+          result.push({
132
+            title: DateManager.getInstance().getTranslatedDate(item.date),
133
+            data: item.meal[0].foodcategory,
134
+            keyExtractor: this.getKeyExtractor,
135
+          });
129 136
         });
130
-      });
137
+      }
131 138
     }
132 139
     return result;
133 140
   };

Loading…
Cancel
Save