// @ts-expect-error import { randomInt } from '@jitsi/js-utils/random'; import React, { Component } from 'react'; import { WithTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { appNavigate, reloadNow } from '../../../../app/actions.native'; import { IReduxState, IStore } from '../../../../app/types'; import { translate } from '../../../i18n/functions'; import { isFatalJitsiConnectionError } from '../../../lib-jitsi-meet/functions.native'; import { hideDialog } from '../../actions'; import logger from '../../logger'; import ConfirmDialog from './ConfirmDialog'; /** * The type of the React {@code Component} props of * {@link PageReloadDialog}. */ interface IProps extends WithTranslation { conferenceError?: Error; configError?: Error; connectionError?: Error; dispatch: IStore['dispatch']; isNetworkFailure: boolean; reason?: string; } /** * The type of the React {@code Component} state of * {@link PageReloadDialog}. */ interface IPageReloadDialogState { timeLeft: number; } /** * Implements a React Component that is shown before the * conference is reloaded. * Shows a warning message and counts down towards the re-load. */ class PageReloadDialog extends Component { _interval?: number; _timeoutSeconds: number; /** * Initializes a new PageReloadOverlay instance. * * @param {Object} props - The read-only properties with which the new * instance is to be initialized. * @public */ constructor(props: IProps) { super(props); this._timeoutSeconds = 10 + randomInt(0, 20); this.state = { timeLeft: this._timeoutSeconds }; this._onCancel = this._onCancel.bind(this); this._onReloadNow = this._onReloadNow.bind(this); this._onReconnecting = this._onReconnecting.bind(this); } /** * React Component method that executes once component is mounted. * * @inheritdoc * @returns {void} */ override componentDidMount() { const { timeLeft } = this.state; logger.info( `The conference will be reloaded after ${timeLeft} seconds.` ); this._interval = setInterval(() => this._onReconnecting(), 1000); } /** * Clears the timer interval. * * @inheritdoc * @returns {void} */ override componentWillUnmount() { if (this._interval) { clearInterval(this._interval); this._interval = undefined; } } /** * Handle clicking of the "Cancel" button. It will navigate back to the * welcome page. * * @private * @returns {boolean} */ _onCancel() { const { dispatch } = this.props; clearInterval(this._interval ?? 0); dispatch(appNavigate(undefined)); return true; } /** * Handles automatic reconnection. * * @private * @returns {void} */ _onReconnecting() { const { dispatch } = this.props; const { timeLeft } = this.state; if (timeLeft === 0) { if (this._interval) { dispatch(hideDialog()); this._onReloadNow(); this._interval = undefined; } } this.setState({ timeLeft: timeLeft - 1 }); } /** * Handle clicking on the "Reload Now" button. It will navigate to the same * conference URL as before immediately, without waiting for the timer to * kick in. * * @private * @returns {boolean} */ _onReloadNow() { const { dispatch } = this.props; clearInterval(this._interval ?? 0); dispatch(reloadNow()); return true; } /** * Implements React's {@link Component#render()}. * * @inheritdoc * @returns {ReactElement} */ override render() { const { isNetworkFailure, t } = this.props; const { timeLeft } = this.state; let message, title; if (isNetworkFailure) { title = 'dialog.conferenceDisconnectTitle'; message = 'dialog.conferenceDisconnectMsg'; } else { title = 'dialog.conferenceReloadTitle'; message = 'dialog.conferenceReloadMsg'; } return ( ); } } /** * Maps (parts of) the redux state to the associated component's props. * * @param {Object} state - The redux state. * @param {IProps} ownProps - The own props of the component. * @protected * @returns {{ * isNetworkFailure: boolean, * reason: string * }} */ function mapStateToProps(state: IReduxState, ownProps: IProps) { const { conferenceError, configError, connectionError } = ownProps; const fatalConnectionError = connectionError && isFatalJitsiConnectionError(connectionError); const isNetworkFailure = Boolean(configError || fatalConnectionError); let reason; if (conferenceError) { reason = `error.conference.${conferenceError.name}`; } else if (connectionError) { reason = `error.conference.${connectionError.name}`; } else if (configError) { reason = `error.config.${configError.name}`; } else { logger.error('No reload reason defined!'); } return { isNetworkFailure, reason }; } export default translate(connect(mapStateToProps)(PageReloadDialog));