Merge pull request #1393 from jitsi/filmstrip_overlays
Filmstrip overlays
This commit is contained in:
commit
e8068cf5ac
|
@ -27,7 +27,86 @@
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__button {
|
&-filmstrip-only {
|
||||||
float: none !important;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_overlay {
|
||||||
|
color: $primaryButtonColor;
|
||||||
|
background-color: $overlayButtonBg;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $primaryButtonBackground;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&_primary {
|
&_primary {
|
||||||
background-color: $primaryButtonBackground;
|
background-color: $primaryButtonBackground;
|
||||||
border: 1px solid $primaryButtonBackground;
|
border: 1px solid $primaryButtonBackground;
|
||||||
|
@ -86,4 +98,4 @@
|
||||||
&_center {
|
&_center {
|
||||||
float: none !important;
|
float: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,16 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: $overlayZ;
|
z-index: $overlayZ;
|
||||||
background: $defaultBackground;
|
background: $defaultBackground;
|
||||||
|
&.filmstrip-only {
|
||||||
|
@include transparentBg($filmStripOnlyOverlayBg, 0.8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__container-light {
|
&__container-light {
|
||||||
@include transparentBg($defaultBackground, 0.7);
|
@include transparentBg($defaultBackground, 0.7);
|
||||||
|
&.filmstrip-only {
|
||||||
|
@include transparentBg($filmStripOnlyOverlayBg, 0.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
|
@ -21,6 +27,11 @@
|
||||||
width: 56%;
|
width: 56%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@include transform(translateX(-50%));
|
@include transform(translateX(-50%));
|
||||||
|
&.filmstrip-only {
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
@include transform(none);
|
||||||
|
}
|
||||||
|
|
||||||
&_bottom {
|
&_bottom {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -33,4 +44,4 @@
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,7 @@
|
||||||
#reloadProgressBar {
|
#reloadProgressBar {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
margin: 5px auto;
|
margin: 5px auto;
|
||||||
|
> .aui-progress-indicator-value {
|
||||||
|
background: $reloadProgressBarBg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,14 @@ $primaryButtonFontWeight: 400;
|
||||||
|
|
||||||
$buttonShadowColor: #192d4f;
|
$buttonShadowColor: #192d4f;
|
||||||
|
|
||||||
|
$overlayButtonBg: #0074E0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Color variables
|
* Color variables
|
||||||
**/
|
**/
|
||||||
$defaultBackground: #474747;
|
$defaultBackground: #474747;
|
||||||
|
$filmStripOnlyOverlayBg: #000;
|
||||||
|
$reloadProgressBarBg: #0074E0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection indicator
|
* Connection indicator
|
||||||
|
@ -60,6 +64,10 @@ $dialogTitleFontWeight: 400;
|
||||||
**/
|
**/
|
||||||
$inlayColorBg: lighten($defaultBackground, 20%);
|
$inlayColorBg: lighten($defaultBackground, 20%);
|
||||||
$inlayBorderColor: lighten($auiDialogContentBg, 10%);
|
$inlayBorderColor: lighten($auiDialogContentBg, 10%);
|
||||||
|
$inlayIconBg: #000;
|
||||||
|
$inlayIconColor: #fff;
|
||||||
|
$inlayFilmstripOnlyColor: #474747;
|
||||||
|
$inlayFilmstripOnlyBg: #fff;
|
||||||
|
|
||||||
// Main controls
|
// Main controls
|
||||||
$inputBackground: $controlBackground;
|
$inputBackground: $controlBackground;
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
"defaultLink": "e.g. __url__",
|
"defaultLink": "e.g. __url__",
|
||||||
"callingName": "__name__",
|
"callingName": "__name__",
|
||||||
"userMedia": {
|
"userMedia": {
|
||||||
"react-nativeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
"chromeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
"androidGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
"androidGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
"firefoxGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Share Selected Device</i></b> button",
|
"firefoxGrantPermissions": "Select <b><i>Share Selected Device</i></b> when your browser asks for permissions.",
|
||||||
"operaGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
"operaGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
"iexplorerGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>OK</i></b> button",
|
"iexplorerGrantPermissions": "Select <b><i>OK</i></b> when your browser asks for permissions.",
|
||||||
"safariGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>OK</i></b> button",
|
"safariGrantPermissions": "Select <b><i>OK</i></b> when your browser asks for permissions.",
|
||||||
"nwjsGrantPermissions": "Please grant permissions to use your camera and microphone"
|
"nwjsGrantPermissions": "Please grant permissions to use your camera and microphone"
|
||||||
},
|
},
|
||||||
"keyboardShortcuts": {
|
"keyboardShortcuts": {
|
||||||
|
@ -87,6 +87,7 @@
|
||||||
},
|
},
|
||||||
"suspendedoverlay": {
|
"suspendedoverlay": {
|
||||||
"title": "Your video call was interrupted, because this computer went to sleep.",
|
"title": "Your video call was interrupted, because this computer went to sleep.",
|
||||||
|
"text": "Press <i>Rejoin</i> button to connect back to your conversation.",
|
||||||
"rejoinKeyTitle": "Rejoin"
|
"rejoinKeyTitle": "Rejoin"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
|
@ -229,12 +230,11 @@
|
||||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||||
"failtoinstall": "Failed to install desktop sharing extension",
|
"failtoinstall": "Failed to install desktop sharing extension",
|
||||||
"failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
|
"failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
|
||||||
"conferenceReloadTitle": "Unfortunately, something went wrong",
|
"conferenceReloadTitle": "Unfortunately, something went wrong.",
|
||||||
"conferenceReloadMsg": "We're trying to fix this",
|
"conferenceReloadMsg": "We're trying to fix this. Reconnecting in __seconds__ sec...",
|
||||||
"conferenceDisconnectTitle": "You have been disconnected. You may want to check your network connection.",
|
"conferenceDisconnectTitle": "You have been disconnected.",
|
||||||
"conferenceDisconnectMsg": "Reconnecting in...",
|
"conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in __seconds__ sec...",
|
||||||
"reconnectNow": "Reconnect now",
|
"rejoinNow": "Rejoin now",
|
||||||
"conferenceReloadTimeLeft": "__seconds__ sec.",
|
|
||||||
"maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!",
|
"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",
|
"lockTitle": "Lock failed",
|
||||||
"lockMessage": "Failed to lock the conference.",
|
"lockMessage": "Failed to lock the conference.",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
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
|
* 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.
|
* Reloads the conference using original URL with all of the parameters.
|
||||||
*/
|
*/
|
||||||
reload() {
|
reload() {
|
||||||
logger.info("Reloading the conference using URL: " + this.originalURL);
|
logger.info(`Reloading the conference using URL: ${this.originalURL}`);
|
||||||
replace(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (
|
|
||||||
<div
|
|
||||||
className = { containerClass }
|
|
||||||
id = 'overlay'>
|
|
||||||
<div className = 'overlay__content'>
|
|
||||||
{
|
|
||||||
this._renderOverlayContent()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 (
|
||||||
|
<ReloadButton textKey = 'dialog.rejoinNow' />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the progress bar.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement|null}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_renderProgressBar() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className = 'aui-progress-indicator'
|
||||||
|
id = 'reloadProgressBar'>
|
||||||
|
<span className = 'aui-progress-indicator-value' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<div>
|
||||||
|
<div className = { iconBGClass } />
|
||||||
|
<div className = 'inlay-filmstrip-only__icon-container'>
|
||||||
|
<span className = { iconClass } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement|null}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OverlayFrame isLightOverlay = { this.props.isLightOverlay }>
|
||||||
|
<div className = 'inlay-filmstrip-only'>
|
||||||
|
<div className = 'inlay-filmstrip-only__content'>
|
||||||
|
{
|
||||||
|
this.props.children
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className = 'inlay-filmstrip-only__avatar-container'>
|
||||||
|
<Avatar uri = { this.props._avatar } />
|
||||||
|
{
|
||||||
|
this._renderIcon()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</OverlayFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
|
@ -1,12 +1,17 @@
|
||||||
/* global APP */
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import PageReloadFilmStripOnlyOverlay from './PageReloadFilmStripOnlyOverlay';
|
||||||
import PageReloadOverlay from './PageReloadOverlay';
|
import PageReloadOverlay from './PageReloadOverlay';
|
||||||
|
import SuspendedFilmStripOnlyOverlay from './SuspendedFilmStripOnlyOverlay';
|
||||||
import SuspendedOverlay from './SuspendedOverlay';
|
import SuspendedOverlay from './SuspendedOverlay';
|
||||||
|
import UserMediaPermissionsFilmStripOnlyOverlay
|
||||||
|
from './UserMediaPermissionsFilmStripOnlyOverlay';
|
||||||
import UserMediaPermissionsOverlay from './UserMediaPermissionsOverlay';
|
import UserMediaPermissionsOverlay from './UserMediaPermissionsOverlay';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React Component that will display the correct overlay when
|
* Implements a React Component that will display the correct overlay when
|
||||||
* needed.
|
* needed.
|
||||||
|
@ -94,6 +99,25 @@ class OverlayContainer extends Component {
|
||||||
_suspendDetected: React.PropTypes.bool
|
_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.
|
* React Component method that executes once component is updated.
|
||||||
*
|
*
|
||||||
|
@ -117,25 +141,28 @@ class OverlayContainer extends Component {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
const filmStripOnlyMode = this.state.filmStripOnly;
|
||||||
|
let overlayComponent, props;
|
||||||
|
|
||||||
if (this.props._connectionEstablished && this.props._haveToReload) {
|
if (this.props._connectionEstablished && this.props._haveToReload) {
|
||||||
return (
|
overlayComponent = filmStripOnlyMode
|
||||||
<PageReloadOverlay
|
? PageReloadFilmStripOnlyOverlay : PageReloadOverlay;
|
||||||
isNetworkFailure = { this.props._isNetworkFailure }
|
props = {
|
||||||
reason = { this.props._reason } />
|
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) {
|
if (overlayComponent) {
|
||||||
return (
|
return React.createElement(overlayComponent, props);
|
||||||
<SuspendedOverlay />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props._isMediaPermissionPromptVisible) {
|
|
||||||
return (
|
|
||||||
<UserMediaPermissionsOverlay
|
|
||||||
browser = { this.props._browser } />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -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 (
|
||||||
|
<div
|
||||||
|
className = { containerClass }
|
||||||
|
id = 'overlay'>
|
||||||
|
<div className = { contentClass }>
|
||||||
|
{
|
||||||
|
this.props.children
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<FilmStripOnlyOverlayFrame>
|
||||||
|
<div className = 'inlay-filmstrip-only__container'>
|
||||||
|
<div className = 'inlay-filmstrip-only__title'>
|
||||||
|
{ t(title) }
|
||||||
|
</div>
|
||||||
|
<div className = 'inlay-filmstrip-only__text'>
|
||||||
|
{ t(message, { seconds: timeLeft }) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
this._renderButton()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this._renderProgressBar()
|
||||||
|
}
|
||||||
|
</FilmStripOnlyOverlayFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(PageReloadFilmStripOnlyOverlay);
|
|
@ -1,194 +1,58 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { randomInt } from '../../base/util';
|
|
||||||
|
|
||||||
import AbstractOverlay from './AbstractOverlay';
|
import AbstractPageReloadOverlay from './AbstractPageReloadOverlay';
|
||||||
import ReloadTimer from './ReloadTimer';
|
import OverlayFrame from './OverlayFrame';
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
|
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React Component for page reload overlay. Shown before the
|
* Implements a React Component for page reload overlay. Shown before the
|
||||||
* conference is reloaded. Shows a warning message and counts down towards the
|
* conference is reloaded. Shows a warning message and counts down towards the
|
||||||
* reload.
|
* reload.
|
||||||
*/
|
*/
|
||||||
class PageReloadOverlay extends AbstractOverlay {
|
class PageReloadOverlay extends AbstractPageReloadOverlay {
|
||||||
/**
|
/**
|
||||||
* PageReloadOverlay component's property types.
|
* PageReloadOverlay component's property types.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
...AbstractPageReloadOverlay.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.
|
* The function to translate human-readable text.
|
||||||
* NOTE: Used by PageReloadOverlay only.
|
*
|
||||||
* @public
|
* @public
|
||||||
* @type {string}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
reason: React.PropTypes.string
|
t: React.PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new PageReloadOverlay instance.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @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}
|
* @returns {ReactElement|null}
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
_renderButton() {
|
render() {
|
||||||
if (this.props.isNetworkFailure) {
|
const { isNetworkFailure, t } = this.props;
|
||||||
const className
|
const { message, timeLeft, title } = this.state;
|
||||||
= 'button-control button-control_primary button-control_center';
|
|
||||||
const { t } = this.props;
|
|
||||||
|
|
||||||
/* eslint-disable react/jsx-handler-names */
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className = { className }
|
|
||||||
id = 'reconnectNow'
|
|
||||||
onClick = { this._reconnectNow }>
|
|
||||||
{ t('dialog.reconnectNow') }
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className = 'inlay'>
|
<OverlayFrame isLightOverlay = { isNetworkFailure }>
|
||||||
<span
|
<div className = 'inlay'>
|
||||||
className = 'reload_overlay_title'>
|
<span
|
||||||
{ t(this.state.title) }
|
className = 'reload_overlay_title'>
|
||||||
</span>
|
{ t(title) }
|
||||||
<span
|
</span>
|
||||||
className = 'reload_overlay_text'>
|
<span className = 'reload_overlay_text'>
|
||||||
{ t(this.state.message) }
|
{ t(message, { seconds: timeLeft }) }
|
||||||
</span>
|
</span>
|
||||||
<ReloadTimer
|
{ this._renderProgressBar() }
|
||||||
end = { 0 }
|
{ this._renderButton() }
|
||||||
interval = { 1 }
|
</div>
|
||||||
onFinish = { this._reconnectNow }
|
</OverlayFrame>
|
||||||
start = { this.state.timeoutSeconds }
|
|
||||||
step = { -1 } />
|
|
||||||
{ this._renderButton() }
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable react/jsx-handler-names */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 (
|
||||||
|
<button
|
||||||
|
className = { className }
|
||||||
|
onClick = { reconnectNow }>
|
||||||
|
{ t(this.props.textKey) }
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
/* eslint-enable react/jsx-handler-names */
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(ReloadButton);
|
|
@ -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 (
|
||||||
|
<FilmStripOnlyOverlayFrame isLightOverlay = { true }>
|
||||||
|
<div className = 'inlay-filmstrip-only__container'>
|
||||||
|
<div className = 'inlay-filmstrip-only__title'>
|
||||||
|
{ t('suspendedoverlay.title') }
|
||||||
|
</div>
|
||||||
|
<div className = 'inlay-filmstrip-only__text'>
|
||||||
|
{ translateToHTML(t, 'suspendedoverlay.text') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ReloadButton textKey = 'suspendedoverlay.rejoinKeyTitle' />
|
||||||
|
</FilmStripOnlyOverlayFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(SuspendedFilmStripOnlyOverlay);
|
|
@ -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
|
* Implements a React Component for suspended overlay. Shown when a suspend is
|
||||||
* detected.
|
* 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}
|
* @static
|
||||||
* @override
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
_renderOverlayContent() {
|
static propTypes = {
|
||||||
const btnClass = 'inlay__button button-control button-control_primary';
|
/**
|
||||||
|
* 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 { t } = this.props;
|
||||||
|
|
||||||
/* eslint-disable react/jsx-handler-names */
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className = 'inlay'>
|
<OverlayFrame>
|
||||||
<span className = 'inlay__icon icon-microphone' />
|
<div className = 'inlay'>
|
||||||
<span className = 'inlay__icon icon-camera' />
|
<span className = 'inlay__icon icon-microphone' />
|
||||||
<h3
|
<span className = 'inlay__icon icon-camera' />
|
||||||
className = 'inlay__title'>
|
<h3
|
||||||
{ t('suspendedoverlay.title') }
|
className = 'inlay__title'>
|
||||||
</h3>
|
{ t('suspendedoverlay.title') }
|
||||||
<button
|
</h3>
|
||||||
className = { btnClass }
|
<span className = 'inlay__text'>
|
||||||
onClick = { this._reconnectNow }>
|
{
|
||||||
{ t('suspendedoverlay.rejoinKeyTitle') }
|
translateToHTML(t, 'suspendedoverlay.title')
|
||||||
</button>
|
}
|
||||||
</div>
|
</span>
|
||||||
|
<ReloadButton
|
||||||
|
textKey = 'suspendedoverlay.rejoinKeyTitle' />
|
||||||
|
</div>
|
||||||
|
</OverlayFrame>
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable react/jsx-handler-names */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 (
|
||||||
|
<FilmStripOnlyOverlayFrame
|
||||||
|
icon = 'icon-mic-camera-combined'
|
||||||
|
isLightOverlay = { true }>
|
||||||
|
<div className = 'inlay-filmstrip-only__container'>
|
||||||
|
<div className = 'inlay-filmstrip-only__title'>
|
||||||
|
{
|
||||||
|
t('startupoverlay.title',
|
||||||
|
{ postProcess: 'resolveAppName' })
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className = 'inlay-filmstrip-only__text'>
|
||||||
|
{
|
||||||
|
translateToHTML(t, textKey)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FilmStripOnlyOverlayFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(UserMediaPermissionsFilmStripOnlyOverlay);
|
|
@ -1,16 +1,16 @@
|
||||||
/* global interfaceConfig */
|
/* global interfaceConfig */
|
||||||
|
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import { translate, translateToHTML } from '../../base/i18n';
|
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
|
* Implements a React Component for overlay with guidance how to proceed with
|
||||||
* gUM prompt.
|
* gUM prompt.
|
||||||
*/
|
*/
|
||||||
class UserMediaPermissionsOverlay extends AbstractOverlay {
|
class UserMediaPermissionsOverlay extends Component {
|
||||||
/**
|
/**
|
||||||
* UserMediaPermissionsOverlay component's property types.
|
* UserMediaPermissionsOverlay component's property types.
|
||||||
*
|
*
|
||||||
|
@ -24,7 +24,15 @@ class UserMediaPermissionsOverlay extends AbstractOverlay {
|
||||||
* @public
|
* @public
|
||||||
* @type {string}
|
* @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
|
* Implements React's {@link Component#render()}.
|
||||||
* with gUM prompt.
|
|
||||||
*
|
*
|
||||||
|
* @inheritdoc
|
||||||
* @returns {ReactElement|null}
|
* @returns {ReactElement|null}
|
||||||
* @override
|
|
||||||
* @protected
|
|
||||||
*/
|
*/
|
||||||
_renderOverlayContent() {
|
render() {
|
||||||
const { browser, t } = this.props;
|
const { browser, t } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<OverlayFrame>
|
||||||
<div className = 'inlay'>
|
<div className = 'inlay'>
|
||||||
<span className = 'inlay__icon icon-microphone' />
|
<span className = 'inlay__icon icon-microphone' />
|
||||||
<span className = 'inlay__icon icon-camera' />
|
<span className = 'inlay__icon icon-camera' />
|
||||||
<h3 className = 'inlay__title'>
|
<h3 className = 'inlay__title'>
|
||||||
{
|
{
|
||||||
t(
|
t('startupoverlay.title',
|
||||||
'startupoverlay.title',
|
|
||||||
{ postProcess: 'resolveAppName' })
|
{ postProcess: 'resolveAppName' })
|
||||||
}
|
}
|
||||||
</h3>
|
</h3>
|
||||||
<span className = 'inlay__text'>
|
<span className = 'inlay__text'>
|
||||||
{
|
{
|
||||||
translateToHTML(
|
translateToHTML(t,
|
||||||
t,
|
|
||||||
`userMedia.${browser}GrantPermissions`)
|
`userMedia.${browser}GrantPermissions`)
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className = 'policy overlay__policy'>
|
<div className = 'policy overlay__policy'>
|
||||||
<p className = 'policy__text'>
|
<p className = 'policy__text'>
|
||||||
{ t('startupoverlay.policyText') }
|
{ translateToHTML(t, 'startupoverlay.policyText') }
|
||||||
</p>
|
</p>
|
||||||
{
|
{
|
||||||
this._renderPolicyLogo()
|
this._renderPolicyLogo()
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</OverlayFrame>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue