|
|
|
@ -1,18 +1,27 @@ |
|
|
|
|
// @flow
|
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
createConnectionEvent, |
|
|
|
|
sendAnalytics |
|
|
|
|
} from '../analytics'; |
|
|
|
|
|
|
|
|
|
import { SET_ROOM } from '../base/conference'; |
|
|
|
|
import { |
|
|
|
|
CONNECTION_ESTABLISHED, |
|
|
|
|
CONNECTION_FAILED, |
|
|
|
|
getURLWithoutParams |
|
|
|
|
} from '../base/connection'; |
|
|
|
|
import { MiddlewareRegistry } from '../base/redux'; |
|
|
|
|
|
|
|
|
|
import { reloadNow } from './actions'; |
|
|
|
|
import { _getRouteToRender } from './getRouteToRender'; |
|
|
|
|
|
|
|
|
|
MiddlewareRegistry.register(store => next => action => { |
|
|
|
|
switch (action.type) { |
|
|
|
|
case CONNECTION_ESTABLISHED: |
|
|
|
|
return _connectionEstablished(store, next, action); |
|
|
|
|
case CONNECTION_FAILED: |
|
|
|
|
return _connectionFailed(store, next, action); |
|
|
|
|
|
|
|
|
|
case SET_ROOM: |
|
|
|
|
return _setRoom(store, next, action); |
|
|
|
@ -62,6 +71,70 @@ function _connectionEstablished(store, next, action) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Navigates to a route in accord with a specific redux state. |
|
|
|
|
* |
|
|
|
|