feat(overlays): for filmstrip only mode
This commit is contained in:
parent
c461e8b63c
commit
3ae99ea0b9
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,7 @@
|
|||
#reloadProgressBar {
|
||||
width: 180px;
|
||||
margin: 5px auto;
|
||||
> .aui-progress-indicator-value {
|
||||
background: $reloadProgressBarBg;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
"defaultLink": "e.g. __url__",
|
||||
"callingName": "__name__",
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
||||
"chromeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
||||
"androidGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
||||
"firefoxGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Share Selected Device</i></b> button",
|
||||
"operaGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
|
||||
"iexplorerGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>OK</i></b> button",
|
||||
"safariGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>OK</i></b> button",
|
||||
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"androidGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"firefoxGrantPermissions": "Select <b><i>Share Selected Device</i></b> when your browser asks for permissions.",
|
||||
"operaGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"iexplorerGrantPermissions": "Select <b><i>OK</i></b> when your browser asks for permissions.",
|
||||
"safariGrantPermissions": "Select <b><i>OK</i></b> 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 <i>Rejoin</i> 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.",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 { 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 (
|
||||
<PageReloadOverlay
|
||||
isNetworkFailure = { this.props._isNetworkFailure }
|
||||
reason = { this.props._reason } />
|
||||
);
|
||||
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 (
|
||||
<SuspendedOverlay />
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props._isMediaPermissionPromptVisible) {
|
||||
return (
|
||||
<UserMediaPermissionsOverlay
|
||||
browser = { this.props._browser } />
|
||||
);
|
||||
if (overlayComponent) {
|
||||
return React.createElement(overlayComponent, props);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Implements an abstract React Component for overlay - the components which are
|
||||
* displayed on top of the application covering the whole screen.
|
||||
*
|
||||
* @abstract
|
||||
* Implements a React Component for the frame of the overlays.
|
||||
*/
|
||||
export default class OverlayFrame extends Component {
|
||||
/**
|
||||
|
@ -27,6 +26,26 @@ export default class OverlayFrame extends Component {
|
|||
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()}.
|
||||
*
|
||||
|
@ -34,14 +53,20 @@ export default class OverlayFrame extends Component {
|
|||
* @returns {ReactElement|null}
|
||||
*/
|
||||
render() {
|
||||
const containerClass = this.props.isLightOverlay
|
||||
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 = 'overlay__content'>
|
||||
<div className = { contentClass }>
|
||||
{
|
||||
this.props.children
|
||||
}
|
||||
|
|
|
@ -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,42 +1,23 @@
|
|||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { randomInt } from '../../base/util';
|
||||
|
||||
import AbstractPageReloadOverlay from './AbstractPageReloadOverlay';
|
||||
import OverlayFrame from './OverlayFrame';
|
||||
import { reconnectNow } from '../functions';
|
||||
import ReloadTimer from './ReloadTimer';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* 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 Component {
|
||||
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,
|
||||
|
||||
/**
|
||||
* The reason for the error that will cause the reload.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
reason: React.PropTypes.string,
|
||||
...AbstractPageReloadOverlay.propTypes,
|
||||
|
||||
/**
|
||||
* The function to translate human-readable text.
|
||||
|
@ -47,118 +28,6 @@ class PageReloadOverlay extends Component {
|
|||
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 = {
|
||||
/**
|
||||
* 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
|
||||
* @returns {void}
|
||||
*/
|
||||
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 (
|
||||
<button
|
||||
className = { className }
|
||||
id = 'reconnectNow'
|
||||
onClick = { reconnectNow }>
|
||||
{ t('dialog.reconnectNow') }
|
||||
</button>
|
||||
);
|
||||
|
||||
|
||||
/* eslint-enable react/jsx-handler-names */
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -166,33 +35,24 @@ class PageReloadOverlay extends Component {
|
|||
* @returns {ReactElement|null}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
/* eslint-disable react/jsx-handler-names */
|
||||
const { isNetworkFailure, t } = this.props;
|
||||
const { message, timeLeft, title } = this.state;
|
||||
|
||||
return (
|
||||
<OverlayFrame isLightOverlay = { this.state.isLightOverlay }>
|
||||
<OverlayFrame isLightOverlay = { isNetworkFailure }>
|
||||
<div className = 'inlay'>
|
||||
<span
|
||||
className = 'reload_overlay_title'>
|
||||
{ t(this.state.title) }
|
||||
{ t(title) }
|
||||
</span>
|
||||
<span
|
||||
className = 'reload_overlay_text'>
|
||||
{ t(this.state.message) }
|
||||
<span className = 'reload_overlay_text'>
|
||||
{ t(message, { seconds: timeLeft }) }
|
||||
</span>
|
||||
<ReloadTimer
|
||||
end = { 0 }
|
||||
interval = { 1 }
|
||||
onFinish = { reconnectNow }
|
||||
start = { this.state.timeoutSeconds }
|
||||
step = { -1 } />
|
||||
{ this._renderProgressBar() }
|
||||
{ this._renderButton() }
|
||||
</div>
|
||||
</OverlayFrame>
|
||||
);
|
||||
|
||||
/* 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
|
||||
* 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 (
|
||||
<div className = 'inlay'>
|
||||
<span className = 'inlay__icon icon-microphone' />
|
||||
<span className = 'inlay__icon icon-camera' />
|
||||
<h3
|
||||
className = 'inlay__title'>
|
||||
{ t('suspendedoverlay.title') }
|
||||
</h3>
|
||||
<button
|
||||
className = { btnClass }
|
||||
onClick = { this._reconnectNow }>
|
||||
{ t('suspendedoverlay.rejoinKeyTitle') }
|
||||
</button>
|
||||
</div>
|
||||
<OverlayFrame>
|
||||
<div className = 'inlay'>
|
||||
<span className = 'inlay__icon icon-microphone' />
|
||||
<span className = 'inlay__icon icon-camera' />
|
||||
<h3
|
||||
className = 'inlay__title'>
|
||||
{ t('suspendedoverlay.title') }
|
||||
</h3>
|
||||
<span className = 'inlay__text'>
|
||||
{
|
||||
translateToHTML(t, 'suspendedoverlay.title')
|
||||
}
|
||||
</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);
|
|
@ -71,15 +71,13 @@ class UserMediaPermissionsOverlay extends Component {
|
|||
<span className = 'inlay__icon icon-camera' />
|
||||
<h3 className = 'inlay__title'>
|
||||
{
|
||||
t(
|
||||
'startupoverlay.title',
|
||||
t('startupoverlay.title',
|
||||
{ postProcess: 'resolveAppName' })
|
||||
}
|
||||
</h3>
|
||||
<span className = 'inlay__text'>
|
||||
{
|
||||
translateToHTML(
|
||||
t,
|
||||
translateToHTML(t,
|
||||
`userMedia.${browser}GrantPermissions`)
|
||||
}
|
||||
</span>
|
||||
|
|
Loading…
Reference in New Issue