diff --git a/css/_inlay.scss b/css/_inlay.scss index 7c32fefb2..7960bb89b 100644 --- a/css/_inlay.scss +++ b/css/_inlay.scss @@ -27,7 +27,86 @@ font-size: 50px; } - &__button { - float: none !important; + &-filmstrip-only { + background-color: $inlayFilmstripOnlyBg; + color: $inlayFilmstripOnlyColor; + margin-left: 20px; + margin-right: 20px; + margin-top: 20px; + bottom: 30px; + position: absolute; + display: flex; + max-height: 120px; + height: 80%; + right: 0px; + border-radius: 4px; + overflow: hidden; + &__content { + padding: 20px; + display: flex; + justify-content: center; + position: relative; + > .button-control { + align-self: center; + } + > #reloadProgressBar { + position: absolute; + left: 0px; + bottom: 0px; + margin-bottom: 0px; + width: 100%; + border-radius: 0px; + > .aui-progress-indicator-value { + border-radius: 0px; + } + } + } + &__title { + font-size: 18px; + font-weight: 600; + } + + &__container { + align-self: center; + } + + &__text { + margin-top: 10px; + font-size: 14px; + font-weight: 600; + } + + &__icon { + font-size: 50px; + align-self: center; + color: $inlayIconColor; + opacity: 0.6; + } + &__icon-container { + text-align: center; + display: flex; + justify-content: center; + position: absolute; + width: 100%; + height: 100%; + top: 0px; + } + + &__avatar-container { + position: relative; + > img { + height: 100%; + } + } + + &__icon-background { + background: $inlayIconBg; + opacity: 0.6; + position: absolute; + width: 100%; + height: 100%; + top: 0px; + } } -} \ No newline at end of file + +} diff --git a/css/components/_button-control.scss b/css/components/_button-control.scss index 5fa000292..43d8d4141 100644 --- a/css/components/_button-control.scss +++ b/css/components/_button-control.scss @@ -57,6 +57,18 @@ } } + &_overlay { + color: $primaryButtonColor; + background-color: $overlayButtonBg; + border-radius: 2px; + border: none; + + &:hover { + background-color: $primaryButtonBackground; + border: none; + } + } + &_primary { background-color: $primaryButtonBackground; border: 1px solid $primaryButtonBackground; @@ -86,4 +98,4 @@ &_center { float: none !important; } -} \ No newline at end of file +} diff --git a/css/overlay/_overlay.scss b/css/overlay/_overlay.scss index 0e40f293d..000bd8025 100644 --- a/css/overlay/_overlay.scss +++ b/css/overlay/_overlay.scss @@ -8,10 +8,16 @@ position: fixed; z-index: $overlayZ; background: $defaultBackground; + &.filmstrip-only { + @include transparentBg($filmStripOnlyOverlayBg, 0.8); + } } &__container-light { @include transparentBg($defaultBackground, 0.7); + &.filmstrip-only { + @include transparentBg($filmStripOnlyOverlayBg, 0.2); + } } &__content { @@ -21,6 +27,11 @@ width: 56%; left: 50%; @include transform(translateX(-50%)); + &.filmstrip-only { + left: 0px; + width: 100%; + @include transform(none); + } &_bottom { position: absolute; @@ -33,4 +44,4 @@ bottom: 24px; width: 100%; } -} \ No newline at end of file +} diff --git a/css/reload_overlay/_reload_overlay.scss b/css/reload_overlay/_reload_overlay.scss index c0f86f5e9..033a9664d 100644 --- a/css/reload_overlay/_reload_overlay.scss +++ b/css/reload_overlay/_reload_overlay.scss @@ -13,4 +13,7 @@ #reloadProgressBar { width: 180px; margin: 5px auto; + > .aui-progress-indicator-value { + background: $reloadProgressBarBg; + } } diff --git a/css/themes/_light.scss b/css/themes/_light.scss index 95210cc13..42b082549 100644 --- a/css/themes/_light.scss +++ b/css/themes/_light.scss @@ -35,10 +35,14 @@ $primaryButtonFontWeight: 400; $buttonShadowColor: #192d4f; +$overlayButtonBg: #0074E0; + /** * Color variables **/ $defaultBackground: #474747; +$filmStripOnlyOverlayBg: #000; +$reloadProgressBarBg: #0074E0; /** * Connection indicator @@ -60,6 +64,10 @@ $dialogTitleFontWeight: 400; **/ $inlayColorBg: lighten($defaultBackground, 20%); $inlayBorderColor: lighten($auiDialogContentBg, 10%); +$inlayIconBg: #000; +$inlayIconColor: #fff; +$inlayFilmstripOnlyColor: #474747; +$inlayFilmstripOnlyBg: #fff; // Main controls $inputBackground: $controlBackground; diff --git a/lang/main.json b/lang/main.json index 2de9bb7f2..8deb0892f 100644 --- a/lang/main.json +++ b/lang/main.json @@ -15,13 +15,13 @@ "defaultLink": "e.g. __url__", "callingName": "__name__", "userMedia": { - "react-nativeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing Allow button", - "chromeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing Allow button", - "androidGrantPermissions": "Please grant permissions to use your camera and microphone by pressing Allow button", - "firefoxGrantPermissions": "Please grant permissions to use your camera and microphone by pressing Share Selected Device button", - "operaGrantPermissions": "Please grant permissions to use your camera and microphone by pressing Allow button", - "iexplorerGrantPermissions": "Please grant permissions to use your camera and microphone by pressing OK button", - "safariGrantPermissions": "Please grant permissions to use your camera and microphone by pressing OK button", + "react-nativeGrantPermissions": "Select Allow when your browser asks for permissions.", + "chromeGrantPermissions": "Select Allow when your browser asks for permissions.", + "androidGrantPermissions": "Select Allow when your browser asks for permissions.", + "firefoxGrantPermissions": "Select Share Selected Device when your browser asks for permissions.", + "operaGrantPermissions": "Select Allow when your browser asks for permissions.", + "iexplorerGrantPermissions": "Select OK when your browser asks for permissions.", + "safariGrantPermissions": "Select OK when your browser asks for permissions.", "nwjsGrantPermissions": "Please grant permissions to use your camera and microphone" }, "keyboardShortcuts": { @@ -87,6 +87,7 @@ }, "suspendedoverlay": { "title": "Your video call was interrupted, because this computer went to sleep.", + "text": "Press Rejoin button to connect back to your conversation.", "rejoinKeyTitle": "Rejoin" }, "toolbar": { @@ -229,12 +230,11 @@ "detectext": "Error when trying to detect desktopsharing extension.", "failtoinstall": "Failed to install desktop sharing extension", "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.", - "conferenceReloadTitle": "Unfortunately, something went wrong", - "conferenceReloadMsg": "We're trying to fix this", - "conferenceDisconnectTitle": "You have been disconnected. You may want to check your network connection.", - "conferenceDisconnectMsg": "Reconnecting in...", - "reconnectNow": "Reconnect now", - "conferenceReloadTimeLeft": "__seconds__ sec.", + "conferenceReloadTitle": "Unfortunately, something went wrong.", + "conferenceReloadMsg": "We're trying to fix this. Reconnecting in __seconds__ sec...", + "conferenceDisconnectTitle": "You have been disconnected.", + "conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in __seconds__ sec...", + "rejoinNow": "Rejoin now", "maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!", "lockTitle": "Lock failed", "lockMessage": "Failed to lock the conference.", diff --git a/modules/URL/ConferenceUrl.js b/modules/URL/ConferenceUrl.js index 5bc075043..bda35ca8b 100644 --- a/modules/URL/ConferenceUrl.js +++ b/modules/URL/ConferenceUrl.js @@ -1,6 +1,6 @@ const logger = require("jitsi-meet-logger").getLogger(__filename); -import { replace } from '../util/helpers'; +import { reload, replace } from '../util/helpers'; /** * The modules stores information about the URL used to start the conference and @@ -67,7 +67,14 @@ export default class ConferenceUrl { * Reloads the conference using original URL with all of the parameters. */ reload() { - logger.info("Reloading the conference using URL: " + this.originalURL); - replace(this.originalURL); + logger.info(`Reloading the conference using URL: ${this.originalURL}`); + + // Check if we are in an iframe and reload with the reload() utility + // because replace() is not working on an iframe. + if(window.self !== window.top) { + reload(); + } else { + replace(this.originalURL); + } } } diff --git a/react/features/overlay/components/AbstractOverlay.js b/react/features/overlay/components/AbstractOverlay.js deleted file mode 100644 index 1c60c317a..000000000 --- a/react/features/overlay/components/AbstractOverlay.js +++ /dev/null @@ -1,80 +0,0 @@ -/* global APP */ - -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. - * - * @abstract - */ -export default class AbstractOverlay extends Component { - /** - * Initializes a new AbstractOverlay instance. - * - * @param {Object} props - The read-only properties with which the new - * instance is to be initialized. - * @public - */ - constructor(props) { - super(props); - - this.state = { - /** - * Indicates the CSS style of the overlay. If true, then ighter; - * darker, otherwise. - * - * @type {boolean} - */ - isLightOverlay: false - }; - } - - /** - * 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. - * - * @returns {void} - * @protected - */ - _reconnectNow() { - // FIXME: In future we should dispatch an action here that will result - // in reload. - APP.ConferenceUrl.reload(); - } - - /** - * Abstract method which should be used by subclasses to provide the overlay - * content. - * - * @returns {ReactElement|null} - * @protected - */ - _renderOverlayContent() { - return null; - } -} diff --git a/react/features/overlay/components/AbstractPageReloadOverlay.js b/react/features/overlay/components/AbstractPageReloadOverlay.js new file mode 100644 index 000000000..f21acc2e4 --- /dev/null +++ b/react/features/overlay/components/AbstractPageReloadOverlay.js @@ -0,0 +1,192 @@ +import React, { Component } from 'react'; + +import { randomInt } from '../../base/util'; + +import { reconnectNow } from '../functions'; +import ReloadButton from './ReloadButton'; + +declare var AJS: Object; +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 = { + /** + * The indicator which determines whether the reload was caused by + * network failure. + * + * @public + * @type {boolean} + */ + isNetworkFailure: React.PropTypes.bool, + + /** + * The reason for the error that will cause the reload. + * NOTE: Used by PageReloadOverlay only. + * + * @public + * @type {string} + */ + reason: React.PropTypes.string + } + + /** + * Initializes a new AbstractPageReloadOverlay instance. + * + * @param {Object} props - The read-only properties with which the new + * instance is to be initialized. + * @public + */ + constructor(props) { + 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 = { + /** + * The translation key for the title of the overlay. + * + * @type {string} + */ + message, + + /** + * Current value(time) of the timer. + * + * @type {number} + */ + timeLeft: timeoutSeconds, + + /** + * 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. + * + * @type {string} + */ + title + }; + } + + /** + * Renders the button for relaod the page if necessary. + * + * @returns {ReactElement|null} + * @private + */ + _renderButton() { + if (this.props.isNetworkFailure) { + return ( + + ); + } + + return null; + } + + /** + * Renders the progress bar. + * + * @returns {ReactElement|null} + * @protected + */ + _renderProgressBar() { + return ( +
+ +
+ ); + } + + /** + * React Component method that executes once component is mounted. + * + * @inheritdoc + * @returns {void} + * @protected + */ + 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.`); + + AJS.progressBars.update('#reloadProgressBar', 0); + + this.intervalId = setInterval(() => { + if (this.state.timeLeft === 0) { + clearInterval(this.intervalId); + reconnectNow(); + } else { + this.setState(prevState => { + return { + timeLeft: prevState.timeLeft - 1 + }; + }); + } + }, 1000); + } + + /** + * React Component method that executes once component is updated. + * + * @inheritdoc + * @returns {void} + * @protected + */ + componentDidUpdate() { + AJS.progressBars.update('#reloadProgressBar', + (this.state.timeoutSeconds - this.state.timeLeft) + / this.state.timeoutSeconds); + } + + /** + * Clears the timer interval. + * + * @inheritdoc + * @returns {void} + */ + componentWillUnmount() { + clearInterval(this.intervalId); + } +} diff --git a/react/features/overlay/components/FilmStripOnlyOverlayFrame.js b/react/features/overlay/components/FilmStripOnlyOverlayFrame.js new file mode 100644 index 000000000..6440058cc --- /dev/null +++ b/react/features/overlay/components/FilmStripOnlyOverlayFrame.js @@ -0,0 +1,133 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; + +import { + Avatar, + getAvatarURL, + getLocalParticipant +} from '../../base/participants'; + +import OverlayFrame from './OverlayFrame'; + +/** + * Implements a React Component for the frame of the overlays in filmstrip only + * mode. + */ +class FilmStripOnlyOverlayFrame extends Component { + /** + * FilmStripOnlyOverlayFrame component's property types. + * + * @static + */ + static propTypes = { + /** + * The source (e.g. URI, URL) of the avatar image of the local + * participant. + * + * @private + */ + _avatar: React.PropTypes.string, + + /** + * The children components to be displayed into the overlay frame for + * filmstrip only mode. + * + * @type {ReactElement} + */ + children: React.PropTypes.node.isRequired, + + /** + * The css class name for the icon that will be displayed over the + * avatar. + * + * @type {string} + */ + icon: React.PropTypes.string, + + /** + * Indicates the css style of the overlay. If true, then lighter; + * darker, otherwise. + * + * @type {boolean} + */ + isLightOverlay: React.PropTypes.bool + } + + /** + * Renders content related to the icon. + * + * @returns {ReactElement|null} + * @private + */ + _renderIcon() { + if (!this.props.icon) { + return null; + } + + const iconClass = `inlay-filmstrip-only__icon ${this.props.icon}`; + const iconBGClass = 'inlay-filmstrip-only__icon-background'; + + return ( +
+
+
+ +
+
+ ); + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + return ( + +
+
+ { + this.props.children + } +
+
+ + { + this._renderIcon() + } +
+
+
+ ); + } +} + +/** + * Maps (parts of) the Redux state to the associated FilmStripOnlyOverlayFrame + * props. + * + * @param {Object} state - The Redux state. + * @private + * @returns {{ + * _avatar: string + * }} + */ +function _mapStateToProps(state) { + const participant + = getLocalParticipant( + state['features/base/participants']); + const { avatarId, avatarUrl, email } = participant || {}; + + return { + _avatar: getAvatarURL({ + avatarId, + avatarUrl, + email, + participantId: participant.id + }) + }; +} + +export default connect(_mapStateToProps)(FilmStripOnlyOverlayFrame); diff --git a/react/features/overlay/components/OverlayContainer.js b/react/features/overlay/components/OverlayContainer.js index 5e6f3d502..2fb9b91cb 100644 --- a/react/features/overlay/components/OverlayContainer.js +++ b/react/features/overlay/components/OverlayContainer.js @@ -1,12 +1,17 @@ -/* global APP */ - import React, { Component } from 'react'; import { connect } from 'react-redux'; +import PageReloadFilmStripOnlyOverlay from './PageReloadFilmStripOnlyOverlay'; import PageReloadOverlay from './PageReloadOverlay'; +import SuspendedFilmStripOnlyOverlay from './SuspendedFilmStripOnlyOverlay'; import SuspendedOverlay from './SuspendedOverlay'; +import UserMediaPermissionsFilmStripOnlyOverlay + from './UserMediaPermissionsFilmStripOnlyOverlay'; import UserMediaPermissionsOverlay from './UserMediaPermissionsOverlay'; +declare var APP: Object; +declare var interfaceConfig: Object; + /** * Implements a React Component that will display the correct overlay when * needed. @@ -94,6 +99,25 @@ class OverlayContainer extends Component { _suspendDetected: React.PropTypes.bool } + /** + * 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 = { + /** + * Indicates whether the film strip only mode is enabled or not. + * + * @type {boolean} + */ + filmStripOnly: interfaceConfig.filmStripOnly + }; + } + /** * React Component method that executes once component is updated. * @@ -117,25 +141,28 @@ class OverlayContainer extends Component { * @public */ render() { + const filmStripOnlyMode = this.state.filmStripOnly; + let overlayComponent, props; + if (this.props._connectionEstablished && this.props._haveToReload) { - return ( - - ); + overlayComponent = filmStripOnlyMode + ? PageReloadFilmStripOnlyOverlay : PageReloadOverlay; + props = { + isNetworkFailure: this.props._isNetworkFailure, + reason: this.props._reason + }; + } else if (this.props._suspendDetected) { + overlayComponent = filmStripOnlyMode + ? SuspendedFilmStripOnlyOverlay : SuspendedOverlay; + } else if (this.props._isMediaPermissionPromptVisible) { + overlayComponent = filmStripOnlyMode + ? UserMediaPermissionsFilmStripOnlyOverlay + : UserMediaPermissionsOverlay; + props = { browser: this.props._browser }; } - if (this.props._suspendDetected) { - return ( - - ); - } - - if (this.props._isMediaPermissionPromptVisible) { - return ( - - ); + if (overlayComponent) { + return React.createElement(overlayComponent, props); } return null; diff --git a/react/features/overlay/components/OverlayFrame.js b/react/features/overlay/components/OverlayFrame.js new file mode 100644 index 000000000..644bdd27b --- /dev/null +++ b/react/features/overlay/components/OverlayFrame.js @@ -0,0 +1,77 @@ +import React, { Component } from 'react'; + +declare var interfaceConfig: Object; + +/** + * Implements a React Component for the frame of the overlays. + */ +export default class OverlayFrame extends Component { + /** + * OverlayFrame component's property types. + * + * @static + */ + static propTypes = { + /** + * The children components to be displayed into the overlay frame. + */ + children: React.PropTypes.node.isRequired, + + /** + * Indicates the css style of the overlay. If true, then lighter; + * darker, otherwise. + * + * @type {boolean} + */ + isLightOverlay: React.PropTypes.bool + } + + /** + * Initializes a new AbstractOverlay instance. + * + * @param {Object} props - The read-only properties with which the new + * instance is to be initialized. + * @public + */ + constructor(props) { + super(props); + + this.state = { + /** + * Indicates whether the film strip only mode is enabled or not. + * + * @type {boolean} + */ + filmStripOnly: interfaceConfig.filmStripOnly + }; + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + let containerClass = this.props.isLightOverlay + ? 'overlay__container-light' : 'overlay__container'; + let contentClass = 'overlay__content'; + + if (this.state.filmStripOnly) { + containerClass += ' filmstrip-only'; + contentClass += ' filmstrip-only'; + } + + return ( +
+
+ { + this.props.children + } +
+
+ ); + } +} diff --git a/react/features/overlay/components/PageReloadFilmStripOnlyOverlay.js b/react/features/overlay/components/PageReloadFilmStripOnlyOverlay.js new file mode 100644 index 000000000..325828e17 --- /dev/null +++ b/react/features/overlay/components/PageReloadFilmStripOnlyOverlay.js @@ -0,0 +1,62 @@ +import React from 'react'; + +import { translate } from '../../base/i18n'; + +import AbstractPageReloadOverlay from './AbstractPageReloadOverlay'; +import FilmStripOnlyOverlayFrame from './FilmStripOnlyOverlayFrame'; + +/** + * Implements a React Component for page reload overlay for filmstrip only + * mode. Shown before the conference is reloaded. Shows a warning message and + * counts down towards the reload. + */ +class PageReloadFilmStripOnlyOverlay extends AbstractPageReloadOverlay { + /** + * PageReloadFilmStripOnlyOverlay component's property types. + * + * @static + */ + static propTypes = { + ...AbstractPageReloadOverlay.propTypes, + + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + const { t } = this.props; + const { message, timeLeft, title } = this.state; + + return ( + +
+
+ { t(title) } +
+
+ { t(message, { seconds: timeLeft }) } +
+
+ { + this._renderButton() + } + { + this._renderProgressBar() + } +
+ ); + } +} + +export default translate(PageReloadFilmStripOnlyOverlay); diff --git a/react/features/overlay/components/PageReloadOverlay.js b/react/features/overlay/components/PageReloadOverlay.js index 54f17d90d..bb971d42a 100644 --- a/react/features/overlay/components/PageReloadOverlay.js +++ b/react/features/overlay/components/PageReloadOverlay.js @@ -1,194 +1,58 @@ import React from 'react'; import { translate } from '../../base/i18n'; -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); +import AbstractPageReloadOverlay from './AbstractPageReloadOverlay'; +import OverlayFrame from './OverlayFrame'; /** * Implements a React Component for page reload overlay. Shown before the * conference is reloaded. Shows a warning message and counts down towards the * reload. */ -class PageReloadOverlay extends AbstractOverlay { +class PageReloadOverlay extends AbstractPageReloadOverlay { /** * PageReloadOverlay component's property types. * * @static */ static propTypes = { - /** - * The indicator which determines whether the reload was caused by - * network failure. - * @public - * @type {boolean} - */ - isNetworkFailure: React.PropTypes.bool, + ...AbstractPageReloadOverlay.propTypes, /** - * The reason for the error that will cause the reload. - * NOTE: Used by PageReloadOverlay only. + * The function to translate human-readable text. + * * @public - * @type {string} + * @type {Function} */ - reason: React.PropTypes.string + t: React.PropTypes.func } /** - * Initializes a new PageReloadOverlay instance. - * - * @param {Object} props - The read-only properties with which the new - * instance is to be initialized. - * @public - */ - constructor(props) { - 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 isLightOverlay, message, title; - - if (this.props.isNetworkFailure) { - title = 'dialog.conferenceDisconnectTitle'; - message = 'dialog.conferenceDisconnectMsg'; - isLightOverlay = true; - } else { - title = 'dialog.conferenceReloadTitle'; - message = 'dialog.conferenceReloadMsg'; - isLightOverlay = false; - } - - this.state = { - ...this.state, - - /** - * 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. - * - * @type {string} - */ - message, - - /** - * 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. - * - * @type {string} - */ - title - }; - } - - /** - * This method is executed when comonent is mounted. + * Implements React's {@link Component#render()}. * * @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. - * * @returns {ReactElement|null} - * @private */ - _renderButton() { - if (this.props.isNetworkFailure) { - const className - = 'button-control button-control_primary button-control_center'; - const { t } = this.props; - - /* eslint-disable react/jsx-handler-names */ - - return ( - - ); - - - /* eslint-enable react/jsx-handler-names */ - } - - return null; - } - - /** - * Constructs overlay body with the warning message and count down towards - * the conference reload. - * - * @returns {ReactElement|null} - * @override - * @protected - */ - _renderOverlayContent() { - const { t } = this.props; - - /* eslint-disable react/jsx-handler-names */ + render() { + const { isNetworkFailure, t } = this.props; + const { message, timeLeft, title } = this.state; return ( -
- - { t(this.state.title) } - - - { t(this.state.message) } - - - { this._renderButton() } -
+ +
+ + { t(title) } + + + { t(message, { seconds: timeLeft }) } + + { this._renderProgressBar() } + { this._renderButton() } +
+
); - - /* eslint-enable react/jsx-handler-names */ } } diff --git a/react/features/overlay/components/ReloadButton.js b/react/features/overlay/components/ReloadButton.js new file mode 100644 index 000000000..d2cb7ed47 --- /dev/null +++ b/react/features/overlay/components/ReloadButton.js @@ -0,0 +1,60 @@ +import React, { Component } from 'react'; + +import { translate } from '../../base/i18n'; + +import { reconnectNow } from '../functions'; + +/** + * Implements a React Component for button for the overlays that will reload + * the page. + */ +class ReloadButton extends Component { + /** + * PageReloadOverlay component's property types. + * + * @static + */ + static propTypes = { + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func, + + /** + * The translation key for the text in the button. + * + * @type {string} + */ + textKey: React.PropTypes.string.isRequired + } + + /** + * Renders the button for relaod the page if necessary. + * + * @returns {ReactElement|null} + * @private + */ + render() { + const className + = 'button-control button-control_overlay button-control_center'; + const { t } = this.props; + + /* eslint-disable react/jsx-handler-names */ + + return ( + + ); + + /* eslint-enable react/jsx-handler-names */ + } + +} + +export default translate(ReloadButton); diff --git a/react/features/overlay/components/SuspendedFilmStripOnlyOverlay.js b/react/features/overlay/components/SuspendedFilmStripOnlyOverlay.js new file mode 100644 index 000000000..d08a47593 --- /dev/null +++ b/react/features/overlay/components/SuspendedFilmStripOnlyOverlay.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react'; + +import { translate, translateToHTML } from '../../base/i18n'; + +import FilmStripOnlyOverlayFrame from './FilmStripOnlyOverlayFrame'; +import ReloadButton from './ReloadButton'; + +/** + * Implements a React Component for suspended overlay for filmstrip only mode. + * Shown when suspended is detected. + */ +class SuspendedFilmStripOnlyOverlay extends Component { + /** + * SuspendedFilmStripOnlyOverlay component's property types. + * + * @static + */ + static propTypes = { + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + const { t } = this.props; + + return ( + +
+
+ { t('suspendedoverlay.title') } +
+
+ { translateToHTML(t, 'suspendedoverlay.text') } +
+
+ +
+ ); + } +} + +export default translate(SuspendedFilmStripOnlyOverlay); diff --git a/react/features/overlay/components/SuspendedOverlay.js b/react/features/overlay/components/SuspendedOverlay.js index 1e417e1c3..3535b57e8 100644 --- a/react/features/overlay/components/SuspendedOverlay.js +++ b/react/features/overlay/components/SuspendedOverlay.js @@ -1,44 +1,58 @@ -import React from 'react'; +import React, { Component } from 'react'; -import { translate } from '../../base/i18n'; +import { translate, translateToHTML } from '../../base/i18n'; -import AbstractOverlay from './AbstractOverlay'; +import OverlayFrame from './OverlayFrame'; +import ReloadButton from './ReloadButton'; /** * Implements a React Component for suspended overlay. Shown when a suspend is * detected. */ -class SuspendedOverlay extends AbstractOverlay { +class SuspendedOverlay extends Component { /** - * Constructs overlay body with the message and a button to rejoin. + * SuspendedOverlay component's property types. * - * @returns {ReactElement|null} - * @override - * @protected + * @static */ - _renderOverlayContent() { - const btnClass = 'inlay__button button-control button-control_primary'; + static propTypes = { + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { const { t } = this.props; - /* eslint-disable react/jsx-handler-names */ - return ( -
- - -

- { t('suspendedoverlay.title') } -

- -
+ +
+ + +

+ { t('suspendedoverlay.title') } +

+ + { + translateToHTML(t, 'suspendedoverlay.title') + } + + +
+
); - - /* eslint-enable react/jsx-handler-names */ } } diff --git a/react/features/overlay/components/UserMediaPermissionsFilmStripOnlyOverlay.js b/react/features/overlay/components/UserMediaPermissionsFilmStripOnlyOverlay.js new file mode 100644 index 000000000..244b9a350 --- /dev/null +++ b/react/features/overlay/components/UserMediaPermissionsFilmStripOnlyOverlay.js @@ -0,0 +1,68 @@ +import React, { Component } from 'react'; + +import { translate, translateToHTML } from '../../base/i18n'; + +import FilmStripOnlyOverlayFrame from './FilmStripOnlyOverlayFrame'; + +/** + * Implements a React Component for overlay with guidance how to proceed with + * gUM prompt. This component will be displayed only for filmstrip only mode. + */ +class UserMediaPermissionsFilmStripOnlyOverlay extends Component { + /** + * UserMediaPermissionsFilmStripOnlyOverlay component's property types. + * + * @static + */ + static propTypes = { + /** + * The browser which is used currently. The text is different for every + * browser. + * + * @public + * @type {string} + */ + browser: React.PropTypes.string, + + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func + } + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement|null} + */ + render() { + const { t } = this.props; + const textKey = `userMedia.${this.props.browser}GrantPermissions`; + + return ( + +
+
+ { + t('startupoverlay.title', + { postProcess: 'resolveAppName' }) + } +
+
+ { + translateToHTML(t, textKey) + } +
+
+
+ ); + } +} + +export default translate(UserMediaPermissionsFilmStripOnlyOverlay); diff --git a/react/features/overlay/components/UserMediaPermissionsOverlay.js b/react/features/overlay/components/UserMediaPermissionsOverlay.js index 193563b98..a6fd8fcec 100644 --- a/react/features/overlay/components/UserMediaPermissionsOverlay.js +++ b/react/features/overlay/components/UserMediaPermissionsOverlay.js @@ -1,16 +1,16 @@ /* global interfaceConfig */ -import React from 'react'; +import React, { Component } from 'react'; import { translate, translateToHTML } from '../../base/i18n'; -import AbstractOverlay from './AbstractOverlay'; +import OverlayFrame from './OverlayFrame'; /** * Implements a React Component for overlay with guidance how to proceed with * gUM prompt. */ -class UserMediaPermissionsOverlay extends AbstractOverlay { +class UserMediaPermissionsOverlay extends Component { /** * UserMediaPermissionsOverlay component's property types. * @@ -24,7 +24,15 @@ class UserMediaPermissionsOverlay extends AbstractOverlay { * @public * @type {string} */ - browser: React.PropTypes.string + browser: React.PropTypes.string, + + /** + * The function to translate human-readable text. + * + * @public + * @type {Function} + */ + t: React.PropTypes.func } /** @@ -48,45 +56,41 @@ class UserMediaPermissionsOverlay extends AbstractOverlay { } /** - * Constructs overlay body with the message with guidance how to proceed - * with gUM prompt. + * Implements React's {@link Component#render()}. * + * @inheritdoc * @returns {ReactElement|null} - * @override - * @protected */ - _renderOverlayContent() { + render() { const { browser, t } = this.props; return ( -
+

{ - t( - 'startupoverlay.title', + t('startupoverlay.title', { postProcess: 'resolveAppName' }) }

{ - translateToHTML( - t, + translateToHTML(t, `userMedia.${browser}GrantPermissions`) }

- { t('startupoverlay.policyText') } + { translateToHTML(t, 'startupoverlay.policyText') }

{ this._renderPolicyLogo() }
-
+ ); } diff --git a/react/features/overlay/functions.js b/react/features/overlay/functions.js new file mode 100644 index 000000000..d9f486233 --- /dev/null +++ b/react/features/overlay/functions.js @@ -0,0 +1,12 @@ +/* global APP */ +/** + * Reloads the page. + * + * @returns {void} + * @protected + */ +export function reconnectNow() { + // FIXME: In future we should dispatch an action here that will result + // in reload. + APP.ConferenceUrl.reload(); +}