瀏覽代碼

[RN] If base/config knows a domain, then the app knows it

Knowledge is power, man!

The config.js cache predates the feature base/known-domains.
Technically, it's also able to recall more domains that the feature
recent-list can (because the latter limits its entries).
master
Lyubo Marinov 7 年之前
父節點
當前提交
75fe3e3b9d

+ 60
- 72
react/features/app/actions.js 查看文件

@@ -1,7 +1,13 @@
1 1
 /* @flow */
2 2
 
3 3
 import { setRoom } from '../base/conference';
4
-import { configWillLoad, loadConfigError, setConfig } from '../base/config';
4
+import {
5
+    configWillLoad,
6
+    loadConfigError,
7
+    restoreConfig,
8
+    setConfig,
9
+    storeConfig
10
+} from '../base/config';
5 11
 import { setLocationURL } from '../base/connection';
6 12
 import { loadConfig } from '../base/lib-jitsi-meet';
7 13
 import { parseURIString } from '../base/util';
@@ -24,48 +30,6 @@ export function appNavigate(uri: ?string) {
24 30
         _appNavigateToOptionalLocation(dispatch, getState, parseURIString(uri));
25 31
 }
26 32
 
27
-/**
28
- * Redirects to another page generated by replacing the path in the original URL
29
- * with the given path.
30
- *
31
- * @param {(string)} pathname - The path to navigate to.
32
- * @returns {Function}
33
- */
34
-export function redirectWithStoredParams(pathname: string) {
35
-    return (dispatch: Dispatch<*>, getState: Function) => {
36
-        const { locationURL } = getState()['features/base/connection'];
37
-        const newLocationURL = new URL(locationURL.href);
38
-
39
-        newLocationURL.pathname = pathname;
40
-        window.location.assign(newLocationURL.toString());
41
-    };
42
-}
43
-
44
-/**
45
- * Reloads the page by restoring the original URL.
46
- *
47
- * @returns {Function}
48
- */
49
-export function reloadWithStoredParams() {
50
-    return (dispatch: Dispatch<*>, getState: Function) => {
51
-        const { locationURL } = getState()['features/base/connection'];
52
-        const windowLocation = window.location;
53
-        const oldSearchString = windowLocation.search;
54
-
55
-        windowLocation.replace(locationURL.toString());
56
-
57
-        if (window.self !== window.top
58
-            && locationURL.search === oldSearchString) {
59
-            // NOTE: Assuming that only the hash or search part of the URL will
60
-            // be changed!
61
-            // location.reload will not trigger redirect/reload for iframe when
62
-            // only the hash params are changed. That's why we need to call
63
-            // reload in addition to replace.
64
-            windowLocation.reload();
65
-        }
66
-    };
67
-}
68
-
69 33
 /**
70 34
  * Triggers an in-app navigation to a specific location URI.
71 35
  *
@@ -89,7 +53,7 @@ function _appNavigateToMandatoryLocation(
89 53
     dispatch(configWillLoad(newLocation));
90 54
 
91 55
     return (
92
-        _loadConfig(newLocation)
56
+        _loadConfig(dispatch, getState, newLocation)
93 57
             .then(
94 58
                 config => loadConfigSettled(/* error */ undefined, config),
95 59
                 error => loadConfigSettled(error, /* config */ undefined))
@@ -214,12 +178,17 @@ export function appWillUnmount(app: Object) {
214 178
 /**
215 179
  * Loads config.js from a specific host.
216 180
  *
181
+ * @param {Dispatch} dispatch - The redux {@code dispatch} function.
182
+ * @param {Function} getState - The redux {@code getState} function.
217 183
  * @param {Object} location - The location URI which specifies the host to load
218 184
  * the config.js from.
219 185
  * @private
220 186
  * @returns {Promise<Object>}
221 187
  */
222
-function _loadConfig({ contextRoot, host, protocol, room }) {
188
+function _loadConfig(
189
+        dispatch: Dispatch<*>,
190
+        getState: Function,
191
+        { contextRoot, host, protocol, room }) {
223 192
     // XXX As the mobile/React Native app does not employ config on the
224 193
     // WelcomePage, do not download config.js from the deployment when
225 194
     // navigating to the WelcomePage - the perceived/visible navigation will be
@@ -246,21 +215,9 @@ function _loadConfig({ contextRoot, host, protocol, room }) {
246 215
 
247 216
     /* eslint-enable no-param-reassign */
248 217
 
249
-    const key = `config.js/${baseURL}`;
250
-
251 218
     return loadConfig(url).then(
252 219
         /* onFulfilled */ config => {
253
-            // Try to store the configuration in localStorage. If the deployment
254
-            // specified 'getroom' as a function, for example, it does not make
255
-            // sense to and it will not be stored.
256
-            try {
257
-                if (typeof window.config === 'undefined'
258
-                        || window.config !== config) {
259
-                    window.localStorage.setItem(key, JSON.stringify(config));
260
-                }
261
-            } catch (e) {
262
-                // Ignore the error because the caching is optional.
263
-            }
220
+            dispatch(storeConfig(baseURL, config));
264 221
 
265 222
             return config;
266 223
         },
@@ -268,23 +225,54 @@ function _loadConfig({ contextRoot, host, protocol, room }) {
268 225
             // XXX The (down)loading of config failed. Try to use the last
269 226
             // successfully fetched for that deployment. It may not match the
270 227
             // shard.
271
-            let storage;
228
+            const config = restoreConfig(baseURL);
272 229
 
273
-            try {
274
-                // XXX Even reading the property localStorage of window may
275
-                // throw an error (which is user agent-specific behavior).
276
-                storage = window.localStorage;
277
-
278
-                const config = storage.getItem(key);
279
-
280
-                if (config) {
281
-                    return JSON.parse(config);
282
-                }
283
-            } catch (e) {
284
-                // Somehow incorrect data ended up in the storage. Clean it up.
285
-                storage && storage.removeItem(key);
230
+            if (config) {
231
+                return config;
286 232
             }
287 233
 
288 234
             throw error;
289 235
         });
290 236
 }
237
+
238
+/**
239
+ * Redirects to another page generated by replacing the path in the original URL
240
+ * with the given path.
241
+ *
242
+ * @param {(string)} pathname - The path to navigate to.
243
+ * @returns {Function}
244
+ */
245
+export function redirectWithStoredParams(pathname: string) {
246
+    return (dispatch: Dispatch<*>, getState: Function) => {
247
+        const { locationURL } = getState()['features/base/connection'];
248
+        const newLocationURL = new URL(locationURL.href);
249
+
250
+        newLocationURL.pathname = pathname;
251
+        window.location.assign(newLocationURL.toString());
252
+    };
253
+}
254
+
255
+/**
256
+ * Reloads the page by restoring the original URL.
257
+ *
258
+ * @returns {Function}
259
+ */
260
+export function reloadWithStoredParams() {
261
+    return (dispatch: Dispatch<*>, getState: Function) => {
262
+        const { locationURL } = getState()['features/base/connection'];
263
+        const windowLocation = window.location;
264
+        const oldSearchString = windowLocation.search;
265
+
266
+        windowLocation.replace(locationURL.toString());
267
+
268
+        if (window.self !== window.top
269
+                && locationURL.search === oldSearchString) {
270
+            // NOTE: Assuming that only the hash or search part of the URL will
271
+            // be changed!
272
+            // location.reload will not trigger redirect/reload for iframe when
273
+            // only the hash params are changed. That's why we need to call
274
+            // reload in addition to replace.
275
+            windowLocation.reload();
276
+        }
277
+    };
278
+}

+ 45
- 0
react/features/base/config/actions.js 查看文件

@@ -2,11 +2,15 @@
2 2
 
3 3
 import type { Dispatch } from 'redux';
4 4
 
5
+import { addKnownDomains } from '../known-domains';
6
+import { parseURIString } from '../util';
7
+
5 8
 import {
6 9
     CONFIG_WILL_LOAD,
7 10
     LOAD_CONFIG_ERROR,
8 11
     SET_CONFIG
9 12
 } from './actionTypes';
13
+import { _CONFIG_STORE_PREFIX } from './constants';
10 14
 import { setConfigFromURLParams } from './functions';
11 15
 
12 16
 /**
@@ -87,3 +91,44 @@ export function setConfig(config: Object = {}) {
87 91
         });
88 92
     };
89 93
 }
94
+
95
+/**
96
+ * Stores a specific Jitsi Meet config.js object into {@code localStorage}.
97
+ *
98
+ * @param {string} baseURL - The base URL from which the config.js was
99
+ * downloaded.
100
+ * @param {Object} config - The Jitsi Meet config.js to store.
101
+ * @returns {Function}
102
+ */
103
+export function storeConfig(baseURL: string, config: Object) {
104
+    return (dispatch: Dispatch<*>) => {
105
+        // Try to store the configuration in localStorage. If the deployment
106
+        // specified 'getroom' as a function, for example, it does not make
107
+        // sense to and it will not be stored.
108
+        let b = false;
109
+
110
+        try {
111
+            if (typeof window.config === 'undefined'
112
+                    || window.config !== config) {
113
+                window.localStorage.setItem(
114
+                    `${_CONFIG_STORE_PREFIX}/${baseURL}`,
115
+                    JSON.stringify(config));
116
+                b = true;
117
+            }
118
+        } catch (e) {
119
+            // Ignore the error because the caching is optional.
120
+        }
121
+
122
+        // If base/config knows a domain, then the app knows it.
123
+        if (b) {
124
+            try {
125
+                dispatch(addKnownDomains(parseURIString(baseURL).host));
126
+            } catch (e) {
127
+                // Ignore the error because the fiddling with "known domains" is
128
+                // a side effect here.
129
+            }
130
+        }
131
+
132
+        return b;
133
+    };
134
+}

+ 8
- 0
react/features/base/config/constants.js 查看文件

@@ -0,0 +1,8 @@
1
+/**
2
+ * The prefix of the {@code localStorage} key into which {@link storeConfig}
3
+ * stores and from which {@link restoreConfig} restores.
4
+ *
5
+ * @protected
6
+ * @type string
7
+ */
8
+export const _CONFIG_STORE_PREFIX = 'config.js';

+ 34
- 0
react/features/base/config/functions.js 查看文件

@@ -2,6 +2,7 @@
2 2
 
3 3
 import _ from 'lodash';
4 4
 
5
+import { _CONFIG_STORE_PREFIX } from './constants';
5 6
 import parseURLParams from './parseURLParams';
6 7
 
7 8
 declare var $: Object;
@@ -238,6 +239,39 @@ function _getWhitelistedJSON(configName, configJSON) {
238 239
     return _.pick(configJSON, WHITELISTED_KEYS);
239 240
 }
240 241
 
242
+/**
243
+ * Restores a Jitsi Meet config.js from {@code localStorage} if it was
244
+ * previously downloaded from a specific {@code baseURL} and stored with
245
+ * {@link storeConfig}.
246
+ *
247
+ * @param {string} baseURL - The base URL from which the config.js was
248
+ * previously downloaded and stored with {@code storeConfig}.
249
+ * @returns {?Object} The Jitsi Meet config.js which was previously downloaded
250
+ * from {@code baseURL} and stored with {@code storeConfig} if it was restored;
251
+ * otherwise, {@code undefined}.
252
+ */
253
+export function restoreConfig(baseURL: string): ?Object {
254
+    let storage;
255
+    const key = `${_CONFIG_STORE_PREFIX}/${baseURL}`;
256
+
257
+    try {
258
+        // XXX Even reading the property localStorage of window may throw an
259
+        // error (which is user agent-specific behavior).
260
+        storage = window.localStorage;
261
+
262
+        const config = storage.getItem(key);
263
+
264
+        if (config) {
265
+            return JSON.parse(config) || undefined;
266
+        }
267
+    } catch (e) {
268
+        // Somehow incorrect data ended up in the storage. Clean it up.
269
+        storage && storage.removeItem(key);
270
+    }
271
+
272
+    return undefined;
273
+}
274
+
241 275
 /* eslint-disable max-params */
242 276
 
243 277
 /**

+ 60
- 0
react/features/base/config/middleware.js 查看文件

@@ -1,8 +1,12 @@
1 1
 // @flow
2 2
 
3
+import { APP_WILL_MOUNT } from '../../app';
4
+import { addKnownDomains } from '../known-domains';
3 5
 import { MiddlewareRegistry } from '../redux';
6
+import { parseURIString } from '../util';
4 7
 
5 8
 import { SET_CONFIG } from './actionTypes';
9
+import { _CONFIG_STORE_PREFIX } from './constants';
6 10
 
7 11
 /**
8 12
  * The middleware of the feature {@code base/config}.
@@ -13,6 +17,9 @@ import { SET_CONFIG } from './actionTypes';
13 17
  */
14 18
 MiddlewareRegistry.register(store => next => action => {
15 19
     switch (action.type) {
20
+    case APP_WILL_MOUNT:
21
+        return _appWillMount(store, next, action);
22
+
16 23
     case SET_CONFIG:
17 24
         return _setConfig(store, next, action);
18 25
     }
@@ -20,6 +27,59 @@ MiddlewareRegistry.register(store => next => action => {
20 27
     return next(action);
21 28
 });
22 29
 
30
+/**
31
+ * Notifies the feature {@code base/config} that the {@link APP_WILL_MOUNT}
32
+ * redux action is being {@code dispatch}ed in a specific redux store.
33
+ *
34
+ * @param {Store} store - The redux store in which the specified {@code action}
35
+ * is being dispatched.
36
+ * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
37
+ * specified {@code action} in the specified {@code store}.
38
+ * @param {Action} action - The redux action which is being {@code dispatch}ed
39
+ * in the specified {@code store}.
40
+ * @private
41
+ * @returns {*} The return value of {@code next(action)}.
42
+ */
43
+function _appWillMount(store, next, action) {
44
+    const result = next(action);
45
+
46
+    // It's an opportune time to transfer the feature base/config's knowledge
47
+    // about "known domains" (which is local to the feature) to the feature
48
+    // base/known-domains (which is global to the app).
49
+    //
50
+    // XXX Since the feature base/config predates the feature calendar-sync and,
51
+    // consequently, the feature known-domains, it's possible for the feature
52
+    // base/config to know of domains which the feature known-domains is yet to
53
+    // discover.
54
+    const { localStorage } = window;
55
+
56
+    if (localStorage) {
57
+        const prefix = `${_CONFIG_STORE_PREFIX}/`;
58
+        const knownDomains = [];
59
+
60
+        for (let i = 0; /* localStorage.key(i) */; ++i) {
61
+            const key = localStorage.key(i);
62
+
63
+            if (key) {
64
+                let baseURL;
65
+
66
+                if (key.startsWith(prefix)
67
+                        && (baseURL = key.substring(prefix.length))) {
68
+                    const uri = parseURIString(baseURL);
69
+                    let host;
70
+
71
+                    uri && (host = uri.host) && knownDomains.push(host);
72
+                }
73
+            } else {
74
+                break;
75
+            }
76
+        }
77
+        knownDomains.length && store.dispatch(addKnownDomains(knownDomains));
78
+    }
79
+
80
+    return result;
81
+}
82
+
23 83
 /**
24 84
  * Notifies the feature {@code base/config} that the {@link SET_CONFIG} redux
25 85
  * action is being {@code dispatch}ed in a specific redux store.

Loading…
取消
儲存