2018-05-23 21:10:04 +00:00
|
|
|
// @flow
|
2017-10-02 23:08:07 +00:00
|
|
|
|
2018-05-23 10:48:59 +00:00
|
|
|
import type { Dispatch } from 'redux';
|
|
|
|
|
2017-05-09 05:15:43 +00:00
|
|
|
import { setRoom } from '../base/conference';
|
2018-05-14 20:49:00 +00:00
|
|
|
import {
|
|
|
|
configWillLoad,
|
|
|
|
loadConfigError,
|
|
|
|
restoreConfig,
|
|
|
|
setConfig,
|
|
|
|
storeConfig
|
|
|
|
} from '../base/config';
|
2017-09-06 23:26:33 +00:00
|
|
|
import { setLocationURL } from '../base/connection';
|
2017-04-23 20:14:02 +00:00
|
|
|
import { loadConfig } from '../base/lib-jitsi-meet';
|
2018-07-02 21:22:51 +00:00
|
|
|
import { parseURIString, toURLString } from '../base/util';
|
|
|
|
import { setFatalError } from '../overlay';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-07-11 08:57:07 +00:00
|
|
|
import { getDefaultURL } from './functions';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-07-02 21:22:51 +00:00
|
|
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|
|
|
|
2017-05-26 22:11:33 +00:00
|
|
|
declare var APP: Object;
|
2016-12-14 12:09:29 +00:00
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
2017-05-09 05:15:43 +00:00
|
|
|
* Triggers an in-app navigation to a specific route. Allows navigation to be
|
|
|
|
* abstracted between the mobile/React Native and Web/React applications.
|
2016-10-05 14:36:59 +00:00
|
|
|
*
|
2018-02-27 20:21:28 +00:00
|
|
|
* @param {string|undefined} uri - The URI to which to navigate. It may be a
|
2017-05-09 05:15:43 +00:00
|
|
|
* full URL with an HTTP(S) scheme, a full or partial URI with the app-specific
|
2017-05-26 22:11:33 +00:00
|
|
|
* scheme, or a mere room name.
|
2016-10-05 14:36:59 +00:00
|
|
|
* @returns {Function}
|
|
|
|
*/
|
2017-05-09 05:15:43 +00:00
|
|
|
export function appNavigate(uri: ?string) {
|
2019-03-19 15:42:25 +00:00
|
|
|
return (dispatch: Dispatch<any>, getState: Function) =>
|
2017-07-21 21:12:02 +00:00
|
|
|
_appNavigateToOptionalLocation(dispatch, getState, parseURIString(uri));
|
2017-05-09 05:15:43 +00:00
|
|
|
}
|
2017-04-19 14:52:27 +00:00
|
|
|
|
2017-05-09 05:15:43 +00:00
|
|
|
/**
|
|
|
|
* Triggers an in-app navigation to a specific location URI.
|
|
|
|
*
|
|
|
|
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
|
|
|
* @param {Function} getState - The redux function that gets/retrieves the redux
|
|
|
|
* state.
|
|
|
|
* @param {Object} newLocation - The location URI to navigate to. The value
|
|
|
|
* cannot be undefined and is assumed to have all properties such as
|
2017-08-31 19:16:44 +00:00
|
|
|
* {@code host}, {@code contextRoot}, and {@code room} defined. Depending on the
|
|
|
|
* property, it may have a value equal to {@code undefined} and that may be
|
|
|
|
* acceptable.
|
2017-05-09 05:15:43 +00:00
|
|
|
* @private
|
2017-12-01 21:07:58 +00:00
|
|
|
* @returns {Promise<void>}
|
2017-05-09 05:15:43 +00:00
|
|
|
*/
|
|
|
|
function _appNavigateToMandatoryLocation(
|
2019-03-19 15:42:25 +00:00
|
|
|
dispatch: Dispatch<any>, getState: Function,
|
2017-12-01 21:07:58 +00:00
|
|
|
newLocation: Object
|
|
|
|
): Promise<void> {
|
2017-09-06 23:26:33 +00:00
|
|
|
const { room } = newLocation;
|
2018-05-23 21:10:04 +00:00
|
|
|
const locationURL = new URL(newLocation.toString());
|
2017-09-06 23:26:33 +00:00
|
|
|
|
2018-05-23 21:10:04 +00:00
|
|
|
dispatch(configWillLoad(locationURL));
|
2017-11-29 14:15:57 +00:00
|
|
|
|
2017-09-06 23:26:33 +00:00
|
|
|
return (
|
2018-05-14 20:49:00 +00:00
|
|
|
_loadConfig(dispatch, getState, newLocation)
|
2017-09-06 23:26:33 +00:00
|
|
|
.then(
|
|
|
|
config => loadConfigSettled(/* error */ undefined, config),
|
|
|
|
error => loadConfigSettled(error, /* config */ undefined))
|
|
|
|
.then(() => dispatch(setRoom(room))));
|
2017-05-09 05:15:43 +00:00
|
|
|
|
|
|
|
/**
|
2017-09-01 04:13:59 +00:00
|
|
|
* Notifies that an attempt to load a configuration has completed. Due to
|
2017-10-01 06:35:19 +00:00
|
|
|
* the asynchronous nature of the loading, the specified {@code config} may
|
2017-08-31 19:16:44 +00:00
|
|
|
* or may not be required by the time the notification arrives.
|
2017-05-09 05:15:43 +00:00
|
|
|
*
|
2017-09-01 04:13:59 +00:00
|
|
|
* @param {string|undefined} error - If the loading has failed, the error
|
2017-05-09 05:15:43 +00:00
|
|
|
* detailing the cause of the failure.
|
|
|
|
* @param {Object|undefined} config - If the loading has succeeded, the
|
2017-09-01 04:13:59 +00:00
|
|
|
* loaded configuration.
|
2017-05-09 05:15:43 +00:00
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-09-06 23:26:33 +00:00
|
|
|
function loadConfigSettled(error, config) {
|
2018-05-23 21:10:04 +00:00
|
|
|
// Due to the asynchronous nature of the loading, the specified config
|
2018-07-12 03:57:44 +00:00
|
|
|
// may or may not be required by the time the notification arrives. If
|
|
|
|
// we receive the config for a location we are no longer interested in,
|
|
|
|
// "ignore" it - deliver it to the external API, for example, but do not
|
|
|
|
// proceed with the appNavigate procedure/process.
|
2018-05-23 21:10:04 +00:00
|
|
|
if (getState()['features/base/config'].locationURL === locationURL) {
|
core: refactor routing
Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.
This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.
In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:
- JitsiMeetJS initialization is now synchronous: there is nothing async about
it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
support can now be detected immediately, so take advantage of this to simplify
how we handle unsupported browsers. See [0].
The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.
[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
2018-06-29 07:58:31 +00:00
|
|
|
dispatch(setLocationURL(locationURL));
|
|
|
|
dispatch(setConfig(config));
|
2018-05-23 21:10:04 +00:00
|
|
|
} else {
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
error || (error = new Error('Config no longer needed!'));
|
2017-08-31 19:16:44 +00:00
|
|
|
|
core: refactor routing
Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.
This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.
In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:
- JitsiMeetJS initialization is now synchronous: there is nothing async about
it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
support can now be detected immediately, so take advantage of this to simplify
how we handle unsupported browsers. See [0].
The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.
[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
2018-06-29 07:58:31 +00:00
|
|
|
// XXX The failure could be, for example, because of a
|
2018-07-12 03:57:44 +00:00
|
|
|
// certificate-related error. In which case the connection will fail
|
|
|
|
// later in Strophe anyway.
|
core: refactor routing
Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.
This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.
In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:
- JitsiMeetJS initialization is now synchronous: there is nothing async about
it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
support can now be detected immediately, so take advantage of this to simplify
how we handle unsupported browsers. See [0].
The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.
[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
2018-06-29 07:58:31 +00:00
|
|
|
dispatch(loadConfigError(error, locationURL));
|
2017-04-19 14:52:27 +00:00
|
|
|
|
core: refactor routing
Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.
This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.
In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:
- JitsiMeetJS initialization is now synchronous: there is nothing async about
it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
support can now be detected immediately, so take advantage of this to simplify
how we handle unsupported browsers. See [0].
The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.
[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
2018-06-29 07:58:31 +00:00
|
|
|
throw error;
|
|
|
|
}
|
2017-05-09 05:15:43 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-07 22:06:16 +00:00
|
|
|
|
2017-05-09 05:15:43 +00:00
|
|
|
/**
|
|
|
|
* Triggers an in-app navigation to a specific or undefined location (URI).
|
|
|
|
*
|
|
|
|
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
|
|
|
* @param {Function} getState - The redux function that gets/retrieves the redux
|
|
|
|
* state.
|
|
|
|
* @param {Object} location - The location (URI) to navigate to. The value may
|
|
|
|
* be undefined.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
function _appNavigateToOptionalLocation(
|
2019-03-19 15:42:25 +00:00
|
|
|
dispatch: Dispatch<any>, getState: Function,
|
2017-05-09 05:15:43 +00:00
|
|
|
location: Object) {
|
|
|
|
// If the specified location (URI) does not identify a host, use the app's
|
|
|
|
// default.
|
|
|
|
if (!location || !location.host) {
|
2018-07-11 08:57:07 +00:00
|
|
|
const defaultLocation = parseURIString(getDefaultURL(getState));
|
2017-05-09 05:15:43 +00:00
|
|
|
|
|
|
|
if (location) {
|
|
|
|
location.host = defaultLocation.host;
|
|
|
|
|
|
|
|
// FIXME Turn location's host, hostname, and port properties into
|
|
|
|
// setters in order to reduce the risks of inconsistent state.
|
|
|
|
location.hostname = defaultLocation.hostname;
|
2018-08-14 19:29:32 +00:00
|
|
|
location.pathname
|
|
|
|
= defaultLocation.pathname + location.pathname.substr(1);
|
2017-05-09 05:15:43 +00:00
|
|
|
location.port = defaultLocation.port;
|
|
|
|
location.protocol = defaultLocation.protocol;
|
|
|
|
} else {
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
location = defaultLocation;
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|
2017-05-09 05:15:43 +00:00
|
|
|
}
|
2017-01-25 22:11:44 +00:00
|
|
|
|
2017-07-27 18:50:09 +00:00
|
|
|
location.protocol || (location.protocol = 'https:');
|
2017-05-09 05:15:43 +00:00
|
|
|
|
2017-09-06 23:26:33 +00:00
|
|
|
return _appNavigateToMandatoryLocation(dispatch, getState, location);
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|
|
|
|
|
2017-05-09 05:15:43 +00:00
|
|
|
/**
|
|
|
|
* Loads config.js from a specific host.
|
|
|
|
*
|
2018-05-14 20:49:00 +00:00
|
|
|
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
|
|
|
* @param {Function} getState - The redux {@code getState} function.
|
2017-05-26 22:11:33 +00:00
|
|
|
* @param {Object} location - The location URI which specifies the host to load
|
2017-05-09 05:15:43 +00:00
|
|
|
* the config.js from.
|
2017-08-31 19:16:44 +00:00
|
|
|
* @private
|
2017-05-09 05:15:43 +00:00
|
|
|
* @returns {Promise<Object>}
|
|
|
|
*/
|
2018-05-14 20:49:00 +00:00
|
|
|
function _loadConfig(
|
2019-03-19 15:42:25 +00:00
|
|
|
dispatch: Dispatch<any>,
|
2018-05-14 20:49:00 +00:00
|
|
|
getState: Function,
|
|
|
|
{ contextRoot, host, protocol, room }) {
|
2017-08-25 12:51:57 +00:00
|
|
|
// 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
|
|
|
|
// faster.
|
|
|
|
if (!room && typeof APP === 'undefined') {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2017-08-31 21:38:35 +00:00
|
|
|
/* eslint-disable no-param-reassign */
|
|
|
|
|
|
|
|
protocol = protocol.toLowerCase();
|
2017-05-09 05:15:43 +00:00
|
|
|
|
|
|
|
// The React Native app supports an app-specific scheme which is sure to not
|
|
|
|
// be supported by fetch (or whatever loadConfig utilizes).
|
2017-07-21 21:12:02 +00:00
|
|
|
protocol !== 'http:' && protocol !== 'https:' && (protocol = 'https:');
|
2017-05-09 05:15:43 +00:00
|
|
|
|
|
|
|
// TDOO userinfo
|
|
|
|
|
2017-09-05 20:53:39 +00:00
|
|
|
const baseURL = `${protocol}//${host}${contextRoot || '/'}`;
|
|
|
|
let url = `${baseURL}config.js`;
|
2017-08-31 21:38:35 +00:00
|
|
|
|
|
|
|
// XXX In order to support multiple shards, tell the room to the deployment.
|
|
|
|
room && (url += `?room=${room.toLowerCase()}`);
|
|
|
|
|
|
|
|
/* eslint-enable no-param-reassign */
|
|
|
|
|
2017-12-05 20:34:24 +00:00
|
|
|
return loadConfig(url).then(
|
2017-09-05 20:53:39 +00:00
|
|
|
/* onFulfilled */ config => {
|
2018-05-23 21:10:04 +00:00
|
|
|
// FIXME If the config is no longer needed (in the terms of
|
|
|
|
// _loadConfig) and that happened because of an intervening
|
|
|
|
// _loadConfig for the same baseURL, then the unneeded config may be
|
|
|
|
// stored after the needed config. Anyway.
|
2018-05-14 20:49:00 +00:00
|
|
|
dispatch(storeConfig(baseURL, config));
|
2017-09-05 20:53:39 +00:00
|
|
|
|
|
|
|
return config;
|
|
|
|
},
|
|
|
|
/* onRejected */ error => {
|
|
|
|
// XXX The (down)loading of config failed. Try to use the last
|
|
|
|
// successfully fetched for that deployment. It may not match the
|
|
|
|
// shard.
|
2018-05-14 20:49:00 +00:00
|
|
|
const config = restoreConfig(baseURL);
|
2017-08-28 11:20:53 +00:00
|
|
|
|
2018-05-14 20:49:00 +00:00
|
|
|
if (config) {
|
|
|
|
return config;
|
2017-09-05 20:53:39 +00:00
|
|
|
}
|
2017-08-28 11:20:53 +00:00
|
|
|
|
2017-09-05 20:53:39 +00:00
|
|
|
throw error;
|
|
|
|
});
|
2017-05-09 05:15:43 +00:00
|
|
|
}
|
2018-05-14 20:49:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2019-03-19 15:42:25 +00:00
|
|
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
2018-05-14 20:49:00 +00:00
|
|
|
const { locationURL } = getState()['features/base/connection'];
|
|
|
|
const newLocationURL = new URL(locationURL.href);
|
|
|
|
|
|
|
|
newLocationURL.pathname = pathname;
|
|
|
|
window.location.assign(newLocationURL.toString());
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-07-02 21:22:51 +00:00
|
|
|
/**
|
|
|
|
* Reloads the page.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function reloadNow() {
|
|
|
|
return (dispatch: Dispatch<Function>, getState: Function) => {
|
|
|
|
dispatch(setFatalError(undefined));
|
|
|
|
|
|
|
|
const { locationURL } = getState()['features/base/connection'];
|
|
|
|
|
|
|
|
logger.info(`Reloading the conference using URL: ${locationURL}`);
|
|
|
|
|
|
|
|
if (navigator.product === 'ReactNative') {
|
|
|
|
dispatch(appNavigate(toURLString(locationURL)));
|
|
|
|
} else {
|
|
|
|
dispatch(reloadWithStoredParams());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-05-14 20:49:00 +00:00
|
|
|
/**
|
|
|
|
* Reloads the page by restoring the original URL.
|
|
|
|
*
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function reloadWithStoredParams() {
|
2019-03-19 15:42:25 +00:00
|
|
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
2018-05-14 20:49:00 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|