[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).
This commit is contained in:
parent
631f51d627
commit
75fe3e3b9d
|
@ -1,7 +1,13 @@
|
|||
/* @flow */
|
||||
|
||||
import { setRoom } from '../base/conference';
|
||||
import { configWillLoad, loadConfigError, setConfig } from '../base/config';
|
||||
import {
|
||||
configWillLoad,
|
||||
loadConfigError,
|
||||
restoreConfig,
|
||||
setConfig,
|
||||
storeConfig
|
||||
} from '../base/config';
|
||||
import { setLocationURL } from '../base/connection';
|
||||
import { loadConfig } from '../base/lib-jitsi-meet';
|
||||
import { parseURIString } from '../base/util';
|
||||
|
@ -24,48 +30,6 @@ export function appNavigate(uri: ?string) {
|
|||
_appNavigateToOptionalLocation(dispatch, getState, parseURIString(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to another page generated by replacing the path in the original URL
|
||||
* with the given path.
|
||||
*
|
||||
* @param {(string)} pathname - The path to navigate to.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function redirectWithStoredParams(pathname: string) {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
const newLocationURL = new URL(locationURL.href);
|
||||
|
||||
newLocationURL.pathname = pathname;
|
||||
window.location.assign(newLocationURL.toString());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the page by restoring the original URL.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function reloadWithStoredParams() {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
const windowLocation = window.location;
|
||||
const oldSearchString = windowLocation.search;
|
||||
|
||||
windowLocation.replace(locationURL.toString());
|
||||
|
||||
if (window.self !== window.top
|
||||
&& locationURL.search === oldSearchString) {
|
||||
// NOTE: Assuming that only the hash or search part of the URL will
|
||||
// be changed!
|
||||
// location.reload will not trigger redirect/reload for iframe when
|
||||
// only the hash params are changed. That's why we need to call
|
||||
// reload in addition to replace.
|
||||
windowLocation.reload();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an in-app navigation to a specific location URI.
|
||||
*
|
||||
|
@ -89,7 +53,7 @@ function _appNavigateToMandatoryLocation(
|
|||
dispatch(configWillLoad(newLocation));
|
||||
|
||||
return (
|
||||
_loadConfig(newLocation)
|
||||
_loadConfig(dispatch, getState, newLocation)
|
||||
.then(
|
||||
config => loadConfigSettled(/* error */ undefined, config),
|
||||
error => loadConfigSettled(error, /* config */ undefined))
|
||||
|
@ -214,12 +178,17 @@ export function appWillUnmount(app: Object) {
|
|||
/**
|
||||
* Loads config.js from a specific host.
|
||||
*
|
||||
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
||||
* @param {Function} getState - The redux {@code getState} function.
|
||||
* @param {Object} location - The location URI which specifies the host to load
|
||||
* the config.js from.
|
||||
* @private
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
function _loadConfig({ contextRoot, host, protocol, room }) {
|
||||
function _loadConfig(
|
||||
dispatch: Dispatch<*>,
|
||||
getState: Function,
|
||||
{ contextRoot, host, protocol, room }) {
|
||||
// XXX As the mobile/React Native app does not employ config on the
|
||||
// WelcomePage, do not download config.js from the deployment when
|
||||
// navigating to the WelcomePage - the perceived/visible navigation will be
|
||||
|
@ -246,21 +215,9 @@ function _loadConfig({ contextRoot, host, protocol, room }) {
|
|||
|
||||
/* eslint-enable no-param-reassign */
|
||||
|
||||
const key = `config.js/${baseURL}`;
|
||||
|
||||
return loadConfig(url).then(
|
||||
/* onFulfilled */ config => {
|
||||
// Try to store the configuration in localStorage. If the deployment
|
||||
// specified 'getroom' as a function, for example, it does not make
|
||||
// sense to and it will not be stored.
|
||||
try {
|
||||
if (typeof window.config === 'undefined'
|
||||
|| window.config !== config) {
|
||||
window.localStorage.setItem(key, JSON.stringify(config));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore the error because the caching is optional.
|
||||
}
|
||||
dispatch(storeConfig(baseURL, config));
|
||||
|
||||
return config;
|
||||
},
|
||||
|
@ -268,23 +225,54 @@ function _loadConfig({ contextRoot, host, protocol, room }) {
|
|||
// XXX The (down)loading of config failed. Try to use the last
|
||||
// successfully fetched for that deployment. It may not match the
|
||||
// shard.
|
||||
let storage;
|
||||
const config = restoreConfig(baseURL);
|
||||
|
||||
try {
|
||||
// XXX Even reading the property localStorage of window may
|
||||
// throw an error (which is user agent-specific behavior).
|
||||
storage = window.localStorage;
|
||||
|
||||
const config = storage.getItem(key);
|
||||
|
||||
if (config) {
|
||||
return JSON.parse(config);
|
||||
}
|
||||
} catch (e) {
|
||||
// Somehow incorrect data ended up in the storage. Clean it up.
|
||||
storage && storage.removeItem(key);
|
||||
if (config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to another page generated by replacing the path in the original URL
|
||||
* with the given path.
|
||||
*
|
||||
* @param {(string)} pathname - The path to navigate to.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function redirectWithStoredParams(pathname: string) {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
const newLocationURL = new URL(locationURL.href);
|
||||
|
||||
newLocationURL.pathname = pathname;
|
||||
window.location.assign(newLocationURL.toString());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the page by restoring the original URL.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function reloadWithStoredParams() {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
const windowLocation = window.location;
|
||||
const oldSearchString = windowLocation.search;
|
||||
|
||||
windowLocation.replace(locationURL.toString());
|
||||
|
||||
if (window.self !== window.top
|
||||
&& locationURL.search === oldSearchString) {
|
||||
// NOTE: Assuming that only the hash or search part of the URL will
|
||||
// be changed!
|
||||
// location.reload will not trigger redirect/reload for iframe when
|
||||
// only the hash params are changed. That's why we need to call
|
||||
// reload in addition to replace.
|
||||
windowLocation.reload();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { addKnownDomains } from '../known-domains';
|
||||
import { parseURIString } from '../util';
|
||||
|
||||
import {
|
||||
CONFIG_WILL_LOAD,
|
||||
LOAD_CONFIG_ERROR,
|
||||
SET_CONFIG
|
||||
} from './actionTypes';
|
||||
import { _CONFIG_STORE_PREFIX } from './constants';
|
||||
import { setConfigFromURLParams } from './functions';
|
||||
|
||||
/**
|
||||
|
@ -87,3 +91,44 @@ export function setConfig(config: Object = {}) {
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a specific Jitsi Meet config.js object into {@code localStorage}.
|
||||
*
|
||||
* @param {string} baseURL - The base URL from which the config.js was
|
||||
* downloaded.
|
||||
* @param {Object} config - The Jitsi Meet config.js to store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function storeConfig(baseURL: string, config: Object) {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
// Try to store the configuration in localStorage. If the deployment
|
||||
// specified 'getroom' as a function, for example, it does not make
|
||||
// sense to and it will not be stored.
|
||||
let b = false;
|
||||
|
||||
try {
|
||||
if (typeof window.config === 'undefined'
|
||||
|| window.config !== config) {
|
||||
window.localStorage.setItem(
|
||||
`${_CONFIG_STORE_PREFIX}/${baseURL}`,
|
||||
JSON.stringify(config));
|
||||
b = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore the error because the caching is optional.
|
||||
}
|
||||
|
||||
// If base/config knows a domain, then the app knows it.
|
||||
if (b) {
|
||||
try {
|
||||
dispatch(addKnownDomains(parseURIString(baseURL).host));
|
||||
} catch (e) {
|
||||
// Ignore the error because the fiddling with "known domains" is
|
||||
// a side effect here.
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* The prefix of the {@code localStorage} key into which {@link storeConfig}
|
||||
* stores and from which {@link restoreConfig} restores.
|
||||
*
|
||||
* @protected
|
||||
* @type string
|
||||
*/
|
||||
export const _CONFIG_STORE_PREFIX = 'config.js';
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { _CONFIG_STORE_PREFIX } from './constants';
|
||||
import parseURLParams from './parseURLParams';
|
||||
|
||||
declare var $: Object;
|
||||
|
@ -238,6 +239,39 @@ function _getWhitelistedJSON(configName, configJSON) {
|
|||
return _.pick(configJSON, WHITELISTED_KEYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a Jitsi Meet config.js from {@code localStorage} if it was
|
||||
* previously downloaded from a specific {@code baseURL} and stored with
|
||||
* {@link storeConfig}.
|
||||
*
|
||||
* @param {string} baseURL - The base URL from which the config.js was
|
||||
* previously downloaded and stored with {@code storeConfig}.
|
||||
* @returns {?Object} The Jitsi Meet config.js which was previously downloaded
|
||||
* from {@code baseURL} and stored with {@code storeConfig} if it was restored;
|
||||
* otherwise, {@code undefined}.
|
||||
*/
|
||||
export function restoreConfig(baseURL: string): ?Object {
|
||||
let storage;
|
||||
const key = `${_CONFIG_STORE_PREFIX}/${baseURL}`;
|
||||
|
||||
try {
|
||||
// XXX Even reading the property localStorage of window may throw an
|
||||
// error (which is user agent-specific behavior).
|
||||
storage = window.localStorage;
|
||||
|
||||
const config = storage.getItem(key);
|
||||
|
||||
if (config) {
|
||||
return JSON.parse(config) || undefined;
|
||||
}
|
||||
} catch (e) {
|
||||
// Somehow incorrect data ended up in the storage. Clean it up.
|
||||
storage && storage.removeItem(key);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* eslint-disable max-params */
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import { APP_WILL_MOUNT } from '../../app';
|
||||
import { addKnownDomains } from '../known-domains';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
import { parseURIString } from '../util';
|
||||
|
||||
import { SET_CONFIG } from './actionTypes';
|
||||
import { _CONFIG_STORE_PREFIX } from './constants';
|
||||
|
||||
/**
|
||||
* The middleware of the feature {@code base/config}.
|
||||
|
@ -13,6 +17,9 @@ import { SET_CONFIG } from './actionTypes';
|
|||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
return _appWillMount(store, next, action);
|
||||
|
||||
case SET_CONFIG:
|
||||
return _setConfig(store, next, action);
|
||||
}
|
||||
|
@ -20,6 +27,59 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the feature {@code base/config} that the {@link APP_WILL_MOUNT}
|
||||
* redux action is being {@code dispatch}ed in a specific redux store.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||
* specified {@code action} in the specified {@code store}.
|
||||
* @param {Action} action - The redux action which is being {@code dispatch}ed
|
||||
* in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {*} The return value of {@code next(action)}.
|
||||
*/
|
||||
function _appWillMount(store, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
// It's an opportune time to transfer the feature base/config's knowledge
|
||||
// about "known domains" (which is local to the feature) to the feature
|
||||
// base/known-domains (which is global to the app).
|
||||
//
|
||||
// XXX Since the feature base/config predates the feature calendar-sync and,
|
||||
// consequently, the feature known-domains, it's possible for the feature
|
||||
// base/config to know of domains which the feature known-domains is yet to
|
||||
// discover.
|
||||
const { localStorage } = window;
|
||||
|
||||
if (localStorage) {
|
||||
const prefix = `${_CONFIG_STORE_PREFIX}/`;
|
||||
const knownDomains = [];
|
||||
|
||||
for (let i = 0; /* localStorage.key(i) */; ++i) {
|
||||
const key = localStorage.key(i);
|
||||
|
||||
if (key) {
|
||||
let baseURL;
|
||||
|
||||
if (key.startsWith(prefix)
|
||||
&& (baseURL = key.substring(prefix.length))) {
|
||||
const uri = parseURIString(baseURL);
|
||||
let host;
|
||||
|
||||
uri && (host = uri.host) && knownDomains.push(host);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
knownDomains.length && store.dispatch(addKnownDomains(knownDomains));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the feature {@code base/config} that the {@link SET_CONFIG} redux
|
||||
* action is being {@code dispatch}ed in a specific redux store.
|
||||
|
|
Loading…
Reference in New Issue