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 (
-
- { t('dialog.reconnectNow') }
-
- );
-
-
- /* 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 (
+
+ { t(this.props.textKey) }
+
+ );
+
+ /* 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.rejoinKeyTitle') }
-
-
+
+
+
+
+
+ { 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();
+}