diff --git a/modules/FollowMe.js b/modules/FollowMe.js index 0d5086809..0626d1bd0 100644 --- a/modules/FollowMe.js +++ b/modules/FollowMe.js @@ -25,6 +25,15 @@ import FilmStrip from './UI/videolayout/FilmStrip'; */ const _COMMAND = "follow-me"; +/** + * The timeout after which a follow-me command that has been received will be + * ignored if not consumed. + * + * @type {number} in seconds + * @private + */ +const _FOLLOW_ME_RECEIVED_TIMEOUT = 30; + /** * Represents the set of {FollowMe}-related states (properties and their * respective values) which are to be followed by a participant. {FollowMe} @@ -112,6 +121,7 @@ class FollowMe { constructor (conference, UI) { this._conference = conference; this._UI = UI; + this.nextOnStageTimer = 0; // The states of the local participant which are to be followed (by the // remote participants when the local participant is in her right to @@ -265,6 +275,7 @@ class FollowMe { // issued by a defined commander. if (typeof id === 'undefined') return; + // The Command(s) API will send us our own commands and we don't want // to act upon them. if (this._conference.isLocalId(id)) @@ -297,22 +308,24 @@ class FollowMe { // writing whether calling UI.toggleFilmStrip() is acceptable (from // a design standpoint) either. if (filmStripVisible !== FilmStrip.isFilmStripVisible()) - this._UI.eventEmitter.emit( - UIEvents.TOGGLE_FILM_STRIP, - filmStripVisible); + this._UI.eventEmitter.emit(UIEvents.TOGGLE_FILM_STRIP); } } _onNextOnStage(id) { - var clickId = null; - if(typeof id !== 'undefined' && !VideoLayout.isPinned(id)) + var pin; + if(typeof id !== 'undefined' && !VideoLayout.isPinned(id)) { clickId = id; - else if (typeof id == 'undefined' && VideoLayout.getPinnedId()) + pin = true; + } + else if (typeof id == 'undefined' && VideoLayout.getPinnedId()) { clickId = VideoLayout.getPinnedId(); + pin = false; + } if (clickId) - VideoLayout.handleVideoThumbClicked(clickId); + this._pinVideoThumbnailById(clickId, pin); } _onSharedDocumentVisible(sharedDocumentVisible) { @@ -328,6 +341,41 @@ class FollowMe { this._UI.getSharedDocumentManager().toggleEtherpad(); } } + + /** + * Pins / unpins the video thumbnail given by clickId. + * + * @param clickId the identifier of the video thumbnail to pin or unpin + * @param pin {true} to pin, {false} to unpin + * @private + */ + _pinVideoThumbnailById(clickId, pin) { + var self = this; + var smallVideo = VideoLayout.getSmallVideo(clickId); + + // If the SmallVideo for the given clickId exists we proceed with the + // pin/unpin. + if (smallVideo) { + this.nextOnStageTimer = 0; + clearTimeout(this.nextOnStageTimout); + if (pin && !VideoLayout.isPinned(clickId) + || !pin && VideoLayout.isPinned(clickId)) + VideoLayout.handleVideoThumbClicked(clickId); + } + // If there's no SmallVideo object for the given id, lets wait and see + // if it's going to be created in the next 30sec. + else { + this.nextOnStageTimout = setTimeout(function () { + if (self.nextOnStageTimer > _FOLLOW_ME_RECEIVED_TIMEOUT) { + self.nextOnStageTimer = 0; + return; + } + + this.nextOnStageTimer++; + self._pinVideoThumbnailById(clickId, pin); + }, 1000); + } + } } export default FollowMe; diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index 6a878e4d1..e20275b47 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -315,11 +315,21 @@ SmallVideo.prototype.selectVideoElement = function () { return $(RTCUIHelper.findVideoElement($('#' + this.videoSpanId)[0])); }; +/** + * Enables / disables the css responsible for focusing/pinning a video + * thumbnail. + * + * @param isFocused indicates if the thumbnail should be focused/pinned or not + */ SmallVideo.prototype.focus = function(isFocused) { - if(!isFocused) { - this.container.classList.remove("videoContainerFocused"); - } else { - this.container.classList.add("videoContainerFocused"); + var focusedCssClass = "videoContainerFocused"; + var isFocusClassEnabled = $(this.container).hasClass(focusedCssClass); + + if (!isFocused && isFocusClassEnabled) { + $(this.container).removeClass(focusedCssClass); + } + else if (isFocused && !isFocusClassEnabled) { + $(this.container).addClass(focusedCssClass); } };