mirror of https://github.com/jitsi/jitsi-meet
parent
c461e8b63c
commit
3ae99ea0b9
@ -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); |
@ -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); |
@ -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); |
@ -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); |
Loading…
Reference in new issue