Files
jitsi-meet/react/features/overlay/components/AbstractPageReloadOverlay.js
Saúl Ibarra Corretgé 38629b437d feat(overlays): refactor logic for selecting current overlay
Do the selection in mapStateToProps so the container itself doesn't need to
receive all the props that each overlay needs.

Each overlay is responsible for fetching their own props and for providing a
"needsDisplay" static method wich will be called with the full redux state and
should return true if the overlay needs displaying.

Also eliminate duplicated state keeping: the connection and conference error
states can be fetched from their respective base features.
2017-11-27 17:45:11 -06:00

261 lines
6.7 KiB
JavaScript

/* @flow */
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
isFatalJitsiConferenceError,
isFatalJitsiConnectionError
} from '../../base/lib-jitsi-meet';
import { randomInt } from '../../base/util';
import { _reloadNow } from '../actions';
import ReloadButton from './ReloadButton';
declare var APP: Object;
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Implements abstract React Component for the page reload overlays.
*/
export default class AbstractPageReloadOverlay extends Component<*, *> {
/**
* AbstractPageReloadOverlay component's property types.
*
* @static
*/
static propTypes = {
dispatch: PropTypes.func,
/**
* The indicator which determines whether the reload was caused by
* network failure.
*
* @public
* @type {boolean}
*/
isNetworkFailure: PropTypes.bool,
/**
* The reason for the error that will cause the reload.
* NOTE: Used by PageReloadOverlay only.
*
* @public
* @type {string}
*/
reason: PropTypes.string,
/**
* The function to translate human-readable text.
*
* @public
* @type {Function}
*/
t: PropTypes.func
};
_interval: ?number
state: {
/**
* The translation key for the title of the overlay.
*
* @type {string}
*/
message: string,
/**
* Current value(time) of the timer.
*
* @type {number}
*/
timeLeft: number,
/**
* How long the overlay dialog will be displayed before the
* conference will be reloaded.
*
* @type {number}
*/
timeoutSeconds: number,
/**
* The translation key for the title of the overlay.
*
* @type {string}
*/
title: string
}
/**
* Check if this overlay needs to be rendered. This function will be called
* by the {@code OverlayContainer}.
*
* @param {Object} state - The redux state.
* @returns {boolean} - True if this overlay needs to be rendered, false
* otherwise.
*/
static needsRender(state) {
const conferenceError = state['features/base/conference'].error;
const connectionError = state['features/base/connection'].error;
return (
(connectionError && isFatalJitsiConnectionError(connectionError))
|| (conferenceError
&& isFatalJitsiConferenceError(conferenceError))
);
}
/**
* Initializes a new AbstractPageReloadOverlay instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
* @public
*/
constructor(props: Object) {
super(props);
/**
* How long the overlay dialog will be displayed, before the conference
* will be reloaded.
*
* @type {number}
*/
const timeoutSeconds = 10 + randomInt(0, 20);
let message, title;
if (this.props.isNetworkFailure) {
title = 'dialog.conferenceDisconnectTitle';
message = 'dialog.conferenceDisconnectMsg';
} else {
title = 'dialog.conferenceReloadTitle';
message = 'dialog.conferenceReloadMsg';
}
this.state = {
message,
timeLeft: timeoutSeconds,
timeoutSeconds,
title
};
}
/**
* React Component method that executes once component is mounted.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
// FIXME (CallStats - issue) This event will not make it to CallStats
// because the log queue is not flushed before "fabric terminated" is
// sent to the backed.
// FIXME: We should dispatch action for this.
APP.conference.logEvent(
'page.reload',
/* value */ undefined,
/* label */ this.props.reason);
logger.info(
`The conference will be reloaded after ${
this.state.timeoutSeconds} seconds.`);
this._interval
= setInterval(
() => {
if (this.state.timeLeft === 0) {
if (this._interval) {
clearInterval(this._interval);
this._interval = undefined;
}
this.props.dispatch(_reloadNow());
} else {
this.setState(prevState => {
return {
timeLeft: prevState.timeLeft - 1
};
});
}
},
1000);
}
/**
* Clears the timer interval.
*
* @inheritdoc
* @returns {void}
*/
componentWillUnmount() {
if (this._interval) {
clearInterval(this._interval);
this._interval = undefined;
}
}
/**
* Renders the button for relaod the page if necessary.
*
* @protected
* @returns {ReactElement|null}
*/
_renderButton() {
if (this.props.isNetworkFailure) {
return (
<ReloadButton textKey = 'dialog.rejoinNow' />
);
}
return null;
}
/**
* Renders the progress bar.
*
* @protected
* @returns {ReactElement}
*/
_renderProgressBar() {
const { timeoutSeconds, timeLeft } = this.state;
const timeRemaining = timeoutSeconds - timeLeft;
const percentageComplete = Math.floor(
(timeRemaining / timeoutSeconds) * 100);
return (
<div
className = 'progress-indicator'
id = 'reloadProgressBar'>
<div
className = 'progress-indicator-fill'
style = {{
width: `${percentageComplete}%`
}} />
</div>
);
}
}
/**
* Maps (parts of) the redux state to the associated component's props.
*
* @param {Object} state - The redux state.
* @returns {{
* isNetworkFailure: boolean,
* reason: string
* }}
* @protected
*/
export function abstractMapStateToProps(state: Object) {
const conferenceError = state['features/base/conference'].error;
const connectionError = state['features/base/connection'].error;
return {
isNetworkFailure: Boolean(connectionError),
reason: (connectionError || conferenceError).message
};
}