|
@@ -1,14 +1,12 @@
|
1
|
1
|
// @flow
|
2
|
2
|
|
3
|
3
|
import * as React from 'react';
|
4
|
|
-import {withTheme} from 'react-native-paper';
|
5
|
4
|
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
6
|
5
|
import ErrorView from "../Custom/ErrorView";
|
7
|
6
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
8
|
7
|
|
9
|
8
|
type Props = {
|
10
|
9
|
navigation: Object,
|
11
|
|
- theme: Object,
|
12
|
10
|
links: Array<{link: string, mandatory: boolean}>,
|
13
|
11
|
renderFunction: Function,
|
14
|
12
|
}
|
|
@@ -25,24 +23,35 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
25
|
23
|
|
26
|
24
|
currentUserToken: string | null;
|
27
|
25
|
connectionManager: ConnectionManager;
|
28
|
|
- errorCode: number;
|
29
|
|
- data: Array<Object>;
|
30
|
|
- colors: Object;
|
|
26
|
+ errors: Array<number>;
|
|
27
|
+ fetchedData: Array<Object>;
|
31
|
28
|
|
32
|
|
- constructor(props) {
|
|
29
|
+ constructor(props: Object) {
|
33
|
30
|
super(props);
|
34
|
|
- this.colors = props.theme.colors;
|
35
|
31
|
this.connectionManager = ConnectionManager.getInstance();
|
36
|
|
- this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
|
37
|
|
- this.data = new Array(this.props.links.length);
|
|
32
|
+ 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
|
35
|
this.fetchData(); // TODO remove in prod (only use for fast refresh)
|
39
|
36
|
}
|
40
|
37
|
|
41
|
|
- onScreenFocus() {
|
42
|
|
- if (this.currentUserToken !== this.connectionManager.getToken())
|
|
38
|
+ /**
|
|
39
|
+ * Refreshes screen if user changed
|
|
40
|
+ */
|
|
41
|
+ onScreenFocus = () => {
|
|
42
|
+ if (this.currentUserToken !== this.connectionManager.getToken()){
|
|
43
|
+ this.currentUserToken = this.connectionManager.getToken();
|
43
|
44
|
this.fetchData();
|
44
|
|
- }
|
|
45
|
+ }
|
|
46
|
+ };
|
45
|
47
|
|
|
48
|
+ /**
|
|
49
|
+ * Fetches the data from the server.
|
|
50
|
+ *
|
|
51
|
+ * If the user is not logged in errorCode is set to BAD_TOKEN and all requests fail.
|
|
52
|
+ *
|
|
53
|
+ * If the user is logged in, send all requests.
|
|
54
|
+ */
|
46
|
55
|
fetchData = () => {
|
47
|
56
|
if (!this.state.loading)
|
48
|
57
|
this.setState({loading: true});
|
|
@@ -50,39 +59,51 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
50
|
59
|
for (let i = 0; i < this.props.links.length; i++) {
|
51
|
60
|
this.connectionManager.authenticatedRequest(this.props.links[i].link, null, null)
|
52
|
61
|
.then((data) => {
|
53
|
|
- this.onFinishedLoading(data, i, -1);
|
|
62
|
+ this.onRequestFinished(data, i, -1);
|
54
|
63
|
})
|
55
|
64
|
.catch((error) => {
|
56
|
|
- this.onFinishedLoading(null, i, error);
|
|
65
|
+ this.onRequestFinished(null, i, error);
|
57
|
66
|
});
|
58
|
67
|
}
|
59
|
|
-
|
60
|
68
|
} else {
|
61
|
|
- this.onFinishedLoading(null, -1, ERROR_TYPE.BAD_CREDENTIALS);
|
|
69
|
+ for (let i = 0; i < this.props.links.length; i++) {
|
|
70
|
+ this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN);
|
|
71
|
+ }
|
62
|
72
|
}
|
63
|
73
|
};
|
64
|
74
|
|
65
|
|
- onFinishedLoading(data: Object, index: number, error: number) {
|
66
|
|
- if (index >= 0 && index < this.props.links.length)
|
67
|
|
- this.data[index] = data;
|
68
|
|
- this.currentUserToken = data !== undefined
|
69
|
|
- ? this.connectionManager.getToken()
|
70
|
|
- : null;
|
71
|
|
- this.errorCode = error;
|
72
|
|
-
|
73
|
|
- if (this.errorCode === ERROR_TYPE.BAD_TOKEN) { // Token expired, logout user
|
74
|
|
- this.connectionManager.disconnect()
|
75
|
|
- .then(() => {
|
76
|
|
- this.props.navigation.navigate("login");
|
77
|
|
- });
|
78
|
|
- } else if (this.allRequestsFinished())
|
|
75
|
+ /**
|
|
76
|
+ * Callback used when a request finishes, successfully or not.
|
|
77
|
+ * Saves data and error code.
|
|
78
|
+ * If the token is invalid, logout the user and open the login screen.
|
|
79
|
+ * If the last request was received, stop the loading screen.
|
|
80
|
+ *
|
|
81
|
+ * @param data The data fetched from the server
|
|
82
|
+ * @param index The index for the data
|
|
83
|
+ * @param error The error code received
|
|
84
|
+ */
|
|
85
|
+ onRequestFinished(data: Object | null, index: number, error: number) {
|
|
86
|
+ if (index >= 0 && index < this.props.links.length){
|
|
87
|
+ this.fetchedData[index] = data;
|
|
88
|
+ this.errors[index] = error;
|
|
89
|
+ }
|
|
90
|
+
|
|
91
|
+ if (error === ERROR_TYPE.BAD_TOKEN) // Token expired, logout user
|
|
92
|
+ this.connectionManager.disconnect();
|
|
93
|
+
|
|
94
|
+ if (this.allRequestsFinished())
|
79
|
95
|
this.setState({loading: false});
|
80
|
96
|
}
|
81
|
97
|
|
|
98
|
+ /**
|
|
99
|
+ * Checks if all requests finished processing
|
|
100
|
+ *
|
|
101
|
+ * @return {boolean} True if all finished
|
|
102
|
+ */
|
82
|
103
|
allRequestsFinished() {
|
83
|
104
|
let finished = true;
|
84
|
|
- for (let i = 0; i < this.data.length; i++) {
|
85
|
|
- if (this.data[i] === undefined) {
|
|
105
|
+ for (let i = 0; i < this.fetchedData.length; i++) {
|
|
106
|
+ if (this.fetchedData[i] === undefined) {
|
86
|
107
|
finished = false;
|
87
|
108
|
break;
|
88
|
109
|
}
|
|
@@ -90,10 +111,17 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
90
|
111
|
return finished;
|
91
|
112
|
}
|
92
|
113
|
|
|
114
|
+ /**
|
|
115
|
+ * Checks if all requests have finished successfully.
|
|
116
|
+ * This will return false only if a mandatory request failed.
|
|
117
|
+ * All non-mandatory requests can fail without impacting the return value.
|
|
118
|
+ *
|
|
119
|
+ * @return {boolean} True if all finished successfully
|
|
120
|
+ */
|
93
|
121
|
allRequestsValid() {
|
94
|
122
|
let valid = true;
|
95
|
|
- for (let i = 0; i < this.data.length; i++) {
|
96
|
|
- if (this.data[i] === null && this.props.links[i].mandatory) {
|
|
123
|
+ for (let i = 0; i < this.fetchedData.length; i++) {
|
|
124
|
+ if (this.fetchedData[i] === null && this.props.links[i].mandatory) {
|
97
|
125
|
valid = false;
|
98
|
126
|
break;
|
99
|
127
|
}
|
|
@@ -101,15 +129,40 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
101
|
129
|
return valid;
|
102
|
130
|
}
|
103
|
131
|
|
|
132
|
+ /**
|
|
133
|
+ * Gets the error to render.
|
|
134
|
+ * Non-mandatory requests are ignored.
|
|
135
|
+ *
|
|
136
|
+ *
|
|
137
|
+ * @return {number} The error code or ERROR_TYPE.SUCCESS if no error was found
|
|
138
|
+ */
|
|
139
|
+ getError() {
|
|
140
|
+ for (let i = 0; i < this.errors.length; i++) {
|
|
141
|
+ if (this.errors[i] !== 0 && this.props.links[i].mandatory) {
|
|
142
|
+ return this.errors[i];
|
|
143
|
+ }
|
|
144
|
+ }
|
|
145
|
+ return ERROR_TYPE.SUCCESS;
|
|
146
|
+ }
|
|
147
|
+
|
|
148
|
+ /**
|
|
149
|
+ * Gets the error view to display in case of error
|
|
150
|
+ *
|
|
151
|
+ * @return {*}
|
|
152
|
+ */
|
104
|
153
|
getErrorRender() {
|
105
|
154
|
return (
|
106
|
155
|
<ErrorView
|
107
|
|
- errorCode={this.errorCode}
|
|
156
|
+ {...this.props}
|
|
157
|
+ errorCode={this.getError()}
|
108
|
158
|
onRefresh={this.fetchData}
|
109
|
159
|
/>
|
110
|
160
|
);
|
111
|
161
|
}
|
112
|
162
|
|
|
163
|
+ /**
|
|
164
|
+ * Reloads the data, to be called using ref by parent components
|
|
165
|
+ */
|
113
|
166
|
reload() {
|
114
|
167
|
this.fetchData();
|
115
|
168
|
}
|
|
@@ -119,10 +172,10 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
119
|
172
|
this.state.loading
|
120
|
173
|
? <BasicLoadingScreen/>
|
121
|
174
|
: (this.allRequestsValid()
|
122
|
|
- ? this.props.renderFunction(this.data)
|
|
175
|
+ ? this.props.renderFunction(this.fetchedData)
|
123
|
176
|
: this.getErrorRender())
|
124
|
177
|
);
|
125
|
178
|
}
|
126
|
179
|
}
|
127
|
180
|
|
128
|
|
-export default withTheme(AuthenticatedScreen);
|
|
181
|
+export default AuthenticatedScreen;
|