|
@@ -31,176 +31,70 @@ declare var APP: Object;
|
31
|
31
|
* @returns {Function}
|
32
|
32
|
*/
|
33
|
33
|
export function appNavigate(uri: ?string) {
|
34
|
|
- return (dispatch: Dispatch<any>, getState: Function) =>
|
35
|
|
- _appNavigateToOptionalLocation(dispatch, getState, parseURIString(uri));
|
36
|
|
-}
|
37
|
|
-
|
38
|
|
-/**
|
39
|
|
- * Triggers an in-app navigation to a specific location URI.
|
40
|
|
- *
|
41
|
|
- * @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
42
|
|
- * @param {Function} getState - The redux function that gets/retrieves the redux
|
43
|
|
- * state.
|
44
|
|
- * @param {Object} newLocation - The location URI to navigate to. The value
|
45
|
|
- * cannot be undefined and is assumed to have all properties such as
|
46
|
|
- * {@code host}, {@code contextRoot}, and {@code room} defined. Depending on the
|
47
|
|
- * property, it may have a value equal to {@code undefined} and that may be
|
48
|
|
- * acceptable.
|
49
|
|
- * @private
|
50
|
|
- * @returns {Promise<void>}
|
51
|
|
- */
|
52
|
|
-function _appNavigateToMandatoryLocation(
|
53
|
|
- dispatch: Dispatch<any>, getState: Function,
|
54
|
|
- newLocation: Object
|
55
|
|
-): Promise<void> {
|
56
|
|
- const { room } = newLocation;
|
57
|
|
- const locationURL = new URL(newLocation.toString());
|
58
|
|
-
|
59
|
|
- dispatch(configWillLoad(locationURL));
|
60
|
|
-
|
61
|
|
- return (
|
62
|
|
- _loadConfig(dispatch, getState, newLocation)
|
63
|
|
- .then(
|
64
|
|
- config => loadConfigSettled(/* error */ undefined, config),
|
65
|
|
- error => loadConfigSettled(error, /* config */ undefined))
|
66
|
|
- .then(() => dispatch(setRoom(room))));
|
67
|
|
-
|
68
|
|
- /**
|
69
|
|
- * Notifies that an attempt to load a configuration has completed. Due to
|
70
|
|
- * the asynchronous nature of the loading, the specified {@code config} may
|
71
|
|
- * or may not be required by the time the notification arrives.
|
72
|
|
- *
|
73
|
|
- * @param {string|undefined} error - If the loading has failed, the error
|
74
|
|
- * detailing the cause of the failure.
|
75
|
|
- * @param {Object|undefined} config - If the loading has succeeded, the
|
76
|
|
- * loaded configuration.
|
77
|
|
- * @returns {void}
|
78
|
|
- */
|
79
|
|
- function loadConfigSettled(error, config) {
|
80
|
|
- // Due to the asynchronous nature of the loading, the specified config
|
81
|
|
- // may or may not be required by the time the notification arrives. If
|
82
|
|
- // we receive the config for a location we are no longer interested in,
|
83
|
|
- // "ignore" it - deliver it to the external API, for example, but do not
|
84
|
|
- // proceed with the appNavigate procedure/process.
|
85
|
|
- if (getState()['features/base/config'].locationURL === locationURL) {
|
86
|
|
- dispatch(setLocationURL(locationURL));
|
87
|
|
- dispatch(setConfig(config));
|
88
|
|
- } else {
|
89
|
|
- // eslint-disable-next-line no-param-reassign
|
90
|
|
- error || (error = new Error('Config no longer needed!'));
|
91
|
|
-
|
92
|
|
- // XXX The failure could be, for example, because of a
|
93
|
|
- // certificate-related error. In which case the connection will fail
|
94
|
|
- // later in Strophe anyway.
|
95
|
|
- dispatch(loadConfigError(error, locationURL));
|
96
|
|
-
|
97
|
|
- throw error;
|
98
|
|
- }
|
99
|
|
- }
|
100
|
|
-}
|
101
|
|
-
|
102
|
|
-/**
|
103
|
|
- * Triggers an in-app navigation to a specific or undefined location (URI).
|
104
|
|
- *
|
105
|
|
- * @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
106
|
|
- * @param {Function} getState - The redux function that gets/retrieves the redux
|
107
|
|
- * state.
|
108
|
|
- * @param {Object} location - The location (URI) to navigate to. The value may
|
109
|
|
- * be undefined.
|
110
|
|
- * @private
|
111
|
|
- * @returns {void}
|
112
|
|
- */
|
113
|
|
-function _appNavigateToOptionalLocation(
|
114
|
|
- dispatch: Dispatch<any>, getState: Function,
|
115
|
|
- location: Object) {
|
116
|
|
- // If the specified location (URI) does not identify a host, use the app's
|
117
|
|
- // default.
|
118
|
|
- if (!location || !location.host) {
|
119
|
|
- const defaultLocation = parseURIString(getDefaultURL(getState));
|
120
|
|
-
|
121
|
|
- if (location) {
|
122
|
|
- location.host = defaultLocation.host;
|
123
|
|
-
|
124
|
|
- // FIXME Turn location's host, hostname, and port properties into
|
125
|
|
- // setters in order to reduce the risks of inconsistent state.
|
126
|
|
- location.hostname = defaultLocation.hostname;
|
127
|
|
- location.pathname
|
128
|
|
- = defaultLocation.pathname + location.pathname.substr(1);
|
129
|
|
- location.port = defaultLocation.port;
|
130
|
|
- location.protocol = defaultLocation.protocol;
|
131
|
|
- } else {
|
132
|
|
- // eslint-disable-next-line no-param-reassign
|
133
|
|
- location = defaultLocation;
|
|
34
|
+ return async (dispatch: Dispatch<any>, getState: Function) => {
|
|
35
|
+ let location = parseURIString(uri);
|
|
36
|
+
|
|
37
|
+ // If the specified location (URI) does not identify a host, use the app's
|
|
38
|
+ // default.
|
|
39
|
+ if (!location || !location.host) {
|
|
40
|
+ const defaultLocation = parseURIString(getDefaultURL(getState));
|
|
41
|
+
|
|
42
|
+ if (location) {
|
|
43
|
+ location.host = defaultLocation.host;
|
|
44
|
+
|
|
45
|
+ // FIXME Turn location's host, hostname, and port properties into
|
|
46
|
+ // setters in order to reduce the risks of inconsistent state.
|
|
47
|
+ location.hostname = defaultLocation.hostname;
|
|
48
|
+ location.pathname
|
|
49
|
+ = defaultLocation.pathname + location.pathname.substr(1);
|
|
50
|
+ location.port = defaultLocation.port;
|
|
51
|
+ location.protocol = defaultLocation.protocol;
|
|
52
|
+ } else {
|
|
53
|
+ location = defaultLocation;
|
|
54
|
+ }
|
134
|
55
|
}
|
135
|
|
- }
|
136
|
56
|
|
137
|
|
- location.protocol || (location.protocol = 'https:');
|
|
57
|
+ location.protocol || (location.protocol = 'https:');
|
|
58
|
+ const locationURL = new URL(location.toString());
|
138
|
59
|
|
139
|
|
- return _appNavigateToMandatoryLocation(dispatch, getState, location);
|
140
|
|
-}
|
141
|
|
-
|
142
|
|
-/**
|
143
|
|
- * Loads config.js from a specific host.
|
144
|
|
- *
|
145
|
|
- * @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
146
|
|
- * @param {Function} getState - The redux {@code getState} function.
|
147
|
|
- * @param {Object} location - The location URI which specifies the host to load
|
148
|
|
- * the config.js from.
|
149
|
|
- * @private
|
150
|
|
- * @returns {Promise<Object>}
|
151
|
|
- */
|
152
|
|
-function _loadConfig(
|
153
|
|
- dispatch: Dispatch<any>,
|
154
|
|
- getState: Function,
|
155
|
|
- { contextRoot, host, protocol, room }) {
|
156
|
|
- // XXX As the mobile/React Native app does not employ config on the
|
157
|
|
- // WelcomePage, do not download config.js from the deployment when
|
158
|
|
- // navigating to the WelcomePage - the perceived/visible navigation will be
|
159
|
|
- // faster.
|
160
|
|
- if (!room && typeof APP === 'undefined') {
|
161
|
|
- return Promise.resolve();
|
162
|
|
- }
|
|
60
|
+ dispatch(configWillLoad(locationURL));
|
163
|
61
|
|
164
|
|
- /* eslint-disable no-param-reassign */
|
|
62
|
+ let protocol = location.protocol.toLowerCase();
|
|
63
|
+ const { contextRoot, host, room } = location;
|
165
|
64
|
|
166
|
|
- protocol = protocol.toLowerCase();
|
|
65
|
+ // The React Native app supports an app-specific scheme which is sure to not
|
|
66
|
+ // be supported by fetch.
|
|
67
|
+ protocol !== 'http:' && protocol !== 'https:' && (protocol = 'https:');
|
167
|
68
|
|
168
|
|
- // The React Native app supports an app-specific scheme which is sure to not
|
169
|
|
- // be supported by fetch (or whatever loadConfig utilizes).
|
170
|
|
- protocol !== 'http:' && protocol !== 'https:' && (protocol = 'https:');
|
|
69
|
+ const baseURL = `${protocol}//${host}${contextRoot || '/'}`;
|
|
70
|
+ let url = `${baseURL}config.js`;
|
171
|
71
|
|
172
|
|
- // TDOO userinfo
|
|
72
|
+ // XXX In order to support multiple shards, tell the room to the deployment.
|
|
73
|
+ room && (url += `?room=${room.toLowerCase()}`);
|
173
|
74
|
|
174
|
|
- const baseURL = `${protocol}//${host}${contextRoot || '/'}`;
|
175
|
|
- let url = `${baseURL}config.js`;
|
|
75
|
+ let config;
|
176
|
76
|
|
177
|
|
- // XXX In order to support multiple shards, tell the room to the deployment.
|
178
|
|
- room && (url += `?room=${room.toLowerCase()}`);
|
179
|
|
-
|
180
|
|
- /* eslint-enable no-param-reassign */
|
181
|
|
-
|
182
|
|
- return loadConfig(url).then(
|
183
|
|
- /* onFulfilled */ config => {
|
184
|
|
- // FIXME If the config is no longer needed (in the terms of
|
185
|
|
- // _loadConfig) and that happened because of an intervening
|
186
|
|
- // _loadConfig for the same baseURL, then the unneeded config may be
|
187
|
|
- // stored after the needed config. Anyway.
|
|
77
|
+ try {
|
|
78
|
+ config = await loadConfig(url);
|
188
|
79
|
dispatch(storeConfig(baseURL, config));
|
|
80
|
+ } catch (error) {
|
|
81
|
+ config = restoreConfig(baseURL);
|
189
|
82
|
|
190
|
|
- return config;
|
191
|
|
- },
|
192
|
|
- /* onRejected */ error => {
|
193
|
|
- // XXX The (down)loading of config failed. Try to use the last
|
194
|
|
- // successfully fetched for that deployment. It may not match the
|
195
|
|
- // shard.
|
196
|
|
- const config = restoreConfig(baseURL);
|
|
83
|
+ if (!config) {
|
|
84
|
+ dispatch(loadConfigError(error, locationURL));
|
197
|
85
|
|
198
|
|
- if (config) {
|
199
|
|
- return config;
|
|
86
|
+ return;
|
200
|
87
|
}
|
|
88
|
+ }
|
201
|
89
|
|
202
|
|
- throw error;
|
203
|
|
- });
|
|
90
|
+ if (getState()['features/base/config'].locationURL === locationURL) {
|
|
91
|
+ dispatch(setLocationURL(locationURL));
|
|
92
|
+ dispatch(setConfig(config));
|
|
93
|
+ dispatch(setRoom(room));
|
|
94
|
+ } else {
|
|
95
|
+ dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL));
|
|
96
|
+ }
|
|
97
|
+ };
|
204
|
98
|
}
|
205
|
99
|
|
206
|
100
|
/**
|