Browse Source

Improved error handling and added new api endpoints

Arnaud Vergnet 4 years ago
parent
commit
0ba48adb6e

+ 2
- 42
__tests__/managers/ConnectionManager.test.js View File

@@ -1,5 +1,6 @@
1 1
 import React from 'react';
2
-import ConnectionManager, {ERROR_TYPE} from "../../src/managers/ConnectionManager";
2
+import ConnectionManager from "../../src/managers/ConnectionManager";
3
+import {ERROR_TYPE} from "../../src/utils/WebData";
3 4
 import * as SecureStore from 'expo-secure-store';
4 5
 
5 6
 let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
@@ -63,47 +64,6 @@ test('recoverLogin success saved', () => {
63 64
     return expect(c.recoverLogin()).resolves.toBe('token2');
64 65
 });
65 66
 
66
-test('isRequestResponseValid', () => {
67
-    let json = {
68
-        error: 0,
69
-        data: {}
70
-    };
71
-    expect(c.isResponseValid(json)).toBeTrue();
72
-    json = {
73
-        error: 1,
74
-        data: {}
75
-    };
76
-    expect(c.isResponseValid(json)).toBeTrue();
77
-    json = {
78
-        error: 50,
79
-        data: {}
80
-    };
81
-    expect(c.isResponseValid(json)).toBeTrue();
82
-    json = {
83
-        error: 50,
84
-        data: {truc: 'machin'}
85
-    };
86
-    expect(c.isResponseValid(json)).toBeTrue();
87
-    json = {
88
-        message: 'coucou'
89
-    };
90
-    expect(c.isResponseValid(json)).toBeFalse();
91
-    json = {
92
-        error: 'coucou',
93
-        data: {truc: 'machin'}
94
-    };
95
-    expect(c.isResponseValid(json)).toBeFalse();
96
-    json = {
97
-        error: 0,
98
-        data: 'coucou'
99
-    };
100
-    expect(c.isResponseValid(json)).toBeFalse();
101
-    json = {
102
-        error: 0,
103
-    };
104
-    expect(c.isResponseValid(json)).toBeFalse();
105
-});
106
-
107 67
 test("isConnectionResponseValid", () => {
108 68
     let json = {
109 69
         error: 0,

+ 45
- 0
__tests__/utils/WebData.js View File

@@ -0,0 +1,45 @@
1
+import React from 'react';
2
+import {isResponseValid} from "../../src/utils/WebData";
3
+
4
+let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
5
+
6
+test('isRequestResponseValid', () => {
7
+    let json = {
8
+        error: 0,
9
+        data: {}
10
+    };
11
+    expect(isResponseValid(json)).toBeTrue();
12
+    json = {
13
+        error: 1,
14
+        data: {}
15
+    };
16
+    expect(isResponseValid(json)).toBeTrue();
17
+    json = {
18
+        error: 50,
19
+        data: {}
20
+    };
21
+    expect(isResponseValid(json)).toBeTrue();
22
+    json = {
23
+        error: 50,
24
+        data: {truc: 'machin'}
25
+    };
26
+    expect(isResponseValid(json)).toBeTrue();
27
+    json = {
28
+        message: 'coucou'
29
+    };
30
+    expect(isResponseValid(json)).toBeFalse();
31
+    json = {
32
+        error: 'coucou',
33
+        data: {truc: 'machin'}
34
+    };
35
+    expect(isResponseValid(json)).toBeFalse();
36
+    json = {
37
+        error: 0,
38
+        data: 'coucou'
39
+    };
40
+    expect(isResponseValid(json)).toBeFalse();
41
+    json = {
42
+        error: 0,
43
+    };
44
+    expect(isResponseValid(json)).toBeFalse();
45
+});

+ 18
- 11
src/components/Amicale/AuthenticatedScreen.js View File

@@ -1,13 +1,18 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
4
+import ConnectionManager from "../../managers/ConnectionManager";
5
+import {ERROR_TYPE} from "../../utils/WebData";
5 6
 import ErrorView from "../Custom/ErrorView";
6 7
 import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
7 8
 
8 9
 type Props = {
9 10
     navigation: Object,
10
-    links: Array<{link: string, mandatory: boolean}>,
11
+    requests: Array<{
12
+        link: string,
13
+        params: Object,
14
+        mandatory: boolean
15
+    }>,
11 16
     renderFunction: Function,
12 17
 }
13 18
 
@@ -30,15 +35,15 @@ class AuthenticatedScreen extends React.Component<Props, State> {
30 35
         super(props);
31 36
         this.connectionManager = ConnectionManager.getInstance();
32 37
         this.props.navigation.addListener('focus', this.onScreenFocus);
33
-        this.fetchedData = new Array(this.props.links.length);
34
-        this.errors = new Array(this.props.links.length);
38
+        this.fetchedData = new Array(this.props.requests.length);
39
+        this.errors = new Array(this.props.requests.length);
35 40
     }
36 41
 
37 42
     /**
38 43
      * Refreshes screen if user changed
39 44
      */
40 45
     onScreenFocus = () => {
41
-        if (this.currentUserToken !== this.connectionManager.getToken()){
46
+        if (this.currentUserToken !== this.connectionManager.getToken()) {
42 47
             this.currentUserToken = this.connectionManager.getToken();
43 48
             this.fetchData();
44 49
         }
@@ -55,8 +60,10 @@ class AuthenticatedScreen extends React.Component<Props, State> {
55 60
         if (!this.state.loading)
56 61
             this.setState({loading: true});
57 62
         if (this.connectionManager.isLoggedIn()) {
58
-            for (let i = 0; i < this.props.links.length; i++) {
59
-                this.connectionManager.authenticatedRequest(this.props.links[i].link, null, null)
63
+            for (let i = 0; i < this.props.requests.length; i++) {
64
+                this.connectionManager.authenticatedRequest(
65
+                    this.props.requests[i].link,
66
+                    this.props.requests[i].params)
60 67
                     .then((data) => {
61 68
                         this.onRequestFinished(data, i, -1);
62 69
                     })
@@ -65,7 +72,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
65 72
                     });
66 73
             }
67 74
         } else {
68
-            for (let i = 0; i < this.props.links.length; i++) {
75
+            for (let i = 0; i < this.props.requests.length; i++) {
69 76
                 this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN);
70 77
             }
71 78
         }
@@ -82,7 +89,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
82 89
      * @param error The error code received
83 90
      */
84 91
     onRequestFinished(data: Object | null, index: number, error: number) {
85
-        if (index >= 0 && index < this.props.links.length){
92
+        if (index >= 0 && index < this.props.requests.length) {
86 93
             this.fetchedData[index] = data;
87 94
             this.errors[index] = error;
88 95
         }
@@ -120,7 +127,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
120 127
     allRequestsValid() {
121 128
         let valid = true;
122 129
         for (let i = 0; i < this.fetchedData.length; i++) {
123
-            if (this.fetchedData[i] === null && this.props.links[i].mandatory) {
130
+            if (this.fetchedData[i] === null && this.props.requests[i].mandatory) {
124 131
                 valid = false;
125 132
                 break;
126 133
             }
@@ -137,7 +144,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
137 144
      */
138 145
     getError() {
139 146
         for (let i = 0; i < this.errors.length; i++) {
140
-            if (this.errors[i] !== 0 && this.props.links[i].mandatory) {
147
+            if (this.errors[i] !== 0 && this.props.requests[i].mandatory) {
141 148
                 return this.errors[i];
142 149
             }
143 150
         }

+ 1
- 2
src/components/Amicale/Vote/VoteSelect.js View File

@@ -45,8 +45,7 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
45 45
         return new Promise((resolve, reject) => {
46 46
             ConnectionManager.getInstance().authenticatedRequest(
47 47
                 "elections/vote",
48
-                ["vote"],
49
-                [parseInt(this.state.selectedTeam)])
48
+                {"vote": parseInt(this.state.selectedTeam)})
50 49
                 .then(() => {
51 50
                     this.onVoteDialogDismiss();
52 51
                     this.props.onVoteSuccess();

+ 1
- 1
src/components/Custom/ErrorView.js View File

@@ -5,7 +5,7 @@ import {Button, Subheading, withTheme} from 'react-native-paper';
5 5
 import {StyleSheet, View} from "react-native";
6 6
 import {MaterialCommunityIcons} from "@expo/vector-icons";
7 7
 import i18n from 'i18n-js';
8
-import {ERROR_TYPE} from "../../managers/ConnectionManager";
8
+import {ERROR_TYPE} from "../../utils/WebData";
9 9
 
10 10
 type Props = {
11 11
     navigation: Object,

+ 1
- 2
src/components/Lists/WebSectionList.js View File

@@ -1,13 +1,12 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {readData} from "../../utils/WebData";
4
+import {ERROR_TYPE, readData} from "../../utils/WebData";
5 5
 import i18n from "i18n-js";
6 6
 import {Snackbar} from 'react-native-paper';
7 7
 import {RefreshControl, SectionList, View} from "react-native";
8 8
 import ErrorView from "../Custom/ErrorView";
9 9
 import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
10
-import {ERROR_TYPE} from "../../managers/ConnectionManager";
11 10
 
12 11
 type Props = {
13 12
     navigation: Object,

+ 26
- 96
src/managers/ConnectionManager.js View File

@@ -1,23 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import * as SecureStore from 'expo-secure-store';
4
-
5
-export const ERROR_TYPE = {
6
-    SUCCESS: 0,
7
-    BAD_CREDENTIALS: 1,
8
-    BAD_TOKEN: 2,
9
-    NO_CONSENT: 3,
10
-    BAD_INPUT: 400,
11
-    FORBIDDEN: 403,
12
-    CONNECTION_ERROR: 404,
13
-    SERVER_ERROR: 500,
14
-    UNKNOWN: 999,
15
-};
16
-
17
-type response_format = {
18
-    error: number,
19
-    data: Object,
20
-}
4
+import {apiRequest, ERROR_TYPE, isResponseValid} from "../utils/WebData";
21 5
 
22 6
 /**
23 7
  * champ: error
@@ -30,7 +14,7 @@ type response_format = {
30 14
  * 500 : SERVER_ERROR -> pb coté serveur
31 15
  */
32 16
 
33
-const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
17
+
34 18
 const AUTH_PATH = "password";
35 19
 
36 20
 export default class ConnectionManager {
@@ -126,53 +110,27 @@ export default class ConnectionManager {
126 110
     }
127 111
 
128 112
     async connect(email: string, password: string) {
129
-        let data = {
130
-            email: email,
131
-            password: password,
132
-        };
133 113
         return new Promise((resolve, reject) => {
134
-            fetch(API_ENDPOINT + AUTH_PATH, {
135
-                method: 'POST',
136
-                headers: new Headers({
137
-                    'Accept': 'application/json',
138
-                    'Content-Type': 'application/json',
139
-                }),
140
-                body: JSON.stringify(data)
141
-            }).then(async (response) => response.json())
142
-                .then((response: response_format) => {
143
-                    if (this.isConnectionResponseValid(response)) {
144
-                        if (response.error === ERROR_TYPE.SUCCESS) {
145
-                            this.saveLogin(email, response.data.token)
146
-                                .then(() => {
147
-                                    resolve(true);
148
-                                })
149
-                                .catch(() => {
150
-                                    reject(ERROR_TYPE.UNKNOWN);
151
-                                });
152
-                        } else
153
-                            reject(response.error);
154
-                    } else
155
-                        reject(ERROR_TYPE.CONNECTION_ERROR);
114
+            const data = {
115
+                email: email,
116
+                password: password,
117
+            };
118
+            apiRequest(AUTH_PATH, 'POST', data)
119
+                .then((response) => {
120
+                    this.saveLogin(email, response.token)
121
+                        .then(() => {
122
+                            resolve(true);
123
+                        })
124
+                        .catch(() => {
125
+                            reject(ERROR_TYPE.UNKNOWN);
126
+                        });
156 127
                 })
157
-                .catch((error) => {
158
-                    reject(ERROR_TYPE.CONNECTION_ERROR);
159
-                });
128
+                .catch((error) => reject(error));
160 129
         });
161 130
     }
162 131
 
163
-    isResponseValid(response: response_format) {
164
-        let valid = response !== undefined
165
-            && response.error !== undefined
166
-            && typeof response.error === "number";
167
-
168
-        valid = valid
169
-            && response.data !== undefined
170
-            && typeof response.data === "object";
171
-        return valid;
172
-    }
173
-
174
-    isConnectionResponseValid(response: response_format) {
175
-        let valid = this.isResponseValid(response);
132
+    isConnectionResponseValid(response: Object) {
133
+        let valid = isResponseValid(response);
176 134
 
177 135
         if (valid && response.error === ERROR_TYPE.SUCCESS)
178 136
             valid = valid
@@ -182,45 +140,17 @@ export default class ConnectionManager {
182 140
         return valid;
183 141
     }
184 142
 
185
-    generatePostArguments(keys: Array<string>, values: Array<string>) {
186
-        let data = {};
187
-        for (let i = 0; i < keys.length; i++) {
188
-            data[keys[i]] = values[i];
189
-        }
190
-        return data;
191
-    }
192
-
193
-    async authenticatedRequest(path: string, keys: Array<string>|null, values: Array<any>|null) {
143
+    async authenticatedRequest(path: string, params: Object) {
194 144
         return new Promise((resolve, reject) => {
195 145
             if (this.getToken() !== null) {
196
-                let data = {};
197
-                if (keys !== null && values !== null && keys.length === values.length)
198
-                    data = this.generatePostArguments(keys, values);
199 146
                 // console.log(data);
200
-                fetch(API_ENDPOINT + path, {
201
-                    method: 'POST',
202
-                    headers: new Headers({
203
-                        'Accept': 'application/json',
204
-                        'Content-Type': 'application/json',
205
-                    }),
206
-                    body: JSON.stringify({
207
-                        token: this.getToken(),
208
-                        ...data
209
-                    })
210
-                }).then(async (response) => response.json())
211
-                    .then((response: response_format) => {
212
-                        // console.log(response);
213
-                        if (this.isResponseValid(response)) {
214
-                            if (response.error === ERROR_TYPE.SUCCESS)
215
-                                resolve(response.data);
216
-                            else
217
-                                reject(response.error);
218
-                        } else
219
-                            reject(ERROR_TYPE.CONNECTION_ERROR);
220
-                    })
221
-                    .catch(() => {
222
-                        reject(ERROR_TYPE.CONNECTION_ERROR);
223
-                    });
147
+                let data = {
148
+                    token: this.getToken(),
149
+                    ...params
150
+                };
151
+                apiRequest(path, 'POST', data)
152
+                    .then((response) => resolve(response))
153
+                    .catch((error) => reject(error));
224 154
             } else
225 155
                 reject(ERROR_TYPE.UNKNOWN);
226 156
         });

+ 7
- 25
src/screens/Amicale/Clubs/ClubDisplayScreen.js View File

@@ -22,22 +22,6 @@ function openWebLink(event, link) {
22 22
     Linking.openURL(link).catch((err) => console.error('Error opening link', err));
23 23
 }
24 24
 
25
-const FakeClub = {
26
-    "category": [
27
-        3,
28
-        6,
29
-    ],
30
-    "description": "<p class=\"ql-align-justify\">Les 100 Tours de l’INSA reviennent en force pour une cinquième édition les 5 et 6 juin prochain&nbsp;!</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">Prépare-toi pour le plus gros évènement de l’année sur le campus de notre belle école qui nous réunit tous autour d’activités folles&nbsp;pour fêter la fin de l’année dans la bonne humeur !</p><p class=\"ql-align-justify\">L’éco-festival tournera autour d’une grande course par équipe qui nous vaut ce doux nom de 100 tours. Ce sera le moment de défier tes potes pour tenter de remporter de nombreux lots, et surtout l’admiration de tous. Mais cela ne s’arrête pas là, puisque tu pourras aussi participer à des activités à sensation, divers ateliers, et de quoi chiller avec tes potes en écoutant de la bonne musique. Tu pourras ensuite enchaîner sur LA soirée de l’année, rythmée par des artistes sur-motivés&nbsp;!</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">Tu es bien entendu le bienvenu si l’envie te prend de rejoindre l’équipe et de nous aider à organiser cet évènement du turfu&nbsp;!</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">La team 100 Tours</p><p class=\"ql-align-justify\"><br></p><p>Contact&nbsp;: <a href=\"mailto:100Tours@amicale-insat.fr\" target=\"_blank\">100tours@amicale-insat.fr</a></p><p>Facebook&nbsp;: Les 100 Tours de l’INSA</p><p>Instagram&nbsp;: 100tours.insatoulouse</p>",
31
-    "id": 110,
32
-    "logo": "https://www.amicale-insat.fr/storage/clubLogos/2cca8885dd3bdf902124f038b548962b.jpeg",
33
-    "name": "100 Tours",
34
-    "responsibles": [
35
-        "Juliette Duval",
36
-        "Emilie Cuminal",
37
-        "Maxime Doré",
38
-    ],
39
-};
40
-
41 25
 /**
42 26
  * Class defining a club event information page.
43 27
  * If called with data and categories navigation parameters, will use those to display the data.
@@ -132,11 +116,8 @@ class ClubDisplayScreen extends React.Component<Props, State> {
132 116
         this.props.navigation.setOptions({title: data.name})
133 117
     }
134 118
 
135
-    getScreen = (data: Object) => {
136
-        console.log('fetchedData passed to screen:');
137
-        console.log(data);
138
-        console.log("Using fake data");
139
-        data = FakeClub;
119
+    getScreen = (response: Array<Object>) => {
120
+        let data = response[0];
140 121
         this.updateHeaderTitle(data);
141 122
 
142 123
         return (
@@ -182,16 +163,17 @@ class ClubDisplayScreen extends React.Component<Props, State> {
182 163
         if (this.shouldFetchData)
183 164
             return <AuthenticatedScreen
184 165
                 {...this.props}
185
-                links={[
166
+                requests={[
186 167
                     {
187
-                        link: 'clubs/' + this.clubId,
188
-                        mandatory: true,
168
+                        link: 'clubs/info',
169
+                        params: {'id': this.clubId},
170
+                        mandatory: true
189 171
                     }
190 172
                 ]}
191 173
                 renderFunction={this.getScreen}
192 174
             />;
193 175
         else
194
-            return this.getScreen(this.displayData);
176
+            return this.getScreen([this.displayData]);
195 177
     }
196 178
 }
197 179
 

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

@@ -193,9 +193,10 @@ class ClubListScreen extends React.Component<Props, State> {
193 193
         return (
194 194
             <AuthenticatedScreen
195 195
                 {...this.props}
196
-                links={[
196
+                requests={[
197 197
                     {
198 198
                         link: 'clubs/list',
199
+                        params: {},
199 200
                         mandatory: true,
200 201
                     }
201 202
                 ]}

+ 6
- 10
src/screens/Amicale/ProfileScreen.js View File

@@ -265,11 +265,10 @@ class ProfileScreen extends React.Component<Props, State> {
265 265
      * @return {*}
266 266
      */
267 267
     clubListItem = ({item}: Object) => {
268
-        const onPress = () => this.openClubDetailsScreen(0); // TODO get club id
269
-        const isManager = false; // TODO detect if manager
268
+        const onPress = () => this.openClubDetailsScreen(item.id);
270 269
         let description = i18n.t("profileScreen.isMember");
271 270
         let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
272
-        if (isManager) {
271
+        if (item.is_manager) {
273 272
             description = i18n.t("profileScreen.isManager");
274 273
             icon = (props) => <List.Icon {...props} icon="star" color={this.colors.primary}/>;
275 274
         }
@@ -289,17 +288,13 @@ class ProfileScreen extends React.Component<Props, State> {
289 288
      * @param list The club list
290 289
      * @return {*}
291 290
      */
292
-    getClubList(list: Array<string>) {
293
-        let dataset = [];
294
-        for (let i = 0; i < list.length; i++) {
295
-            dataset.push({name: list[i]});
296
-        }
291
+    getClubList(list: Array<Object>) {
297 292
         return (
298 293
             //$FlowFixMe
299 294
             <FlatList
300 295
                 renderItem={this.clubListItem}
301 296
                 keyExtractor={this.clubKeyExtractor}
302
-                data={dataset}
297
+                data={list}
303 298
             />
304 299
         );
305 300
     }
@@ -308,9 +303,10 @@ class ProfileScreen extends React.Component<Props, State> {
308 303
         return (
309 304
             <AuthenticatedScreen
310 305
                 {...this.props}
311
-                links={[
306
+                requests={[
312 307
                     {
313 308
                         link: 'user/profile',
309
+                        params: {},
314 310
                         mandatory: true,
315 311
                     }
316 312
                 ]}

+ 3
- 1
src/screens/Amicale/VoteScreen.js View File

@@ -216,13 +216,15 @@ export default class VoteScreen extends React.Component<Props, State> {
216 216
             <AuthenticatedScreen
217 217
                 {...this.props}
218 218
                 ref={this.authRef}
219
-                links={[
219
+                requests={[
220 220
                     {
221 221
                         link: 'elections/teams',
222
+                        params: {},
222 223
                         mandatory: false,
223 224
                     },
224 225
                     {
225 226
                         link: 'elections/datesString',
227
+                        params: {},
226 228
                         mandatory: false,
227 229
                     },
228 230
                 ]}

+ 47
- 20
src/screens/Planning/PlanningDisplayScreen.js View File

@@ -8,6 +8,9 @@ import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
8 8
 import {Card, withTheme} from 'react-native-paper';
9 9
 import DateManager from "../../managers/DateManager";
10 10
 import ImageModal from 'react-native-image-modal';
11
+import BasicLoadingScreen from "../../components/Custom/BasicLoadingScreen";
12
+import {apiRequest} from "../../utils/WebData";
13
+import ErrorView from "../../components/Custom/ErrorView";
11 14
 
12 15
 type Props = {
13 16
     navigation: Object,
@@ -15,23 +18,14 @@ type Props = {
15 18
 };
16 19
 
17 20
 type State = {
21
+    loading: boolean
18 22
 };
19 23
 
20 24
 function openWebLink(event, link) {
21 25
     Linking.openURL(link).catch((err) => console.error('Error opening link', err));
22 26
 }
23 27
 
24
-const FAKE_EVENT = {
25
-    "id": 142,
26
-    "title": "Soir\u00e9e Impact'INSA",
27
-    "logo": null,
28
-    "date_begin": "2020-04-22 19:00",
29
-    "date_end": "2020-04-22 00:00",
30
-    "description": "<p>R\u00e9servation salle de boom + PK pour la soir\u00e9e Impact'Insa<\/p>",
31
-    "club": "Impact Insa",
32
-    "category_id": 10,
33
-    "url": "https:\/\/www.amicale-insat.fr\/event\/142\/view"
34
-};
28
+const CLUB_INFO_PATH = "event/info";
35 29
 
36 30
 /**
37 31
  * Class defining a planning event information page.
@@ -41,31 +35,55 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
41 35
     displayData: Object;
42 36
     shouldFetchData: boolean;
43 37
     eventId: number;
38
+    errorCode: number;
44 39
 
45 40
     colors: Object;
46 41
 
47
-    state = {
48
-
49
-    };
50
-
51 42
     constructor(props) {
52 43
         super(props);
53 44
         this.colors = props.theme.colors;
54 45
 
55 46
         if (this.props.route.params.data !== undefined) {
56 47
             this.displayData = this.props.route.params.data;
57
-            this.eventId = this.props.route.params.data.eventId;
48
+            console.log(this.displayData);
49
+            this.eventId = this.displayData.id;
58 50
             this.shouldFetchData = false;
51
+            this.errorCode = 0;
52
+            this.state = {
53
+                loading: false,
54
+            };
59 55
         } else {
60
-            this.displayData = FAKE_EVENT;
56
+            this.displayData = null;
61 57
             this.eventId = this.props.route.params.eventId;
62 58
             this.shouldFetchData = true;
63
-            console.log(this.eventId);
59
+            this.errorCode = 0;
60
+            this.state = {
61
+                loading: true,
62
+            };
63
+            this.fetchData();
64
+
64 65
         }
65 66
     }
66 67
 
67
-    render() {
68
-        // console.log("rendering planningDisplayScreen");
68
+    fetchData = () => {
69
+        this.setState({loading: true});
70
+        apiRequest(CLUB_INFO_PATH, 'POST', {id: this.eventId})
71
+            .then(this.onFetchSuccess)
72
+            .catch(this.onFetchError);
73
+    };
74
+
75
+    onFetchSuccess = (data: Object) => {
76
+        this.displayData = data;
77
+        console.log(this.displayData);
78
+        this.setState({loading: false});
79
+    };
80
+
81
+    onFetchError = (error: number) => {
82
+        this.errorCode = error;
83
+        this.setState({loading: false});
84
+    };
85
+
86
+    getContent() {
69 87
         let subtitle = getFormattedEventTime(
70 88
             this.displayData["date_begin"], this.displayData["date_end"]);
71 89
         let dateString = getDateOnlyString(this.displayData["date_begin"]);
@@ -106,6 +124,15 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
106 124
             </ScrollView>
107 125
         );
108 126
     }
127
+
128
+    render() {
129
+        if (this.state.loading)
130
+            return <BasicLoadingScreen/>;
131
+        else if (this.errorCode === 0)
132
+            return this.getContent();
133
+        else
134
+            return <ErrorView {...this.props} errorCode={this.errorCode}   onRefresh={this.fetchData}/>;
135
+    }
109 136
 }
110 137
 
111 138
 export default withTheme(PlanningDisplayScreen);

+ 60
- 0
src/utils/WebData.js View File

@@ -1,5 +1,65 @@
1 1
 // @flow
2 2
 
3
+export const ERROR_TYPE = {
4
+    SUCCESS: 0,
5
+    BAD_CREDENTIALS: 1,
6
+    BAD_TOKEN: 2,
7
+    NO_CONSENT: 3,
8
+    BAD_INPUT: 400,
9
+    FORBIDDEN: 403,
10
+    CONNECTION_ERROR: 404,
11
+    SERVER_ERROR: 500,
12
+    UNKNOWN: 999,
13
+};
14
+
15
+type response_format = {
16
+    error: number,
17
+    data: Object,
18
+}
19
+
20
+const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
21
+
22
+export async function apiRequest(path: string, method: string, params: ?Object) {
23
+    if (params === undefined || params === null)
24
+        params = {};
25
+
26
+    return new Promise((resolve, reject) => {
27
+        fetch(API_ENDPOINT + path, {
28
+            method: method,
29
+            headers: new Headers({
30
+                'Accept': 'application/json',
31
+                'Content-Type': 'application/json',
32
+            }),
33
+            body: JSON.stringify({
34
+                ...params
35
+            })
36
+        }).then(async (response) => response.json())
37
+            .then((response: response_format) => {
38
+                if (isResponseValid(response)) {
39
+                    if (response.error === ERROR_TYPE.SUCCESS)
40
+                        resolve(response.data);
41
+                    else
42
+                        reject(response.error);
43
+                } else
44
+                    reject(ERROR_TYPE.CONNECTION_ERROR);
45
+            })
46
+            .catch(() => {
47
+                reject(ERROR_TYPE.CONNECTION_ERROR);
48
+            });
49
+    });
50
+}
51
+
52
+export function isResponseValid(response: response_format) {
53
+    let valid = response !== undefined
54
+        && response.error !== undefined
55
+        && typeof response.error === "number";
56
+
57
+    valid = valid
58
+        && response.data !== undefined
59
+        && typeof response.data === "object";
60
+    return valid;
61
+}
62
+
3 63
 /**
4 64
  * Read data from FETCH_URL and return it.
5 65
  * If no data was found, returns an empty object

Loading…
Cancel
Save