feat(RN): add a fatal error state which is a catch all
Adds a fatal error state on which will depend whether or not the reload screen is to be displayed. It is to happen when a relevant fatal error action is not claimed by any feature for error recovery (the recoverable flag is not set).
This commit is contained in:
parent
342a00a6af
commit
67d7d4fc14
|
@ -21,3 +21,15 @@ export const MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED');
|
export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the state of the fatal error which shows/hides the reload screen. See
|
||||||
|
* action methods's description for more info about each of the fields.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SET_FATAL_ERROR,
|
||||||
|
* fatalError: ?Object
|
||||||
|
* }
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const SET_FATAL_ERROR = Symbol('SET_FATAL_ERROR');
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { toURLString } from '../base/util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
|
MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
|
||||||
|
SET_FATAL_ERROR,
|
||||||
SUSPEND_DETECTED
|
SUSPEND_DETECTED
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
|
@ -37,6 +38,8 @@ export function mediaPermissionPromptVisibilityChanged(isVisible, browser) {
|
||||||
*/
|
*/
|
||||||
export function _reloadNow() {
|
export function _reloadNow() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
|
dispatch(setFatalError(undefined));
|
||||||
|
|
||||||
const { locationURL } = getState()['features/base/connection'];
|
const { locationURL } = getState()['features/base/connection'];
|
||||||
|
|
||||||
logger.info(`Reloading the conference using URL: ${locationURL}`);
|
logger.info(`Reloading the conference using URL: ${locationURL}`);
|
||||||
|
@ -62,3 +65,22 @@ export function suspendDetected() {
|
||||||
type: SUSPEND_DETECTED
|
type: SUSPEND_DETECTED
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action indicates that an unrecoverable error has occurred and the reload
|
||||||
|
* screen will be displayed or hidden.
|
||||||
|
*
|
||||||
|
* @param {Object} fatalError - A critical error which was not claimed by any
|
||||||
|
* feature for error recovery (the recoverable flag was not set). If
|
||||||
|
* {@code undefined} then any fatal error currently stored will be discarded.
|
||||||
|
* @returns {{
|
||||||
|
* type: SET_FATAL_ERROR,
|
||||||
|
* fatalError: ?Error
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function setFatalError(fatalError) {
|
||||||
|
return {
|
||||||
|
type: SET_FATAL_ERROR,
|
||||||
|
fatalError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,25 @@ export default class AbstractPageReloadOverlay extends Component<*, *> {
|
||||||
* {@code false}, otherwise.
|
* {@code false}, otherwise.
|
||||||
*/
|
*/
|
||||||
static needsRender(state: Object) {
|
static needsRender(state: Object) {
|
||||||
|
// FIXME web does not rely on the 'recoverable' flag set on an error
|
||||||
|
// action, but on a predefined list of fatal errors. Because of that
|
||||||
|
// the value of 'fatalError' which relies on the flag should not be used
|
||||||
|
// on web yet (until conference/connection and their errors handling is
|
||||||
|
// not unified).
|
||||||
|
return typeof APP === 'undefined'
|
||||||
|
? Boolean(state['features/overlay'].fatalError)
|
||||||
|
: this.needsRenderWeb(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether this overlay needs to be rendered (according to a
|
||||||
|
* specific redux state). Called by {@link OverlayContainer}.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The redux state.
|
||||||
|
* @returns {boolean} - If this overlay needs to be rendered, {@code true};
|
||||||
|
* {@code false}, otherwise.
|
||||||
|
*/
|
||||||
|
static needsRenderWeb(state: Object) {
|
||||||
const conferenceError = state['features/base/conference'].error;
|
const conferenceError = state['features/base/conference'].error;
|
||||||
const configError = state['features/base/config'].error;
|
const configError = state['features/base/config'].error;
|
||||||
const connectionError = state['features/base/connection'].error;
|
const connectionError = state['features/base/connection'].error;
|
||||||
|
@ -273,13 +292,14 @@ export default class AbstractPageReloadOverlay extends Component<*, *> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function abstractMapStateToProps(state: Object) {
|
export function abstractMapStateToProps(state: Object) {
|
||||||
const { error: conferenceError } = state['features/base/conference'];
|
|
||||||
const { error: configError } = state['features/base/config'];
|
const { error: configError } = state['features/base/config'];
|
||||||
const { error: connectionError } = state['features/base/connection'];
|
const { error: connectionError } = state['features/base/connection'];
|
||||||
|
const { fatalError } = state['features/overlay'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
details: connectionError ? connectionError.details : undefined,
|
details: fatalError && fatalError.details,
|
||||||
isNetworkFailure: Boolean(configError || connectionError),
|
isNetworkFailure:
|
||||||
reason: (configError || connectionError || conferenceError).message
|
fatalError === configError || fatalError === connectionError,
|
||||||
|
reason: fatalError && fatalError.message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,9 @@ import { appNavigate } from '../../app';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { LoadingIndicator } from '../../base/react';
|
import { LoadingIndicator } from '../../base/react';
|
||||||
|
|
||||||
import { _reloadNow } from '../actions';
|
|
||||||
|
|
||||||
import AbstractPageReloadOverlay, { abstractMapStateToProps }
|
import AbstractPageReloadOverlay, { abstractMapStateToProps }
|
||||||
from './AbstractPageReloadOverlay';
|
from './AbstractPageReloadOverlay';
|
||||||
|
import { _reloadNow, setFatalError } from '../actions';
|
||||||
import OverlayFrame from './OverlayFrame';
|
import OverlayFrame from './OverlayFrame';
|
||||||
import { pageReloadOverlay as styles } from './styles';
|
import { pageReloadOverlay as styles } from './styles';
|
||||||
|
|
||||||
|
@ -42,6 +41,7 @@ class PageReloadOverlay extends AbstractPageReloadOverlay {
|
||||||
*/
|
*/
|
||||||
_onCancel() {
|
_onCancel() {
|
||||||
clearInterval(this._interval);
|
clearInterval(this._interval);
|
||||||
|
this.props.dispatch(setFatalError(undefined));
|
||||||
this.props.dispatch(appNavigate(undefined));
|
this.props.dispatch(appNavigate(undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
||||||
|
import './middleware';
|
||||||
import './reducer';
|
import './reducer';
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { StateListenerRegistry } from '../base/redux';
|
||||||
|
|
||||||
|
import { setFatalError } from './actions';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State listener which emits the {@code fatalErrorOccurred} action which works
|
||||||
|
* as a catch all for critical errors which have not been claimed by any other
|
||||||
|
* feature for error recovery (the recoverable flag is not set).
|
||||||
|
*/
|
||||||
|
StateListenerRegistry.register(
|
||||||
|
/* selector */ state => {
|
||||||
|
const { error: conferenceError } = state['features/base/conference'];
|
||||||
|
const { error: configError } = state['features/base/config'];
|
||||||
|
const { error: connectionError } = state['features/base/connection'];
|
||||||
|
|
||||||
|
return configError || connectionError || conferenceError;
|
||||||
|
},
|
||||||
|
/* listener */ (error, { dispatch }) => {
|
||||||
|
error
|
||||||
|
&& typeof error.recoverable === 'undefined'
|
||||||
|
&& dispatch(setFatalError(error));
|
||||||
|
}
|
||||||
|
);
|
|
@ -4,6 +4,7 @@ import { assign, ReducerRegistry, set } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
|
MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
|
||||||
|
SET_FATAL_ERROR,
|
||||||
SUSPEND_DETECTED
|
SUSPEND_DETECTED
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
|
@ -12,11 +13,14 @@ import {
|
||||||
*
|
*
|
||||||
* FIXME: these pieces of state should probably be in a different place.
|
* FIXME: these pieces of state should probably be in a different place.
|
||||||
*/
|
*/
|
||||||
ReducerRegistry.register('features/overlay', (state = {}, action) => {
|
ReducerRegistry.register('features/overlay', (state = { }, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED:
|
case MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED:
|
||||||
return _mediaPermissionPromptVisibilityChanged(state, action);
|
return _mediaPermissionPromptVisibilityChanged(state, action);
|
||||||
|
|
||||||
|
case SET_FATAL_ERROR:
|
||||||
|
return _setFatalError(state, action);
|
||||||
|
|
||||||
case SUSPEND_DETECTED:
|
case SUSPEND_DETECTED:
|
||||||
return _suspendDetected(state);
|
return _suspendDetected(state);
|
||||||
}
|
}
|
||||||
|
@ -54,3 +58,17 @@ function _mediaPermissionPromptVisibilityChanged(
|
||||||
function _suspendDetected(state) {
|
function _suspendDetected(state) {
|
||||||
return set(state, 'suspendDetected', true);
|
return set(state, 'suspendDetected', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces a specific redux action {@code SET_FATAL_ERROR} of the feature
|
||||||
|
* overlay.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The redux state of the feature overlay.
|
||||||
|
* @param {Error} fatalError - If the value is set it indicates that a fatal
|
||||||
|
* error has occurred and that the reload screen is to be displayed.
|
||||||
|
* @returns {Object}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _setFatalError(state, { fatalError }) {
|
||||||
|
return set(state, 'fatalError', fatalError);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue