2017-10-17 22:10:42 +00:00
|
|
|
// @flow
|
|
|
|
|
2020-01-02 21:55:46 +00:00
|
|
|
import {
|
|
|
|
createConnectionEvent,
|
|
|
|
sendAnalytics
|
|
|
|
} from '../analytics';
|
2017-07-06 18:39:59 +00:00
|
|
|
import { SET_ROOM } from '../base/conference';
|
2017-06-01 18:43:57 +00:00
|
|
|
import {
|
|
|
|
CONNECTION_ESTABLISHED,
|
2020-01-02 21:55:46 +00:00
|
|
|
CONNECTION_FAILED,
|
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
|
|
|
getURLWithoutParams
|
2017-06-01 18:43:57 +00:00
|
|
|
} from '../base/connection';
|
|
|
|
import { MiddlewareRegistry } from '../base/redux';
|
|
|
|
|
2020-01-02 21:55:46 +00:00
|
|
|
import { reloadNow } from './actions';
|
2018-07-12 03:57:44 +00:00
|
|
|
import { _getRouteToRender } from './getRouteToRender';
|
2017-10-17 22:09:52 +00:00
|
|
|
|
2017-06-01 18:43:57 +00:00
|
|
|
MiddlewareRegistry.register(store => next => action => {
|
|
|
|
switch (action.type) {
|
|
|
|
case CONNECTION_ESTABLISHED:
|
|
|
|
return _connectionEstablished(store, next, action);
|
2020-01-02 21:55:46 +00:00
|
|
|
case CONNECTION_FAILED:
|
|
|
|
return _connectionFailed(store, next, action);
|
2017-07-06 18:39:59 +00:00
|
|
|
|
|
|
|
case SET_ROOM:
|
2017-07-12 12:22:23 +00:00
|
|
|
return _setRoom(store, next, action);
|
2017-06-01 18:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return next(action);
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notifies the feature app that the action {@link CONNECTION_ESTABLISHED} is
|
2017-07-06 18:24:24 +00:00
|
|
|
* being dispatched within a specific redux {@code store}.
|
2017-06-01 18:43:57 +00:00
|
|
|
*
|
2017-07-06 18:24:24 +00:00
|
|
|
* @param {Store} store - The redux store in which the specified {@code action}
|
2017-06-01 18:43:57 +00:00
|
|
|
* is being dispatched.
|
2017-07-06 18:24:24 +00:00
|
|
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
2017-06-01 18:43:57 +00:00
|
|
|
* specified {@code action} to the specified {@code store}.
|
2017-07-06 18:24:24 +00:00
|
|
|
* @param {Action} action - The redux action {@code CONNECTION_ESTABLISHED}
|
2017-06-01 18:43:57 +00:00
|
|
|
* which is being dispatched in the specified {@code store}.
|
|
|
|
* @private
|
|
|
|
* @returns {Object} The new state that is the result of the reduction of the
|
|
|
|
* specified {@code action}.
|
|
|
|
*/
|
|
|
|
function _connectionEstablished(store, next, action) {
|
|
|
|
const result = next(action);
|
|
|
|
|
|
|
|
// In the Web app we explicitly do not want to display the hash and
|
|
|
|
// query/search URL params. Unfortunately, window.location and, more
|
|
|
|
// importantly, its params are used not only in jitsi-meet but also in
|
|
|
|
// lib-jitsi-meet. Consequenlty, the time to remove the params is
|
|
|
|
// determined by when no one needs them anymore.
|
|
|
|
const { history, location } = window;
|
|
|
|
|
|
|
|
if (history
|
|
|
|
&& location
|
|
|
|
&& history.length
|
|
|
|
&& typeof history.replaceState === 'function') {
|
|
|
|
const replacement = getURLWithoutParams(location);
|
|
|
|
|
|
|
|
if (location !== replacement) {
|
|
|
|
history.replaceState(
|
|
|
|
history.state,
|
|
|
|
(document && document.title) || '',
|
|
|
|
replacement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2017-07-06 18:39:59 +00:00
|
|
|
|
2020-01-02 21:55:46 +00:00
|
|
|
/**
|
|
|
|
* CONNECTION_FAILED action side effects.
|
|
|
|
*
|
|
|
|
* @param {Object} store - The Redux store.
|
|
|
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the specified {@code action} to
|
|
|
|
* the specified {@code store}.
|
|
|
|
* @param {Action} action - The redux action {@code CONNECTION_FAILED} which is being dispatched in the specified
|
|
|
|
* {@code store}.
|
|
|
|
* @returns {Object}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
function _connectionFailed({ dispatch, getState }, next, action) {
|
|
|
|
// In the case of a split-brain error, reload early and prevent further
|
|
|
|
// handling of the action.
|
|
|
|
if (_isMaybeSplitBrainError(getState, action)) {
|
|
|
|
dispatch(reloadNow());
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return next(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether or not a CONNECTION_FAILED action is for a possible split brain error. A split brain error occurs
|
|
|
|
* when at least two users join a conference on different bridges. It is assumed the split brain scenario occurs very
|
|
|
|
* early on in the call.
|
|
|
|
*
|
|
|
|
* @param {Function} getState - The redux function for fetching the current state.
|
|
|
|
* @param {Action} action - The redux action {@code CONNECTION_FAILED} which is being dispatched in the specified
|
|
|
|
* {@code store}.
|
|
|
|
* @private
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
function _isMaybeSplitBrainError(getState, action) {
|
|
|
|
const { error } = action;
|
|
|
|
const isShardChangedError = error
|
|
|
|
&& error.message === 'item-not-found'
|
|
|
|
&& error.details
|
|
|
|
&& error.details.shard_changed;
|
|
|
|
|
|
|
|
if (isShardChangedError) {
|
|
|
|
const state = getState();
|
|
|
|
const { timeEstablished } = state['features/base/connection'];
|
|
|
|
const { _immediateReloadThreshold } = state['features/base/config'];
|
|
|
|
|
|
|
|
const timeSinceConnectionEstablished = timeEstablished && Date.now() - timeEstablished;
|
|
|
|
const reloadThreshold = typeof _immediateReloadThreshold === 'number' ? _immediateReloadThreshold : 1500;
|
|
|
|
|
|
|
|
const isWithinSplitBrainThreshold = !timeEstablished || timeSinceConnectionEstablished <= reloadThreshold;
|
|
|
|
|
|
|
|
sendAnalytics(createConnectionEvent('failed', {
|
|
|
|
...error,
|
|
|
|
connectionEstablished: timeEstablished,
|
|
|
|
splitBrain: isWithinSplitBrainThreshold,
|
|
|
|
timeSinceConnectionEstablished
|
|
|
|
}));
|
|
|
|
|
|
|
|
return isWithinSplitBrainThreshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-06 18:39:59 +00:00
|
|
|
/**
|
|
|
|
* Navigates to a route in accord with a specific redux state.
|
|
|
|
*
|
2017-07-12 12:22:23 +00:00
|
|
|
* @param {Store} store - The redux store which determines/identifies the route
|
2017-07-06 18:39:59 +00:00
|
|
|
* to navigate to.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-08-17 13:50:49 +00:00
|
|
|
function _navigate({ getState }) {
|
2017-07-12 12:22:23 +00:00
|
|
|
const state = getState();
|
2018-07-11 09:42:43 +00:00
|
|
|
const { app } = state['features/base/app'];
|
2018-04-16 04:14:50 +00:00
|
|
|
|
2018-07-12 03:57:44 +00:00
|
|
|
_getRouteToRender(state).then(route => app._navigate(route));
|
2017-07-12 12:22:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notifies the feature app that the action {@link SET_ROOM} is being dispatched
|
|
|
|
* within a specific redux {@code store}.
|
2017-07-06 18:39:59 +00:00
|
|
|
*
|
|
|
|
* @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} to the specified {@code store}.
|
2017-07-12 12:22:23 +00:00
|
|
|
* @param {Action} action - The redux action, {@code SET_ROOM}, which is being
|
|
|
|
* dispatched in the specified {@code store}.
|
2017-07-06 18:39:59 +00:00
|
|
|
* @private
|
|
|
|
* @returns {Object} The new state that is the result of the reduction of the
|
|
|
|
* specified {@code action}.
|
|
|
|
*/
|
2017-07-12 12:22:23 +00:00
|
|
|
function _setRoom(store, next, action) {
|
2017-07-06 18:39:59 +00:00
|
|
|
const result = next(action);
|
|
|
|
|
2017-07-12 12:22:23 +00:00
|
|
|
_navigate(store);
|
2017-07-06 18:39:59 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|