Browse Source

Update api error codes

Arnaud Vergnet 6 months ago
parent
commit
9b4caade00

+ 8
- 5
src/components/Amicale/Vote/VoteSelect.tsx View File

@@ -25,6 +25,8 @@ import ConnectionManager from '../../../managers/ConnectionManager';
25 25
 import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog';
26 26
 import ErrorDialog from '../../Dialogs/ErrorDialog';
27 27
 import type { VoteTeamType } from '../../../screens/Amicale/VoteScreen';
28
+import { ApiRejectType } from '../../../utils/WebData';
29
+import { REQUEST_STATUS } from '../../../utils/Requests';
28 30
 
29 31
 type PropsType = {
30 32
   teams: Array<VoteTeamType>;
@@ -36,7 +38,7 @@ type StateType = {
36 38
   selectedTeam: string;
37 39
   voteDialogVisible: boolean;
38 40
   errorDialogVisible: boolean;
39
-  currentError: number;
41
+  currentError: ApiRejectType;
40 42
 };
41 43
 
42 44
 const styles = StyleSheet.create({
@@ -58,7 +60,7 @@ export default class VoteSelect extends React.PureComponent<
58 60
       selectedTeam: 'none',
59 61
       voteDialogVisible: false,
60 62
       errorDialogVisible: false,
61
-      currentError: 0,
63
+      currentError: { status: REQUEST_STATUS.SUCCESS },
62 64
     };
63 65
   }
64 66
 
@@ -88,7 +90,7 @@ export default class VoteSelect extends React.PureComponent<
88 90
           props.onVoteSuccess();
89 91
           resolve();
90 92
         })
91
-        .catch((error: number) => {
93
+        .catch((error: ApiRejectType) => {
92 94
           this.onVoteDialogDismiss();
93 95
           this.showErrorDialog(error);
94 96
           resolve();
@@ -96,7 +98,7 @@ export default class VoteSelect extends React.PureComponent<
96 98
     });
97 99
   };
98 100
 
99
-  showErrorDialog = (error: number): void =>
101
+  showErrorDialog = (error: ApiRejectType): void =>
100 102
     this.setState({
101 103
       errorDialogVisible: true,
102 104
       currentError: error,
@@ -156,7 +158,8 @@ export default class VoteSelect extends React.PureComponent<
156 158
         <ErrorDialog
157 159
           visible={state.errorDialogVisible}
158 160
           onDismiss={this.onErrorDialogDismiss}
159
-          errorCode={state.currentError}
161
+          status={state.currentError.status}
162
+          code={state.currentError.code}
160 163
         />
161 164
       </View>
162 165
     );

+ 9
- 42
src/components/Dialogs/ErrorDialog.tsx View File

@@ -19,60 +19,27 @@
19 19
 
20 20
 import * as React from 'react';
21 21
 import i18n from 'i18n-js';
22
-import { ERROR_TYPE } from '../../utils/WebData';
23 22
 import AlertDialog from './AlertDialog';
23
+import {
24
+  API_REQUEST_CODES,
25
+  getErrorMessage,
26
+  REQUEST_STATUS,
27
+} from '../../utils/Requests';
24 28
 
25 29
 type PropsType = {
26 30
   visible: boolean;
27 31
   onDismiss: () => void;
28
-  errorCode: number;
32
+  status?: REQUEST_STATUS;
33
+  code?: API_REQUEST_CODES;
29 34
 };
30 35
 
31 36
 function ErrorDialog(props: PropsType) {
32
-  let title: string;
33
-  let message: string;
34
-
35
-  title = i18n.t('errors.title');
36
-  switch (props.errorCode) {
37
-    case ERROR_TYPE.BAD_CREDENTIALS:
38
-      message = i18n.t('errors.badCredentials');
39
-      break;
40
-    case ERROR_TYPE.BAD_TOKEN:
41
-      message = i18n.t('errors.badToken');
42
-      break;
43
-    case ERROR_TYPE.NO_CONSENT:
44
-      message = i18n.t('errors.noConsent');
45
-      break;
46
-    case ERROR_TYPE.TOKEN_SAVE:
47
-      message = i18n.t('errors.tokenSave');
48
-      break;
49
-    case ERROR_TYPE.TOKEN_RETRIEVE:
50
-      message = i18n.t('errors.unknown');
51
-      break;
52
-    case ERROR_TYPE.BAD_INPUT:
53
-      message = i18n.t('errors.badInput');
54
-      break;
55
-    case ERROR_TYPE.FORBIDDEN:
56
-      message = i18n.t('errors.forbidden');
57
-      break;
58
-    case ERROR_TYPE.CONNECTION_ERROR:
59
-      message = i18n.t('errors.connectionError');
60
-      break;
61
-    case ERROR_TYPE.SERVER_ERROR:
62
-      message = i18n.t('errors.serverError');
63
-      break;
64
-    default:
65
-      message = i18n.t('errors.unknown');
66
-      break;
67
-  }
68
-  message += `\n\nCode ${props.errorCode}`;
69
-
70 37
   return (
71 38
     <AlertDialog
72 39
       visible={props.visible}
73 40
       onDismiss={props.onDismiss}
74
-      title={title}
75
-      message={message}
41
+      title={i18n.t('errors.title')}
42
+      message={getErrorMessage(props)}
76 43
     />
77 44
   );
78 45
 }

+ 7
- 87
src/components/Screens/ErrorView.tsx View File

@@ -21,13 +21,16 @@ import * as React from 'react';
21 21
 import { Button, Subheading, useTheme } from 'react-native-paper';
22 22
 import { StyleSheet, View, ViewStyle } from 'react-native';
23 23
 import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
24
-import i18n from 'i18n-js';
25 24
 import * as Animatable from 'react-native-animatable';
26
-import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
25
+import {
26
+  API_REQUEST_CODES,
27
+  getErrorMessage,
28
+  REQUEST_STATUS,
29
+} from '../../utils/Requests';
27 30
 
28 31
 type Props = {
29 32
   status?: REQUEST_STATUS;
30
-  code?: REQUEST_CODES;
33
+  code?: API_REQUEST_CODES;
31 34
   icon?: string;
32 35
   message?: string;
33 36
   loading?: boolean;
@@ -63,92 +66,9 @@ const styles = StyleSheet.create({
63 66
   },
64 67
 });
65 68
 
66
-function getMessage(props: Props) {
67
-  let fullMessage = {
68
-    message: '',
69
-    icon: '',
70
-  };
71
-  if (props.code === undefined) {
72
-    switch (props.status) {
73
-      case REQUEST_STATUS.BAD_INPUT:
74
-        fullMessage.message = i18n.t('errors.badInput');
75
-        fullMessage.icon = 'alert-circle-outline';
76
-        break;
77
-      case REQUEST_STATUS.FORBIDDEN:
78
-        fullMessage.message = i18n.t('errors.forbidden');
79
-        fullMessage.icon = 'lock';
80
-        break;
81
-      case REQUEST_STATUS.CONNECTION_ERROR:
82
-        fullMessage.message = i18n.t('errors.connectionError');
83
-        fullMessage.icon = 'access-point-network-off';
84
-        break;
85
-      case REQUEST_STATUS.SERVER_ERROR:
86
-        fullMessage.message = i18n.t('errors.serverError');
87
-        fullMessage.icon = 'server-network-off';
88
-        break;
89
-      default:
90
-        fullMessage.message = i18n.t('errors.unknown');
91
-        fullMessage.icon = 'alert-circle-outline';
92
-        break;
93
-    }
94
-  } else {
95
-    switch (props.code) {
96
-      case REQUEST_CODES.BAD_CREDENTIALS:
97
-        fullMessage.message = i18n.t('errors.badCredentials');
98
-        fullMessage.icon = 'account-alert-outline';
99
-        break;
100
-      case REQUEST_CODES.BAD_TOKEN:
101
-        fullMessage.message = i18n.t('errors.badToken');
102
-        fullMessage.icon = 'account-alert-outline';
103
-        break;
104
-      case REQUEST_CODES.NO_CONSENT:
105
-        fullMessage.message = i18n.t('errors.noConsent');
106
-        fullMessage.icon = 'account-remove-outline';
107
-        break;
108
-      case REQUEST_CODES.TOKEN_SAVE:
109
-        fullMessage.message = i18n.t('errors.tokenSave');
110
-        fullMessage.icon = 'alert-circle-outline';
111
-        break;
112
-      case REQUEST_CODES.BAD_INPUT:
113
-        fullMessage.message = i18n.t('errors.badInput');
114
-        fullMessage.icon = 'alert-circle-outline';
115
-        break;
116
-      case REQUEST_CODES.FORBIDDEN:
117
-        fullMessage.message = i18n.t('errors.forbidden');
118
-        fullMessage.icon = 'lock';
119
-        break;
120
-      case REQUEST_CODES.CONNECTION_ERROR:
121
-        fullMessage.message = i18n.t('errors.connectionError');
122
-        fullMessage.icon = 'access-point-network-off';
123
-        break;
124
-      case REQUEST_CODES.SERVER_ERROR:
125
-        fullMessage.message = i18n.t('errors.serverError');
126
-        fullMessage.icon = 'server-network-off';
127
-        break;
128
-      default:
129
-        fullMessage.message = i18n.t('errors.unknown');
130
-        fullMessage.icon = 'alert-circle-outline';
131
-        break;
132
-    }
133
-  }
134
-
135
-  if (props.code !== undefined) {
136
-    fullMessage.message += `\n\nCode {${props.status}:${props.code}}`;
137
-  } else {
138
-    fullMessage.message += `\n\nCode {${props.status}}`;
139
-  }
140
-  if (props.message != null) {
141
-    fullMessage.message = props.message;
142
-  }
143
-  if (props.icon != null) {
144
-    fullMessage.icon = props.icon;
145
-  }
146
-  return fullMessage;
147
-}
148
-
149 69
 function ErrorView(props: Props) {
150 70
   const theme = useTheme();
151
-  const fullMessage = getMessage(props);
71
+  const fullMessage = getErrorMessage(props, props.message, props.icon);
152 72
   const { button } = props;
153 73
 
154 74
   return (

+ 2
- 2
src/components/Screens/RequestScreen.tsx View File

@@ -4,7 +4,7 @@ import { useRequestLogic } from '../../utils/customHooks';
4 4
 import { useFocusEffect } from '@react-navigation/native';
5 5
 import BasicLoadingScreen from './BasicLoadingScreen';
6 6
 import i18n from 'i18n-js';
7
-import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
7
+import { API_REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
8 8
 
9 9
 export type RequestScreenProps<T> = {
10 10
   request: () => Promise<T>;
@@ -14,7 +14,7 @@ export type RequestScreenProps<T> = {
14 14
     lastRefreshDate: Date | undefined,
15 15
     refreshData: (newRequest?: () => Promise<T>) => void,
16 16
     status: REQUEST_STATUS,
17
-    code?: REQUEST_CODES
17
+    code?: API_REQUEST_CODES
18 18
   ) => React.ReactElement;
19 19
   cache?: T;
20 20
   onCacheUpdate?: (newCache: T) => void;

+ 4
- 4
src/components/Screens/WebSectionList.tsx View File

@@ -29,7 +29,7 @@ import ErrorView from './ErrorView';
29 29
 import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
30 30
 import RequestScreen, { RequestScreenProps } from './RequestScreen';
31 31
 import { CollapsibleComponentPropsType } from '../Collapsible/CollapsibleComponent';
32
-import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
32
+import { API_REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
33 33
 
34 34
 export type SectionListDataType<ItemT> = Array<{
35 35
   title: string;
@@ -61,7 +61,7 @@ type Props<ItemT, RawData> = Omit<
61 61
       lastRefreshDate: Date | undefined,
62 62
       refreshData: (newRequest?: () => Promise<RawData>) => void,
63 63
       status: REQUEST_STATUS,
64
-      code?: REQUEST_CODES
64
+      code?: API_REQUEST_CODES
65 65
     ) => SectionListDataType<ItemT>;
66 66
     renderListHeaderComponent?: (
67 67
       data: RawData | undefined,
@@ -69,7 +69,7 @@ type Props<ItemT, RawData> = Omit<
69 69
       lastRefreshDate: Date | undefined,
70 70
       refreshData: (newRequest?: () => Promise<RawData>) => void,
71 71
       status: REQUEST_STATUS,
72
-      code?: REQUEST_CODES
72
+      code?: API_REQUEST_CODES
73 73
     ) => React.ComponentType<any> | React.ReactElement | null;
74 74
     itemHeight?: number | null;
75 75
   };
@@ -103,7 +103,7 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
103 103
     lastRefreshDate: Date | undefined,
104 104
     refreshData: (newRequest?: () => Promise<RawData>) => void,
105 105
     status: REQUEST_STATUS,
106
-    code?: REQUEST_CODES
106
+    code?: API_REQUEST_CODES
107 107
   ) => {
108 108
     const { itemHeight } = props;
109 109
     const dataset = props.createDataset(

+ 2
- 2
src/components/Screens/WebViewScreen.tsx View File

@@ -43,11 +43,11 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
43 43
 import { useTheme } from 'react-native-paper';
44 44
 import { useCollapsibleHeader } from 'react-navigation-collapsible';
45 45
 import MaterialHeaderButtons, { Item } from '../Overrides/CustomHeaderButton';
46
-import { ERROR_TYPE } from '../../utils/WebData';
47 46
 import ErrorView from './ErrorView';
48 47
 import BasicLoadingScreen from './BasicLoadingScreen';
49 48
 import { useFocusEffect, useNavigation } from '@react-navigation/core';
50 49
 import { useCollapsible } from '../../utils/CollapsibleContext';
50
+import { REQUEST_STATUS } from '../../utils/Requests';
51 51
 
52 52
 type Props = {
53 53
   url: string;
@@ -259,7 +259,7 @@ function WebViewScreen(props: Props) {
259 259
       renderLoading={getRenderLoading}
260 260
       renderError={() => (
261 261
         <ErrorView
262
-          status={ERROR_TYPE.CONNECTION_ERROR}
262
+          status={REQUEST_STATUS.CONNECTION_ERROR}
263 263
           button={{
264 264
             icon: 'refresh',
265 265
             text: i18n.t('general.retry'),

+ 23
- 11
src/managers/ConnectionManager.ts View File

@@ -18,8 +18,9 @@
18 18
  */
19 19
 
20 20
 import * as Keychain from 'react-native-keychain';
21
-import type { ApiDataLoginType } from '../utils/WebData';
22
-import { apiRequest, ERROR_TYPE } from '../utils/WebData';
21
+import { REQUEST_STATUS } from '../utils/Requests';
22
+import type { ApiDataLoginType, ApiRejectType } from '../utils/WebData';
23
+import { apiRequest } from '../utils/WebData';
23 24
 
24 25
 /**
25 26
  * champ: error
@@ -141,7 +142,7 @@ export default class ConnectionManager {
141 142
    */
142 143
   async connect(email: string, password: string): Promise<void> {
143 144
     return new Promise(
144
-      (resolve: () => void, reject: (error: number) => void) => {
145
+      (resolve: () => void, reject: (error: ApiRejectType) => void) => {
145 146
         const data = {
146 147
           email,
147 148
           password,
@@ -150,13 +151,19 @@ export default class ConnectionManager {
150 151
           .then((response: ApiDataLoginType) => {
151 152
             if (response.token != null) {
152 153
               this.saveLogin(email, response.token)
153
-                .then((): void => resolve())
154
-                .catch((): void => reject(ERROR_TYPE.TOKEN_SAVE));
154
+                .then(() => resolve())
155
+                .catch(() =>
156
+                  reject({
157
+                    status: REQUEST_STATUS.TOKEN_SAVE,
158
+                  })
159
+                );
155 160
             } else {
156
-              reject(ERROR_TYPE.SERVER_ERROR);
161
+              reject({
162
+                status: REQUEST_STATUS.SERVER_ERROR,
163
+              });
157 164
             }
158 165
           })
159
-          .catch((error: number): void => reject(error));
166
+          .catch(reject);
160 167
       }
161 168
     );
162 169
   }
@@ -173,17 +180,22 @@ export default class ConnectionManager {
173 180
     params: { [key: string]: any }
174 181
   ): Promise<T> {
175 182
     return new Promise(
176
-      (resolve: (response: T) => void, reject: (error: number) => void) => {
183
+      (
184
+        resolve: (response: T) => void,
185
+        reject: (error: ApiRejectType) => void
186
+      ) => {
177 187
         if (this.getToken() !== null) {
178 188
           const data = {
179 189
             ...params,
180 190
             token: this.getToken(),
181 191
           };
182 192
           apiRequest<T>(path, 'POST', data)
183
-            .then((response: T): void => resolve(response))
184
-            .catch((error: number): void => reject(error));
193
+            .then((response: T) => resolve(response))
194
+            .catch(reject);
185 195
         } else {
186
-          reject(ERROR_TYPE.TOKEN_RETRIEVE);
196
+          reject({
197
+            status: REQUEST_STATUS.TOKEN_RETRIEVE,
198
+          });
187 199
         }
188 200
       }
189 201
     );

+ 8
- 5
src/screens/Amicale/Equipment/EquipmentRentScreen.tsx View File

@@ -46,6 +46,8 @@ import ConnectionManager from '../../../managers/ConnectionManager';
46 46
 import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
47 47
 import { MainStackParamsList } from '../../../navigation/MainNavigator';
48 48
 import GENERAL_STYLES from '../../../constants/Styles';
49
+import { ApiRejectType } from '../../../utils/WebData';
50
+import { REQUEST_STATUS } from '../../../utils/Requests';
49 51
 
50 52
 type EquipmentRentScreenNavigationProp = StackScreenProps<
51 53
   MainStackParamsList,
@@ -65,7 +67,7 @@ type StateType = {
65 67
   dialogVisible: boolean;
66 68
   errorDialogVisible: boolean;
67 69
   markedDates: MarkedDatesObjectType;
68
-  currentError: number;
70
+  currentError: ApiRejectType;
69 71
 };
70 72
 
71 73
 const styles = StyleSheet.create({
@@ -133,7 +135,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
133 135
       dialogVisible: false,
134 136
       errorDialogVisible: false,
135 137
       markedDates: {},
136
-      currentError: 0,
138
+      currentError: { status: REQUEST_STATUS.SUCCESS },
137 139
     };
138 140
     this.resetSelection();
139 141
     this.bookRef = React.createRef();
@@ -231,7 +233,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
231 233
             });
232 234
             resolve();
233 235
           })
234
-          .catch((error: number) => {
236
+          .catch((error: ApiRejectType) => {
235 237
             this.onDialogDismiss();
236 238
             this.showErrorDialog(error);
237 239
             resolve();
@@ -284,7 +286,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
284 286
     }
285 287
   };
286 288
 
287
-  showErrorDialog = (error: number) => {
289
+  showErrorDialog = (error: ApiRejectType) => {
288 290
     this.setState({
289 291
       errorDialogVisible: true,
290 292
       currentError: error,
@@ -460,7 +462,8 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
460 462
           <ErrorDialog
461 463
             visible={state.errorDialogVisible}
462 464
             onDismiss={this.onErrorDialogDismiss}
463
-            errorCode={state.currentError}
465
+            status={state.currentError.status}
466
+            code={state.currentError.code}
464 467
           />
465 468
           <Animatable.View
466 469
             ref={this.bookRef}

+ 10
- 8
src/screens/Amicale/LoginScreen.tsx View File

@@ -38,6 +38,8 @@ import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrol
38 38
 import { MainStackParamsList } from '../../navigation/MainNavigator';
39 39
 import GENERAL_STYLES from '../../constants/Styles';
40 40
 import Urls from '../../constants/Urls';
41
+import { ApiRejectType } from '../../utils/WebData';
42
+import { REQUEST_STATUS } from '../../utils/Requests';
41 43
 
42 44
 type LoginScreenNavigationProp = StackScreenProps<MainStackParamsList, 'login'>;
43 45
 
@@ -53,7 +55,7 @@ type StateType = {
53 55
   isPasswordValidated: boolean;
54 56
   loading: boolean;
55 57
   dialogVisible: boolean;
56
-  dialogError: number;
58
+  dialogError: REQUEST_STATUS;
57 59
   mascotDialogVisible: boolean;
58 60
 };
59 61
 
@@ -115,7 +117,7 @@ class LoginScreen extends React.Component<Props, StateType> {
115 117
       isPasswordValidated: false,
116 118
       loading: false,
117 119
       dialogVisible: false,
118
-      dialogError: 0,
120
+      dialogError: REQUEST_STATUS.SUCCESS,
119 121
       mascotDialogVisible: AsyncStorageManager.getBool(
120 122
         AsyncStorageManager.PREFERENCES.loginShowMascot.key
121 123
       ),
@@ -335,10 +337,10 @@ class LoginScreen extends React.Component<Props, StateType> {
335 337
    *
336 338
    * @param error The error given by the login request
337 339
    */
338
-  showErrorDialog = (error: number) => {
340
+  showErrorDialog = (error: ApiRejectType) => {
339 341
     this.setState({
340 342
       dialogVisible: true,
341
-      dialogError: error,
343
+      dialogError: error.status,
342 344
     });
343 345
   };
344 346
 
@@ -433,10 +435,10 @@ class LoginScreen extends React.Component<Props, StateType> {
433 435
         end={{ x: 0.1, y: 1 }}
434 436
       >
435 437
         <KeyboardAvoidingView
436
-          behavior="height"
438
+          behavior={'height'}
437 439
           contentContainerStyle={GENERAL_STYLES.flex}
438 440
           style={GENERAL_STYLES.flex}
439
-          enabled
441
+          enabled={true}
440 442
           keyboardVerticalOffset={100}
441 443
         >
442 444
           <CollapsibleScrollView headerColors={'transparent'}>
@@ -445,7 +447,7 @@ class LoginScreen extends React.Component<Props, StateType> {
445 447
               visible={mascotDialogVisible}
446 448
               title={i18n.t('screens.login.mascotDialog.title')}
447 449
               message={i18n.t('screens.login.mascotDialog.message')}
448
-              icon="help"
450
+              icon={'help'}
449 451
               buttons={{
450 452
                 cancel: {
451 453
                   message: i18n.t('screens.login.mascotDialog.button'),
@@ -458,7 +460,7 @@ class LoginScreen extends React.Component<Props, StateType> {
458 460
             <ErrorDialog
459 461
               visible={dialogVisible}
460 462
               onDismiss={this.hideErrorDialog}
461
-              errorCode={dialogError}
463
+              status={dialogError}
462 464
             />
463 465
           </CollapsibleScrollView>
464 466
         </KeyboardAvoidingView>

+ 85
- 5
src/utils/Requests.tsx View File

@@ -1,5 +1,10 @@
1
+import i18n from 'i18n-js';
2
+import { ApiRejectType } from './WebData';
3
+
1 4
 export enum REQUEST_STATUS {
2 5
   SUCCESS = 200,
6
+  TOKEN_SAVE = 4,
7
+  TOKEN_RETRIEVE = 5,
3 8
   BAD_INPUT = 400,
4 9
   FORBIDDEN = 403,
5 10
   CONNECTION_ERROR = 404,
@@ -7,16 +12,91 @@ export enum REQUEST_STATUS {
7 12
   UNKNOWN = 999,
8 13
 }
9 14
 
10
-export enum REQUEST_CODES {
15
+export enum API_REQUEST_CODES {
11 16
   SUCCESS = 0,
12 17
   BAD_CREDENTIALS = 1,
13 18
   BAD_TOKEN = 2,
14 19
   NO_CONSENT = 3,
15
-  TOKEN_SAVE = 4,
16
-  TOKEN_RETRIEVE = 5,
17 20
   BAD_INPUT = 400,
18 21
   FORBIDDEN = 403,
19
-  CONNECTION_ERROR = 404,
20
-  SERVER_ERROR = 500,
21 22
   UNKNOWN = 999,
22 23
 }
24
+
25
+export function getErrorMessage(
26
+  props: Partial<ApiRejectType>,
27
+  message?: string,
28
+  icon?: string
29
+) {
30
+  let fullMessage = {
31
+    message: '',
32
+    icon: '',
33
+  };
34
+  if (props.code === undefined) {
35
+    switch (props.status) {
36
+      case REQUEST_STATUS.BAD_INPUT:
37
+        fullMessage.message = i18n.t('errors.badInput');
38
+        fullMessage.icon = 'alert-circle-outline';
39
+        break;
40
+      case REQUEST_STATUS.FORBIDDEN:
41
+        fullMessage.message = i18n.t('errors.forbidden');
42
+        fullMessage.icon = 'lock';
43
+        break;
44
+      case REQUEST_STATUS.CONNECTION_ERROR:
45
+        fullMessage.message = i18n.t('errors.connectionError');
46
+        fullMessage.icon = 'access-point-network-off';
47
+        break;
48
+      case REQUEST_STATUS.SERVER_ERROR:
49
+        fullMessage.message = i18n.t('errors.serverError');
50
+        fullMessage.icon = 'server-network-off';
51
+        break;
52
+      case REQUEST_STATUS.TOKEN_SAVE:
53
+        fullMessage.message = i18n.t('errors.tokenSave');
54
+        fullMessage.icon = 'alert-circle-outline';
55
+        break;
56
+      default:
57
+        fullMessage.message = i18n.t('errors.unknown');
58
+        fullMessage.icon = 'alert-circle-outline';
59
+        break;
60
+    }
61
+  } else {
62
+    switch (props.code) {
63
+      case API_REQUEST_CODES.BAD_CREDENTIALS:
64
+        fullMessage.message = i18n.t('errors.badCredentials');
65
+        fullMessage.icon = 'account-alert-outline';
66
+        break;
67
+      case API_REQUEST_CODES.BAD_TOKEN:
68
+        fullMessage.message = i18n.t('errors.badToken');
69
+        fullMessage.icon = 'account-alert-outline';
70
+        break;
71
+      case API_REQUEST_CODES.NO_CONSENT:
72
+        fullMessage.message = i18n.t('errors.noConsent');
73
+        fullMessage.icon = 'account-remove-outline';
74
+        break;
75
+      case API_REQUEST_CODES.BAD_INPUT:
76
+        fullMessage.message = i18n.t('errors.badInput');
77
+        fullMessage.icon = 'alert-circle-outline';
78
+        break;
79
+      case API_REQUEST_CODES.FORBIDDEN:
80
+        fullMessage.message = i18n.t('errors.forbidden');
81
+        fullMessage.icon = 'lock';
82
+        break;
83
+      default:
84
+        fullMessage.message = i18n.t('errors.unknown');
85
+        fullMessage.icon = 'alert-circle-outline';
86
+        break;
87
+    }
88
+  }
89
+
90
+  if (props.code !== undefined) {
91
+    fullMessage.message += `\n\nCode {${props.status}:${props.code}}`;
92
+  } else {
93
+    fullMessage.message += `\n\nCode {${props.status}}`;
94
+  }
95
+  if (message) {
96
+    fullMessage.message = message;
97
+  }
98
+  if (icon) {
99
+    fullMessage.icon = icon;
100
+  }
101
+  return fullMessage;
102
+}

+ 33
- 18
src/utils/WebData.ts View File

@@ -18,20 +18,21 @@
18 18
  */
19 19
 
20 20
 import Urls from '../constants/Urls';
21
+import { API_REQUEST_CODES, REQUEST_STATUS } from './Requests';
21 22
 
22
-export const ERROR_TYPE = {
23
-  SUCCESS: 0,
24
-  BAD_CREDENTIALS: 1,
25
-  BAD_TOKEN: 2,
26
-  NO_CONSENT: 3,
27
-  TOKEN_SAVE: 4,
28
-  TOKEN_RETRIEVE: 5,
29
-  BAD_INPUT: 400,
30
-  FORBIDDEN: 403,
31
-  CONNECTION_ERROR: 404,
32
-  SERVER_ERROR: 500,
33
-  UNKNOWN: 999,
34
-};
23
+// export const ERROR_TYPE = {
24
+//   SUCCESS: 0,
25
+//   BAD_CREDENTIALS: 1,
26
+//   BAD_TOKEN: 2,
27
+//   NO_CONSENT: 3,
28
+//   TOKEN_SAVE: 4,
29
+//   TOKEN_RETRIEVE: 5,
30
+//   BAD_INPUT: 400,
31
+//   FORBIDDEN: 403,
32
+//   CONNECTION_ERROR: 404,
33
+//   SERVER_ERROR: 500,
34
+//   UNKNOWN: 999,
35
+// };
35 36
 
36 37
 export type ApiDataLoginType = {
37 38
   token: string;
@@ -42,6 +43,11 @@ type ApiResponseType<T> = {
42 43
   data: T;
43 44
 };
44 45
 
46
+export type ApiRejectType = {
47
+  status: REQUEST_STATUS;
48
+  code?: API_REQUEST_CODES;
49
+};
50
+
45 51
 /**
46 52
  * Checks if the given API response is valid.
47 53
  *
@@ -76,7 +82,7 @@ export async function apiRequest<T>(
76 82
   params?: object
77 83
 ): Promise<T> {
78 84
   return new Promise(
79
-    (resolve: (data: T) => void, reject: (error: number) => void) => {
85
+    (resolve: (data: T) => void, reject: (error: ApiRejectType) => void) => {
80 86
       let requestParams = {};
81 87
       if (params != null) {
82 88
         requestParams = { ...params };
@@ -95,16 +101,25 @@ export async function apiRequest<T>(
95 101
         )
96 102
         .then((response: ApiResponseType<T>) => {
97 103
           if (isApiResponseValid(response)) {
98
-            if (response.error === ERROR_TYPE.SUCCESS) {
104
+            if (response.error === API_REQUEST_CODES.SUCCESS) {
99 105
               resolve(response.data);
100 106
             } else {
101
-              reject(response.error);
107
+              reject({
108
+                status: REQUEST_STATUS.SUCCESS,
109
+                code: response.error,
110
+              });
102 111
             }
103 112
           } else {
104
-            reject(ERROR_TYPE.SERVER_ERROR);
113
+            reject({
114
+              status: REQUEST_STATUS.SERVER_ERROR,
115
+            });
105 116
           }
106 117
         })
107
-        .catch((): void => reject(ERROR_TYPE.CONNECTION_ERROR));
118
+        .catch(() =>
119
+          reject({
120
+            status: REQUEST_STATUS.SERVER_ERROR,
121
+          })
122
+        );
108 123
     }
109 124
   );
110 125
 }

+ 19
- 8
src/utils/customHooks.tsx View File

@@ -19,6 +19,7 @@
19 19
 
20 20
 import { DependencyList, useEffect, useRef, useState } from 'react';
21 21
 import { REQUEST_STATUS } from './Requests';
22
+import { ApiRejectType } from './WebData';
22 23
 
23 24
 export function useMountEffect(func: () => void) {
24 25
   // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -97,14 +98,24 @@ export function useRequestLogic<T>(
97 98
             onCacheUpdate(requestResponse);
98 99
           }
99 100
         })
100
-        .catch(() => {
101
-          setResponse((prevState) => ({
102
-            loading: false,
103
-            lastRefreshDate: prevState.lastRefreshDate,
104
-            status: REQUEST_STATUS.CONNECTION_ERROR,
105
-            code: undefined,
106
-            data: prevState.data,
107
-          }));
101
+        .catch((error: ApiRejectType | undefined) => {
102
+          if (!error) {
103
+            setResponse((prevState) => ({
104
+              loading: false,
105
+              lastRefreshDate: prevState.lastRefreshDate,
106
+              status: REQUEST_STATUS.CONNECTION_ERROR,
107
+              code: undefined,
108
+              data: prevState.data,
109
+            }));
110
+          } else {
111
+            setResponse((prevState) => ({
112
+              loading: false,
113
+              lastRefreshDate: prevState.lastRefreshDate,
114
+              status: error.status,
115
+              code: error.code,
116
+              data: prevState.data,
117
+            }));
118
+          }
108 119
         });
109 120
     }
110 121
   };

Loading…
Cancel
Save