From 538af01bf5680356eb8ed7815d9148e7ef0f197f Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Sat, 18 Feb 2017 18:42:11 -0600 Subject: [PATCH] Comply w/ coding style --- conference.js | 18 +- connection.js | 12 +- react/features/app/functions.web.js | 3 +- .../features/base/lib-jitsi-meet/functions.js | 28 ++- react/features/base/util/index.js | 1 + .../conference/components/Conference.web.js | 2 +- react/features/overlay/actionTypes.js | 20 +- react/features/overlay/actions.js | 47 ++-- .../overlay/components/AbstractOverlay.js | 65 +++--- .../overlay/components/OverlayContainer.js | 83 ++++--- .../overlay/components/PageReloadOverlay.js | 209 ++++-------------- .../overlay/components/ReloadTimer.js | 141 ++++++++++++ .../overlay/components/SuspendedOverlay.js | 6 +- .../components/UserMediaPermissionsOverlay.js | 12 +- react/features/overlay/index.js | 4 +- react/features/overlay/reducer.js | 77 ++++--- 16 files changed, 413 insertions(+), 315 deletions(-) create mode 100644 react/features/overlay/components/ReloadTimer.js diff --git a/conference.js b/conference.js index 6584ef310..89f9c1954 100644 --- a/conference.js +++ b/conference.js @@ -21,6 +21,9 @@ import analytics from './modules/analytics/analytics'; import EventEmitter from "events"; import { conferenceFailed } from './react/features/base/conference'; +import { + isFatalJitsiConnectionError +} from './react/features/base/lib-jitsi-meet'; import { mediaPermissionPromptVisibilityChanged, suspendDetected @@ -475,21 +478,18 @@ function disconnect() { /** * Handles CONNECTION_FAILED events from lib-jitsi-meet. - * @param {JitsiMeetJS.connection.error} error the error reported. + * + * @param {JitsiMeetJS.connection.error} error - The reported error. * @returns {void} * @private */ -function _connectionFailedHandler (error) { - switch (error) { - case ConnectionErrors.CONNECTION_DROPPED_ERROR: - case ConnectionErrors.OTHER_ERROR: - case ConnectionErrors.SERVER_ERROR: { - APP.connection.removeEventListener( ConnectionEvents.CONNECTION_FAILED, +function _connectionFailedHandler(error) { + if (isFatalJitsiConnectionError(error)) { + APP.connection.removeEventListener( + ConnectionEvents.CONNECTION_FAILED, _connectionFailedHandler); if (room) room.leave(); - break; - } } } diff --git a/connection.js b/connection.js index 93181c6bc..a827f1728 100644 --- a/connection.js +++ b/connection.js @@ -8,6 +8,9 @@ import { connectionEstablished, connectionFailed } from './react/features/base/connection'; +import { + isFatalJitsiConnectionError +} from './react/features/base/lib-jitsi-meet'; const ConnectionEvents = JitsiMeetJS.events.connection; const ConnectionErrors = JitsiMeetJS.errors.connection; @@ -75,18 +78,13 @@ function connect(id, password, roomName) { connection.addEventListener( ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler); - function connectionFailedHandler (error, errMsg) { + function connectionFailedHandler(error, errMsg) { APP.store.dispatch(connectionFailed(connection, error, errMsg)); - switch (error) { - case ConnectionErrors.CONNECTION_DROPPED_ERROR: - case ConnectionErrors.OTHER_ERROR: - case ConnectionErrors.SERVER_ERROR: { + if (isFatalJitsiConnectionError(error)) { connection.removeEventListener( ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler); - break; - } } } diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js index a83de8128..a8204884e 100644 --- a/react/features/app/functions.web.js +++ b/react/features/app/functions.web.js @@ -1,6 +1,7 @@ -/* global APP, JitsiMeetJS, loggingConfig */ +/* global APP, loggingConfig */ import { isRoomValid } from '../base/conference'; +import JitsiMeetJS from '../base/lib-jitsi-meet'; import { RouteRegistry } from '../base/react'; import { interceptComponent } from '../base/util'; import { Conference } from '../conference'; diff --git a/react/features/base/lib-jitsi-meet/functions.js b/react/features/base/lib-jitsi-meet/functions.js index 03ab8910e..01b7e126e 100644 --- a/react/features/base/lib-jitsi-meet/functions.js +++ b/react/features/base/lib-jitsi-meet/functions.js @@ -1,5 +1,31 @@ import { loadScript } from '../../base/util'; +import JitsiMeetJS from './_'; + +declare var APP: Object; + +const JitsiConnectionErrors = JitsiMeetJS.errors.connection; + +/** + * Determines whether a specific JitsiConnectionErrors instance indicates a + * fatal JitsiConnection error. + * + * FIXME Figure out the category of errors defined by the fucntion and describe + * that category. I've currently named the category fatal because it appears to + * be used in the cases of unrecoverable errors that necessitate a reload. + * + * @param {string} error - The JitsiConnectionErrors instance to + * categorize/classify. + * @returns {boolean} True if the specified JitsiConnectionErrors instance + * indicates a fatal JitsiConnection error; otherwise, false. + */ +export function isFatalJitsiConnectionError(error: string) { + return ( + error === JitsiConnectionErrors.CONNECTION_DROPPED_ERROR + || error === JitsiConnectionErrors.OTHER_ERROR + || error === JitsiConnectionErrors.SERVER_ERROR); +} + /** * Loads config.js file from remote server. * @@ -7,7 +33,7 @@ import { loadScript } from '../../base/util'; * @param {string} path='/config.js' - Relative pah to config.js file. * @returns {Promise} */ -export function loadConfig(host, path = '/config.js') { +export function loadConfig(host: string, path: string = '/config.js') { // Returns config.js file from global scope. We can't use the version that's // being used for the React Native app because the old/current Web app uses // config from the global scope. diff --git a/react/features/base/util/index.js b/react/features/base/util/index.js index f8ac08743..a98be1d9e 100644 --- a/react/features/base/util/index.js +++ b/react/features/base/util/index.js @@ -1,3 +1,4 @@ export * from './interceptComponent'; export * from './loadScript'; +export * from './randomUtil'; export * from './roomnameGenerator'; diff --git a/react/features/conference/components/Conference.web.js b/react/features/conference/components/Conference.web.js index 64d58019e..e9b632fa0 100644 --- a/react/features/conference/components/Conference.web.js +++ b/react/features/conference/components/Conference.web.js @@ -6,7 +6,6 @@ import { connect as reactReduxConnect } from 'react-redux'; import { connect, disconnect } from '../../base/connection'; import { Watermarks } from '../../base/react'; import { FeedbackButton } from '../../feedback'; - import { OverlayContainer } from '../../overlay'; /** @@ -164,6 +163,7 @@ class Conference extends Component { + ); diff --git a/react/features/overlay/actionTypes.js b/react/features/overlay/actionTypes.js index 0060140a8..3d5efb7d4 100644 --- a/react/features/overlay/actionTypes.js +++ b/react/features/overlay/actionTypes.js @@ -1,15 +1,5 @@ import { Symbol } from '../base/react'; -/** - * The type of the Redux action which signals that a suspend was detected. - * - * { - * type: SUSPEND_DETECTED - * } - * @public - */ -export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED'); - /** * The type of the Redux action which signals that the prompt for media * permission is visible or not. @@ -23,3 +13,13 @@ export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED'); */ export const MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED = Symbol('MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED'); + +/** + * The type of the Redux action which signals that a suspend was detected. + * + * { + * type: SUSPEND_DETECTED + * } + * @public + */ +export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED'); diff --git a/react/features/overlay/actions.js b/react/features/overlay/actions.js index 742f63e06..3e7fad350 100644 --- a/react/features/overlay/actions.js +++ b/react/features/overlay/actions.js @@ -1,8 +1,28 @@ import { - SUSPEND_DETECTED, - MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED + MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED, + SUSPEND_DETECTED } from './actionTypes'; -import './reducer'; + +/** + * Signals that the prompt for media permission is visible or not. + * + * @param {boolean} isVisible - If the value is true - the prompt for media + * permission is visible otherwise the value is false/undefined. + * @param {string} browser - The name of the current browser. + * @returns {{ + * type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED, + * browser: {string}, + * isVisible: {boolean} + * }} + * @public + */ +export function mediaPermissionPromptVisibilityChanged(isVisible, browser) { + return { + type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED, + browser, + isVisible + }; +} /** * Signals that suspend was detected. @@ -17,24 +37,3 @@ export function suspendDetected() { type: SUSPEND_DETECTED }; } - -/** - * Signals that the prompt for media permission is visible or not. - * - * @param {boolean} isVisible - If the value is true - the prompt for media - * permission is visible otherwise the value is false/undefined. - * @param {string} browser - The name of the current browser. - * @returns {{ - * type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED, - * isVisible: {boolean}, - * browser: {string} - * }} - * @public - */ -export function mediaPermissionPromptVisibilityChanged(isVisible, browser) { - return { - type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED, - isVisible, - browser - }; -} diff --git a/react/features/overlay/components/AbstractOverlay.js b/react/features/overlay/components/AbstractOverlay.js index 44754ff01..b10df5100 100644 --- a/react/features/overlay/components/AbstractOverlay.js +++ b/react/features/overlay/components/AbstractOverlay.js @@ -3,8 +3,8 @@ import React, { Component } from 'react'; /** - * Implements an abstract React Component for overlay - the components which - * are displayed on top of the application covering the whole screen. + * Implements an abstract React Component for overlay - the components which are + * displayed on top of the application covering the whole screen. * * @abstract */ @@ -21,25 +21,15 @@ export default class AbstractOverlay extends Component { this.state = { /** - * Indicates the css style of the overlay. if true - lighter and - * darker otherwise. + * Indicates the CSS style of the overlay. If true, then ighter; + * darker, otherwise. + * * @type {boolean} */ isLightOverlay: false }; } - /** - * Abstract method which should be used by subclasses to provide the overlay - * content. - * - * @returns {ReactElement|null} - * @protected - */ - _renderOverlayContent() { - return null; - } - /** * This method is executed when comonent is mounted. * @@ -51,6 +41,31 @@ export default class AbstractOverlay extends Component { APP.translation.translateElement($('#overlay')); } + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + const containerClass + = this.state.isLightOverlay + ? 'overlay__container-light' + : 'overlay__container'; + + return ( +
+
+ { + this._renderOverlayContent() + } +
+
+ ); + } + /** * Reloads the page. * @@ -64,23 +79,13 @@ export default class AbstractOverlay extends Component { } /** - * Implements React's {@link Component#render()}. + * Abstract method which should be used by subclasses to provide the overlay + * content. * - * @inheritdoc * @returns {ReactElement|null} + * @protected */ - render() { - const containerClass = this.state.isLightOverlay - ? 'overlay__container-light' : 'overlay__container'; - - return ( -
-
- { this._renderOverlayContent() } -
-
- ); + _renderOverlayContent() { + return null; } } diff --git a/react/features/overlay/components/OverlayContainer.js b/react/features/overlay/components/OverlayContainer.js index 8f993335a..0b76e8398 100644 --- a/react/features/overlay/components/OverlayContainer.js +++ b/react/features/overlay/components/OverlayContainer.js @@ -20,16 +20,20 @@ class OverlayContainer extends Component { static propTypes = { /** * The browser which is used currently. + * * NOTE: Used by UserMediaPermissionsOverlay only. + * * @private * @type {string} */ _browser: React.PropTypes.string, /** - * The indicator which determines whether the status of + * The indicator which determines whether the status of the * JitsiConnection object has been "established" or not. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ @@ -38,7 +42,9 @@ class OverlayContainer extends Component { /** * The indicator which determines whether a critical error for reload * has been received. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ @@ -47,16 +53,20 @@ class OverlayContainer extends Component { /** * The indicator which determines whether the reload was caused by * network failure. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ _isNetworkFailure: React.PropTypes.bool, /** - * The indicator which determines whether the GUM permissions prompt - * is displayed or not. + * The indicator which determines whether the GUM permissions prompt is + * displayed or not. + * * NOTE: Used by UserMediaPermissionsOverlay only. + * * @private * @type {boolean} */ @@ -64,16 +74,20 @@ class OverlayContainer extends Component { /** * The reason for the error that will cause the reload. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {string} */ _reason: React.PropTypes.string, /** - * The indicator which determines whether the GUM permissions prompt - * is displayed or not. + * The indicator which determines whether the GUM permissions prompt is + * displayed or not. + * * NOTE: Used by SuspendedOverlay only. + * * @private * @type {string} */ @@ -91,8 +105,8 @@ class OverlayContainer extends Component { // FIXME: Temporary workaround until everything is moved to react. APP.UI.overlayVisible = (this.props._connectionEstablished && this.props._haveToReload) - || this.props._suspendDetected - || this.props._mediaPermissionPromptVisible; + || this.props._suspendDetected + || this.props._mediaPermissionPromptVisible; } /** @@ -133,80 +147,95 @@ class OverlayContainer extends Component { * * @param {Object} state - The Redux state. * @returns {{ - * _browser: string, - * _connectionEstablished: bool, - * _haveToReload: bool, - * _isNetworkFailure: bool, - * _mediaPermissionPromptVisible: bool, - * _reason: string, - * _suspendDetected: bool + * _browser: string, + * _connectionEstablished: bool, + * _haveToReload: bool, + * _isNetworkFailure: bool, + * _mediaPermissionPromptVisible: bool, + * _reason: string, + * _suspendDetected: bool * }} * @private */ function _mapStateToProps(state) { + const stateFeaturesOverlay = state['features/overlay']; + return { /** * The browser which is used currently. + * * NOTE: Used by UserMediaPermissionsOverlay only. + * * @private * @type {string} */ - _browser: state['features/overlay'].browser, + _browser: stateFeaturesOverlay.browser, /** - * The indicator which determines whether the status of + * The indicator which determines whether the status of the * JitsiConnection object has been "established" or not. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ - _connectionEstablished: - state['features/overlay'].connectionEstablished, + _connectionEstablished: stateFeaturesOverlay.connectionEstablished, /** * The indicator which determines whether a critical error for reload * has been received. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ - _haveToReload: state['features/overlay'].haveToReload, + _haveToReload: stateFeaturesOverlay.haveToReload, /** * The indicator which determines whether the reload was caused by * network failure. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {boolean} */ - _isNetworkFailure: state['features/overlay'].isNetworkFailure, + _isNetworkFailure: stateFeaturesOverlay.isNetworkFailure, /** - * The indicator which determines whether the GUM permissions prompt - * is displayed or not. + * The indicator which determines whether the GUM permissions prompt is + * displayed or not. + * * NOTE: Used by UserMediaPermissionsOverlay only. + * * @private * @type {boolean} */ _mediaPermissionPromptVisible: - state['features/overlay'].mediaPermissionPromptVisible, + stateFeaturesOverlay.mediaPermissionPromptVisible, /** * The reason for the error that will cause the reload. + * * NOTE: Used by PageReloadOverlay only. + * * @private * @type {string} */ - _reason: state['features/overlay'].reason, + _reason: stateFeaturesOverlay.reason, /** - * The indicator which determines whether the GUM permissions prompt - * is displayed or not. + * The indicator which determines whether the GUM permissions prompt is + * displayed or not. + * * NOTE: Used by SuspendedOverlay only. + * * @private * @type {string} */ - _suspendDetected: state['features/overlay'].suspendDetected + _suspendDetected: stateFeaturesOverlay.suspendDetected }; } diff --git a/react/features/overlay/components/PageReloadOverlay.js b/react/features/overlay/components/PageReloadOverlay.js index 5155c22dc..12598be5d 100644 --- a/react/features/overlay/components/PageReloadOverlay.js +++ b/react/features/overlay/components/PageReloadOverlay.js @@ -1,141 +1,18 @@ -/* global APP, AJS */ +import React from 'react'; -import React, { Component } from 'react'; - -import { randomInt } from '../../base/util/randomUtil'; +import { randomInt } from '../../base/util'; import AbstractOverlay from './AbstractOverlay'; +import ReloadTimer from './ReloadTimer'; + +declare var APP: Object; const logger = require('jitsi-meet-logger').getLogger(__filename); /** - * Implements a React Component for the reload timer. Starts counter from - * props.start, adds props.step to the current value on every props.interval - * seconds until the current value reaches props.end. Also displays progress - * bar. - */ -class ReloadTimer extends Component { - /** - * ReloadTimer component's property types. - * - * @static - */ - static propTypes = { - /** - * The end of the timer. When this.state.current reaches this - * value the timer will stop and call onFinish function. - * @public - * @type {number} - */ - end: React.PropTypes.number, - - /** - * The interval in sec for adding this.state.step to this.state.current - * @public - * @type {number} - */ - interval: React.PropTypes.number, - - /** - * The function that will be executed when timer finish (when - * this.state.current === this.props.end) - */ - onFinish: React.PropTypes.func, - - /** - * The start of the timer. The initial value for this.state.current. - * @public - * @type {number} - */ - start: React.PropTypes.number, - - /** - * The value which will be added to this.state.current on every step. - * @public - * @type {number} - */ - step: React.PropTypes.number - } - - /** - * Initializes a new ReloadTimer instance. - * - * @param {Object} props - The read-only properties with which the new - * instance is to be initialized. - * @public - */ - constructor(props) { - super(props); - this.state = { - current: this.props.start, - time: Math.abs(this.props.end - this.props.start) - }; - } - - /** - * React Component method that executes once component is mounted. - * - * @inheritdoc - * @returns {void} - * @protected - */ - componentDidMount() { - AJS.progressBars.update('#reloadProgressBar', 0); - const intervalId = setInterval(() => { - if (this.state.current === this.props.end) { - clearInterval(intervalId); - this.props.onFinish(); - - return; - } - this.setState((prevState, props) => { - return { current: prevState.current + props.step }; - }); - }, Math.abs(this.props.interval) * 1000); - } - - /** - * React Component method that executes once component is updated. - * - * @inheritdoc - * @returns {void} - * @protected - */ - componentDidUpdate() { - AJS.progressBars.update('#reloadProgressBar', - Math.abs(this.state.current - this.props.start) / this.state.time); - } - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement|null} - * @public - */ - render() { - return ( -
-
- -
- - { this.state.current } - - -
- ); - } -} - -/** - * Implements a React Component for page reload overlay. Shown before - * the conference is reloaded. Shows a warning message and counts down towards - * the reload. + * Implements a React Component for page reload overlay. Shown before the + * conference is reloaded. Shows a warning message and counts down towards the + * reload. */ export default class PageReloadOverlay extends AbstractOverlay { /** @@ -172,8 +49,8 @@ export default class PageReloadOverlay extends AbstractOverlay { super(props); /** - * How long the overlay dialog will be - * displayed, before the conference will be reloaded. + * How long the overlay dialog will be displayed, before the conference + * will be reloaded. * @type {number} */ const timeoutSeconds = 10 + randomInt(0, 20); @@ -194,33 +71,59 @@ export default class PageReloadOverlay extends AbstractOverlay { ...this.state, /** - * Indicates the css style of the overlay. if true - lighter and - * darker otherwise. + * Indicates the css style of the overlay. If true, then lighter; + * darker, otherwise. + * * @type {boolean} */ isLightOverlay, /** - * The translation key for the title of the overlay + * The translation key for the title of the overlay. + * * @type {string} */ message, /** - * How long the overlay dialog will be - * displayed, before the conference will be reloaded. + * How long the overlay dialog will be displayed before the + * conference will be reloaded. + * * @type {number} */ timeoutSeconds, /** - * The translation key for the title of the overlay + * The translation key for the title of the overlay. + * * @type {string} */ title }; } + /** + * This method is executed when comonent is mounted. + * + * @inheritdoc + * @returns {void} + */ + componentDidMount() { + super.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.`); + } + /** * Renders the button for relaod the page if necessary. * @@ -229,18 +132,21 @@ export default class PageReloadOverlay extends AbstractOverlay { */ _renderButton() { if (this.props.isNetworkFailure) { - const cName = 'button-control button-control_primary ' - + 'button-control_center'; + const className + = 'button-control button-control_primary button-control_center'; /* eslint-disable react/jsx-handler-names */ return (