From 0481e4cf00f009d64a43ecd05f6a471319768924 Mon Sep 17 00:00:00 2001 From: virtuacoplenny Date: Mon, 10 Jul 2017 15:29:44 -0700 Subject: [PATCH] feat(indicators): move the "top toolbar" indicators to react (#1699) * feat(indicators): move the "top toolbar" indicators to react * wrap baseindicator --- css/_videolayout_default.scss | 3 +- modules/UI/util/UIUtil.js | 65 +--------- modules/UI/videolayout/RemoteVideo.js | 6 +- modules/UI/videolayout/SmallVideo.js | 112 ++++++++++++------ .../filmstrip/components/Filmstrip.web.js | 6 +- .../components/web/AudioMutedIndicator.js | 25 ++-- .../filmstrip/components/web/BaseIndicator.js | 67 +++++++---- .../web/DominantSpeakerIndicator.js | 43 +++++++ .../components/web/ModeratorIndicator.js | 25 ++-- .../components/web/RaisedHandIndicator.js | 41 +++++++ .../components/web/VideoMutedIndicator.js | 23 ++-- .../filmstrip/components/web/index.js | 3 + 12 files changed, 257 insertions(+), 162 deletions(-) create mode 100644 react/features/filmstrip/components/web/DominantSpeakerIndicator.js create mode 100644 react/features/filmstrip/components/web/RaisedHandIndicator.js diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index 4eb0b9c68..5384ecbc7 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -58,7 +58,6 @@ padding: $toolbarPadding; padding-bottom: 0; - .connection-indicator-container, .connection-indicator, span.indicator { margin-right: em(5, 8); @@ -302,7 +301,7 @@ margin: 0px 0px 0px 5px; } -#raisehandindicator { +.raisehandindicator { background: $raiseHandBg; } diff --git a/modules/UI/util/UIUtil.js b/modules/UI/util/UIUtil.js index dc1237979..05fbcc65c 100644 --- a/modules/UI/util/UIUtil.js +++ b/modules/UI/util/UIUtil.js @@ -446,71 +446,16 @@ const IndicatorFontSizes = { } }, - /** - * Gets an "indicator" span for a video thumbnail. - * If element doesn't exist then creates it and appends - * video span container. - * - * @param {object} opts - * @param opts.indicatorId {String} - identificator of indicator - * @param opts.videoSpanId {String} - identificator of video span - * @param opts.content {String} HTML content of indicator - * @param opts.tooltip {String} - tooltip key for translation - * - * @returns {HTMLSpanElement} indicatorSpan - */ - getVideoThumbnailIndicatorSpan(opts = {}) { - let indicatorId = opts.indicatorId; - let videoSpanId = opts.videoSpanId; - let indicators = $(`#${videoSpanId} [id="${indicatorId}"]`); - let indicatorSpan; - - if (indicators.length <= 0) { - indicatorSpan = document.createElement('span'); - - indicatorSpan.className = 'indicator'; - indicatorSpan.id = indicatorId; - - if(opts.content) { - indicatorSpan.innerHTML = opts.content; - } - - if (opts.tooltip) { - this.setTooltip(indicatorSpan, opts.tooltip, "top"); - APP.translation.translateElement($(indicatorSpan)); - } - - this._resizeIndicator(indicatorSpan); - - document.getElementById(videoSpanId) - .querySelector('.videocontainer__toptoolbar') - .appendChild(indicatorSpan); - } else { - indicatorSpan = indicators[0]; - } - - return indicatorSpan; - }, - - /** - * Resizing indicator element passing via argument - * according to the current thumbnail size - * @param {HTMLElement} indicator - indicator element - * @private - */ - _resizeIndicator(indicator) { - let height = $('#localVideoContainer').height(); - let fontSize = this.getIndicatorFontSize(height); - $(indicator).css('font-size', fontSize); - }, - /** * Returns font size for indicators according to current * height of thumbnail - * @param {Number} - height - current height of thumbnail + * @param {Number} [thumbnailHeight] - current height of thumbnail * @returns {Number} - font size for current height */ - getIndicatorFontSize(height) { + getIndicatorFontSize(thumbnailHeight) { + const height = typeof thumbnailHeight === 'undefined' + ? $('#localVideoContainer').height() : thumbnailHeight; + const { SMALL, MEDIUM } = ThumbnailSizes; let fontSize = IndicatorFontSizes.NORMAL; diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index 7ada86ea0..aa7d27828 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -529,6 +529,8 @@ RemoteVideo.prototype.remove = function () { this.removeAvatar(); + this._unmountIndicators(); + // Make sure that the large video is updated if are removing its // corresponding small video. this.VideoLayout.updateAfterThumbRemoved(this.id); @@ -688,10 +690,6 @@ RemoteVideo.createContainer = function (spanId) { indicatorBar.className = "videocontainer__toptoolbar"; container.appendChild(indicatorBar); - const connectionIndicatorContainer = document.createElement('span'); - connectionIndicatorContainer.className = 'connection-indicator-container'; - indicatorBar.appendChild(connectionIndicatorContainer); - let toolbar = document.createElement('div'); toolbar.className = "videocontainer__toolbar"; container.appendChild(toolbar); diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index b07d65540..b6e72a7ee 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -18,7 +18,9 @@ import { import { DisplayName } from '../../../react/features/display-name'; import { AudioMutedIndicator, + DominantSpeakerIndicator, ModeratorIndicator, + RaisedHandIndicator, VideoMutedIndicator } from '../../../react/features/filmstrip'; /* eslint-enable no-unused-vars */ @@ -99,6 +101,30 @@ function SmallVideo(VideoLayout) { */ this._popoverIsHovered = false; + /** + * Whether or not the connection indicator should be displayed. + * + * @private + * @type {boolean} + */ + this._showConnectionIndicator = true; + + /** + * Whether or not the dominant speaker indicator should be displayed. + * + * @private + * @type {boolean} + */ + this._showDominantSpeaker = false; + + /** + * Whether or not the raised hand indicator should be displayed. + * + * @private + * @type {boolean} + */ + this._showRaisedHand = false; + // Bind event handlers so they are only bound once for every instance. this._onPopoverHover = this._onPopoverHover.bind(this); this.updateView = this.updateView.bind(this); @@ -252,12 +278,9 @@ SmallVideo.prototype.updateConnectionStats = function (percent, object) { * @returns {void} */ SmallVideo.prototype.removeConnectionIndicator = function () { - const connectionIndicatorContainer - = this.container.querySelector('.connection-indicator-container'); + this._showConnectionIndicator = false; - if (connectionIndicatorContainer) { - ReactDOM.unmountComponentAtNode(connectionIndicatorContainer); - } + this.updateIndicators(); }; /** @@ -643,17 +666,9 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) { return; } - let indicatorSpanId = "dominantspeakerindicator"; - let content = ``; - let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({ - videoSpanId: this.videoSpanId, - indicatorId: indicatorSpanId, - content, - tooltip: 'speaker' - }); + this._showDominantSpeaker = show; - UIUtil.setVisible(indicatorSpan, show); + this.updateIndicators(); }; /** @@ -667,17 +682,9 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) { return; } - let indicatorSpanId = "raisehandindicator"; - let content = ``; - let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({ - indicatorId: indicatorSpanId, - videoSpanId: this.videoSpanId, - content, - tooltip: 'raisedHand' - }); + this._showRaisedHand = show; - UIUtil.setVisible(indicatorSpan, show); + this.updateIndicators(); }; /** @@ -746,21 +753,60 @@ SmallVideo.prototype.updateConnectionIndicator = function (newStats = {}) { this._cachedConnectionStats = Object.assign({}, this._cachedConnectionStats, newStats); - const connectionIndicatorContainer - = this.container.querySelector('.connection-indicator-container'); + this.updateIndicators(); +}; + +/** + * Updates the React element responsible for showing connection status, dominant + * speaker, and raised hand icons. Uses instance variables to get the necessary + * state to display. Will create the React element if not already created. + * + * @private + * @returns {void} + */ +SmallVideo.prototype.updateIndicators = function () { + const indicatorToolbar + = this.container.querySelector('.videocontainer__toptoolbar'); + + const iconSize = UIUtil.getIndicatorFontSize(); /* jshint ignore:start */ ReactDOM.render( - , - connectionIndicatorContainer +
+ { this._showConnectionIndicator + ? + : null } + { this._showRaisedHand + ? : null } + { this._showDominantSpeaker + ? : null } +
, + indicatorToolbar ); /* jshint ignore:end */ }; +/** + * Removes the React element responsible for showing connection status, dominant + * speaker, and raised hand icons. + * + * @private + * @returns {void} + */ +SmallVideo.prototype._unmountIndicators = function () { + const indicatorToolbar + = this.container.querySelector('.videocontainer__toptoolbar'); + + if (indicatorToolbar) { + ReactDOM.unmountComponentAtNode(indicatorToolbar); + } +}; + /** * Updates the current state of the connection indicator popover being hovered. * If hovered, display the small video as if it is hovered. diff --git a/react/features/filmstrip/components/Filmstrip.web.js b/react/features/filmstrip/components/Filmstrip.web.js index eec490353..0fe94f4c1 100644 --- a/react/features/filmstrip/components/Filmstrip.web.js +++ b/react/features/filmstrip/components/Filmstrip.web.js @@ -44,11 +44,7 @@ export default class Filmstrip extends Component { id = 'localAudio' muted = { true } />
-
- -
+
diff --git a/react/features/filmstrip/components/web/AudioMutedIndicator.js b/react/features/filmstrip/components/web/AudioMutedIndicator.js index c49d11f1b..fef23141b 100644 --- a/react/features/filmstrip/components/web/AudioMutedIndicator.js +++ b/react/features/filmstrip/components/web/AudioMutedIndicator.js @@ -1,23 +1,26 @@ +import React, { Component } from 'react'; + import BaseIndicator from './BaseIndicator'; /** * React {@code Component} for showing an audio muted icon with a tooltip. * - * @extends BaseIndicator + * @extends Component */ -class AudioMutedIndicator extends BaseIndicator { +class AudioMutedIndicator extends Component { /** - * Initializes a new AudioMutedIcon instance. + * Implements React's {@link Component#render()}. * - * @param {Object} props - The read-only React Component props with which - * the new instance is to be initialized. + * @inheritdoc + * @returns {ReactElement} */ - constructor(props) { - super(props); - - this._classNames = 'audioMuted toolbar-icon'; - this._iconClass = 'icon-mic-disabled'; - this._tooltipKey = 'videothumbnail.mute'; + render() { + return ( + + ); } } diff --git a/react/features/filmstrip/components/web/BaseIndicator.js b/react/features/filmstrip/components/web/BaseIndicator.js index 30f21142a..c413c15b2 100644 --- a/react/features/filmstrip/components/web/BaseIndicator.js +++ b/react/features/filmstrip/components/web/BaseIndicator.js @@ -8,6 +8,41 @@ import UIUtil from '../../../../../modules/UI/util/UIUtil'; * @extends Component */ class BaseIndicator extends Component { + static defaultProps = { + className: '', + iconClassName: '', + iconSize: 'auto', + id: '' + }; + + static propTypes = { + /** + * The CSS class names to set on the root element of the component. + */ + className: React.PropTypes.string, + + /** + * The CSS classnames to set on the icon element of the component. + */ + iconClassName: React.PropTypes.string, + + /** + * The front size for the icon. + */ + iconSize: React.PropTypes.string, + + /** + * The ID attribue to set on the root element of the component. + */ + id: React.PropTypes.string, + + /** + * The translation key to use for displaying a tooltip when hovering + * over the component. + */ + tooltipKey: React.PropTypes.string + }; + /** * Initializes a new {@code BaseIndicator} instance. * @@ -17,20 +52,6 @@ class BaseIndicator extends Component { constructor(props) { super(props); - /** - * The CSS classes to apply to the root HTML element of the component. - * - * @type {string} - */ - this._classNames = ''; - - /** - * The CSS class which will display an icon. - * - * @type {string} - */ - this._iconClass = ''; - /** * An internal reference to the HTML element at the top of the * component's DOM hierarchy. The reference is needed for attaching a @@ -40,13 +61,6 @@ class BaseIndicator extends Component { */ this._rootElement = null; - /** - * The translation key for the text to display in the tooltip. - * - * @type {string} - */ - this._tooltipKey = ''; - // Bind event handler so it is only bound once for every instance. this._setRootElementRef = this._setRootElementRef.bind(this); } @@ -69,10 +83,13 @@ class BaseIndicator extends Component { */ render() { return ( - + + className = { this.props.iconClassName } + style = {{ fontSize: this.props.iconSize }} /> ); } @@ -101,7 +118,7 @@ class BaseIndicator extends Component { // becomes available for tooltips. UIUtil.setTooltip( this._rootElement, - this._tooltipKey, + this.props.tooltipKey, 'top' ); } diff --git a/react/features/filmstrip/components/web/DominantSpeakerIndicator.js b/react/features/filmstrip/components/web/DominantSpeakerIndicator.js new file mode 100644 index 000000000..758ec2645 --- /dev/null +++ b/react/features/filmstrip/components/web/DominantSpeakerIndicator.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; + +import BaseIndicator from './BaseIndicator'; + +/** + * Thumbnail badge showing that the participant is the dominant speaker in + * the conference. + * + * @extends Component + */ +class DominantSpeakerIndicator extends Component { + /** + * {@code DominantSpeakerIndicator} component's property types. + * + * @static + */ + static propTypes = { + /** + * The font-size for the icon. + * + * @type {number} + */ + iconSize: React.PropTypes.number + }; + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + */ + render() { + return ( + + ); + } +} + +export default DominantSpeakerIndicator; diff --git a/react/features/filmstrip/components/web/ModeratorIndicator.js b/react/features/filmstrip/components/web/ModeratorIndicator.js index 7fc552aaf..aa4521ef5 100644 --- a/react/features/filmstrip/components/web/ModeratorIndicator.js +++ b/react/features/filmstrip/components/web/ModeratorIndicator.js @@ -1,23 +1,26 @@ +import React, { Component } from 'react'; + import BaseIndicator from './BaseIndicator'; /** * React {@code Component} for showing a moderator icon with a tooltip. * - * @extends BaseIndicator + * @extends Component */ -class ModeratorIndicator extends BaseIndicator { +class ModeratorIndicator extends Component { /** - * Initializes a new ModeratorIndicator instance. + * Implements React's {@link Component#render()}. * - * @param {Object} props - The read-only React Component props with which - * the new instance is to be initialized. + * @inheritdoc + * @returns {ReactElement} */ - constructor(props) { - super(props); - - this._classNames = 'focusindicator toolbar-icon right'; - this._iconClass = 'icon-star'; - this._tooltipKey = 'videothumbnail.moderator'; + render() { + return ( + + ); } } diff --git a/react/features/filmstrip/components/web/RaisedHandIndicator.js b/react/features/filmstrip/components/web/RaisedHandIndicator.js new file mode 100644 index 000000000..5ef4ac7f7 --- /dev/null +++ b/react/features/filmstrip/components/web/RaisedHandIndicator.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react'; + +import BaseIndicator from './BaseIndicator'; + +/** + * Thumbnail badge showing that the participant would like to speak. + * + * @extends Component + */ +class RaisedHandIndicator extends Component { + /** + * {@code RaisedHandIndicator} component's property types. + * + * @static + */ + static propTypes = { + /** + * The font-size for the icon. + * + * @type {number} + */ + iconSize: React.PropTypes.number + }; + + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + */ + render() { + return ( + + ); + } +} + +export default RaisedHandIndicator; diff --git a/react/features/filmstrip/components/web/VideoMutedIndicator.js b/react/features/filmstrip/components/web/VideoMutedIndicator.js index f5dcd10d3..f10bc7604 100644 --- a/react/features/filmstrip/components/web/VideoMutedIndicator.js +++ b/react/features/filmstrip/components/web/VideoMutedIndicator.js @@ -1,23 +1,24 @@ +import React, { Component } from 'react'; import BaseIndicator from './BaseIndicator'; /** * React {@code Component} for showing a video muted icon with a tooltip. * - * @extends BaseIndicator + * @extends Component */ -class VideoMutedIndicator extends BaseIndicator { +class VideoMutedIndicator extends Component { /** - * Initializes a new VideoMutedIndicator instance. + * Implements React's {@link Component#render()}. * - * @param {Object} props - The read-only React Component props with which - * the new instance is to be initialized. + * @inheritdoc */ - constructor(props) { - super(props); - - this._classNames = 'videoMuted toolbar-icon'; - this._iconClass = 'icon-camera-disabled'; - this._tooltipKey = 'videothumbnail.videomute'; + render() { + return ( + + ); } } diff --git a/react/features/filmstrip/components/web/index.js b/react/features/filmstrip/components/web/index.js index e47ee6edd..cbbba44ff 100644 --- a/react/features/filmstrip/components/web/index.js +++ b/react/features/filmstrip/components/web/index.js @@ -1,3 +1,6 @@ export { default as AudioMutedIndicator } from './AudioMutedIndicator'; +export { default as DominantSpeakerIndicator } + from './DominantSpeakerIndicator'; export { default as ModeratorIndicator } from './ModeratorIndicator'; +export { default as RaisedHandIndicator } from './RaisedHandIndicator'; export { default as VideoMutedIndicator } from './VideoMutedIndicator';