From 2333249b057ea61b440fa51a2aeb3f0b655ce1cf Mon Sep 17 00:00:00 2001 From: Leonard Kim Date: Thu, 18 May 2017 17:40:14 -0700 Subject: [PATCH] feat(1-on-1): Initial implementation - Expose an api on Filmstrip to hide the remote videos container, which does so by adding a class - Modify listeners for user join, leave, share video to call the api - Hide the container when there is 1 or fewer remote participants - Always show the container if self view is in focus - Show the container if the number of remote thumbnails does not match the count of remote participants, such as the case of sharing a video --- conference.js | 23 ++++++++++++++++++++ config.js | 3 +++ css/_filmstrip.scss | 9 ++++++++ modules/UI/UI.js | 30 ++++++++++++++++++++++++++ modules/UI/shared_video/SharedVideo.js | 3 +++ modules/UI/videolayout/Filmstrip.js | 23 +++++++++++++++++++- modules/UI/videolayout/VideoLayout.js | 9 ++++++++ 7 files changed, 99 insertions(+), 1 deletion(-) diff --git a/conference.js b/conference.js index d74f8ec05..bf79f678f 100644 --- a/conference.js +++ b/conference.js @@ -1286,6 +1286,8 @@ export default { // check the roles for the new user and reflect them APP.UI.updateUserRole(user); + + updateRemoteThumbnailsVisibility(); }); room.on(ConferenceEvents.USER_LEFT, (id, user) => { APP.store.dispatch(participantLeft(id, user)); @@ -1293,6 +1295,8 @@ export default { APP.API.notifyUserLeft(id); APP.UI.removeUser(id, user.getDisplayName()); APP.UI.onSharedVideoStop(id); + + updateRemoteThumbnailsVisibility(); }); room.on(ConferenceEvents.USER_STATUS_CHANGED, (id, status) => { @@ -1475,6 +1479,8 @@ export default { reportError(e); } } + + updateRemoteThumbnailsVisibility(); }); } @@ -1811,6 +1817,8 @@ export default { } }); } + + updateRemoteThumbnailsVisibility(); }); room.addCommandListener( this.commands.defaults.SHARED_VIDEO, ({value, attributes}, id) => { @@ -1826,6 +1834,21 @@ export default { APP.UI.onSharedVideoUpdate(id, value, attributes); } }); + + function updateRemoteThumbnailsVisibility() { + const localUserId = APP.conference.getMyUserId(); + const remoteParticipantsCount = room.getParticipantCount() - 1; + + // Get the remote thumbnail count for cases where there are + // non-participants displaying video, such as with video sharing. + const remoteVideosCount = APP.UI.getRemoteVideosCount(); + + const shouldShowRemoteThumbnails = APP.UI.isPinned(localUserId) + || remoteVideosCount > 1 + || remoteParticipantsCount !== remoteVideosCount; + + APP.UI.setRemoteThumbnailsVisibility(shouldShowRemoteThumbnails); + } }, /** * Adds any room listener. diff --git a/config.js b/config.js index 47c9a8e29..4b7223c5f 100644 --- a/config.js +++ b/config.js @@ -57,6 +57,9 @@ var config = { // eslint-disable-line no-unused-vars webrtcIceTcpDisable: false, openSctp: true, // Toggle to enable/disable SCTP channels + + // Disable hiding of remote thumbnails when in a 1-on-1 conference call. + disable1On1Mode: false, disableStats: false, disableAudioLevels: false, channelLastN: -1, // The default value of the channel attribute last-n. diff --git a/css/_filmstrip.scss b/css/_filmstrip.scss index d8bc409e5..8c7fb225b 100644 --- a/css/_filmstrip.scss +++ b/css/_filmstrip.scss @@ -138,4 +138,13 @@ margin-bottom: auto; padding-right: $defaultToolbarSize; } + + .remote-videos-container { + transition: opacity 1s; + + &.hide-videos { + opacity: 0; + pointer-events: none; + } + } } diff --git a/modules/UI/UI.js b/modules/UI/UI.js index b0893362e..2e713a8e4 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -309,6 +309,10 @@ UI.start = function () { SideContainerToggler.init(eventEmitter); Filmstrip.init(eventEmitter); + // By default start with remote videos hidden and rely on other logic to + // make them visible. + UI.setRemoteThumbnailsVisibility(false); + VideoLayout.init(eventEmitter); if (!interfaceConfig.filmStripOnly) { VideoLayout.initLargeVideo(); @@ -1146,6 +1150,15 @@ UI.getLargeVideo = function () { return VideoLayout.getLargeVideo(); }; +/** + * Returns whether or not the passed in user id is currently pinned to the large + * video. + * + * @param {string} userId - The id of the user to check is pinned or not. + * @returns {boolean} True if the user is currently pinned to the large video. + */ +UI.isPinned = userId => VideoLayout.getPinnedId() === userId; + /** * Shows dialog with a link to FF extension. */ @@ -1396,6 +1409,23 @@ UI.isRingOverlayVisible = () => RingOverlay.isVisible(); */ UI.onUserFeaturesChanged = user => VideoLayout.onUserFeaturesChanged(user); +/** + * Returns the number of known remote videos. + * + * @returns {number} The number of remote videos. + */ +UI.getRemoteVideosCount = () => VideoLayout.getRemoteVideosCount(); + +/** + * Makes remote thumbnail videos visible or not visible. + * + * @param {boolean} shouldHide - True if remote thumbnails should be hidden, + * false f they should be visible. + * @returns {void} + */ +UI.setRemoteThumbnailsVisibility + = shouldHide => Filmstrip.setRemoteVideoVisibility(shouldHide); + const UIListeners = new Map([ [ UIEvents.ETHERPAD_CLICKED, diff --git a/modules/UI/shared_video/SharedVideo.js b/modules/UI/shared_video/SharedVideo.js index 9458a644c..7c5ffb2ee 100644 --- a/modules/UI/shared_video/SharedVideo.js +++ b/modules/UI/shared_video/SharedVideo.js @@ -456,6 +456,9 @@ export default class SharedVideoManager { // revert to original behavior (prevents pausing // for participants not sharing the video to pause it) $("#sharedVideo").css("pointer-events","auto"); + + this.emitter.emit( + UIEvents.UPDATE_SHARED_VIDEO, null, 'removed'); }); this.url = null; diff --git a/modules/UI/videolayout/Filmstrip.js b/modules/UI/videolayout/Filmstrip.js index 1fc076f9f..172b8d09a 100644 --- a/modules/UI/videolayout/Filmstrip.js +++ b/modules/UI/videolayout/Filmstrip.js @@ -1,4 +1,4 @@ -/* global $, APP, JitsiMeetJS, interfaceConfig */ +/* global $, APP, config, JitsiMeetJS, interfaceConfig */ import UIEvents from "../../../service/UI/UIEvents"; import UIUtil from "../util/UIUtil"; @@ -25,6 +25,27 @@ const Filmstrip = { } }, + /** + * Sets a class on the remote videos container for CSS to adjust visibility + * of the remote videos. Will no-op if config.debug is truthy, as should be + * the case with torture tests. + * + * @param {boolean} shouldHide - True if remote videos should be hidden, + * false if they should be visible. + * @returns {void} + */ + setRemoteVideoVisibility(shouldShow) { + // FIXME Checking config.debug is a grand hack to avoid fixing the + // torture tests after the 1-on-1 UI was implemented, which hides remote + // videos on 1-on-1 calls. If this check is to be kept, at least create + // new torture tests to verify 1-on-1 mode. + if (config.debug || config.disable1On1Mode) { + return; + } + + this.filmstripRemoteVideos.toggleClass('hide-videos', !shouldShow); + }, + /** * Initializes the filmstrip toolbar. */ diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 7deb8555f..ef95f9915 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -1133,6 +1133,15 @@ var VideoLayout = { */ getLargeVideoWrapper() { return this.getCurrentlyOnLargeContainer().$wrapper; + }, + + /** + * Returns the number of remove video ids. + * + * @returns {number} The number of remote videos. + */ + getRemoteVideosCount() { + return Object.keys(remoteVideos).length; } };