diff --git a/conference.js b/conference.js index 618b230a2..75333fcc3 100644 --- a/conference.js +++ b/conference.js @@ -73,8 +73,11 @@ function getDisplayName (id) { /** * Mute or unmute local audio stream if it exists. * @param {boolean} muted if audio stream should be muted or unmuted. + * @param {boolean} indicates if this local audio mute was a result of user + * interaction + * */ -function muteLocalAudio (muted) { +function muteLocalAudio (muted, userInteraction) { if (!localAudio) { return; } diff --git a/index.html b/index.html index 8c201d8e3..f4ac8e737 100644 --- a/index.html +++ b/index.html @@ -124,6 +124,9 @@ + diff --git a/lang/main.json b/lang/main.json index 963c6e553..f05fe8791 100644 --- a/lang/main.json +++ b/lang/main.json @@ -65,7 +65,8 @@ "logout": "Logout", "dialpad": "Show dialpad", "sharedVideoMutedPopup": "Your shared video has been muted so
that you can talk to the other participants.", - "micMutedPopup": "Your microphone has been muted so that you
would fully enjoy your shared video." + "micMutedPopup": "Your microphone has been muted so that you
would fully enjoy your shared video.", + "unableToUnmutePopup": "You're not able to un-mute while the shared video is on." }, "bottomtoolbar": { "chat": "Open / close chat", diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 260b003b9..cafe3ef9d 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -331,6 +331,14 @@ function bindEvents() { $(window).resize(onResize); } +/** + * Returns the shared document manager object. + * @return {EtherpadManager} the shared document manager object + */ +UI.getSharedVideoManager = function () { + return sharedVideoManager; +}; + /** * Starts the UI module and initializes all related components. * diff --git a/modules/UI/shared_video/SharedVideo.js b/modules/UI/shared_video/SharedVideo.js index ea2ca86e3..aa9af4deb 100644 --- a/modules/UI/shared_video/SharedVideo.js +++ b/modules/UI/shared_video/SharedVideo.js @@ -26,6 +26,24 @@ export default class SharedVideoManager { this.emitter = emitter; this.isSharedVideoShown = false; this.isPlayerAPILoaded = false; + this.mutedWithUserInteraction = false; + } + + /** + * Indicates if the player volume is currently on. + * + * @returns {*|player|boolean} + */ + isSharedVideoVolumeOn() { + return (this.player && this.player.getVolume() > 0); + } + + /** + * Indicates if the local user is the owner of the shared video. + * @returns {*|boolean} + */ + isSharedVideoOwner() { + return this.from && APP.conference.isLocalId(this.from); } /** @@ -169,10 +187,15 @@ export default class SharedVideoManager { // let's check, if player is not muted lets mute locally if(event.data.volume > 0 && !event.data.muted - && !APP.conference.isLocalAudioMuted()){ - self.emitter.emit(UIEvents.AUDIO_MUTED, true); + && !APP.conference.isLocalAudioMuted()) { + self.emitter.emit(UIEvents.AUDIO_MUTED, true, false); self.showMicMutedPopup(true); } + else if (!self.mutedWithUserInteraction + && (event.data.volume <=0 || event.data.muted) + && APP.conference.isLocalAudioMuted()) { + self.emitter.emit(UIEvents.AUDIO_MUTED, false, false); + } }; window.onPlayerReady = function(event) { @@ -231,9 +254,11 @@ export default class SharedVideoManager { this.processTime(player, attributes, playerPaused); // lets check the volume - if (attributes.volume !== undefined && - player.getVolume() != attributes.volume - && APP.conference.isLocalAudioMuted()) { + if (attributes.volume !== undefined + && player.getVolume() != attributes.volume + && (APP.conference.isLocalAudioMuted() + || !this.mutedWithUserInteraction)) { + player.setVolume(attributes.volume); console.info("Player change of volume:" + attributes.volume); this.showSharedVideoMutedPopup(false); @@ -393,17 +418,21 @@ export default class SharedVideoManager { /** * Receives events for local audio mute/unmute by local user. * @param muted boolena whether it is muted or not. + * @param {boolean} indicates if this mute was a result of user interaction, + * i.e. pressing the mute button or it was programatically triggerred */ - localAudioMuted (muted) { + localAudioMuted (muted, userInteraction) { if(!this.player) return; - if(muted) + if (muted) { + this.mutedWithUserInteraction = userInteraction; return; + } // if we are un-muting and player is not muted, lets muted // to not pollute the conference - if(this.player.getVolume() > 0 || !this.player.isMuted()){ + if (this.player.getVolume() > 0 || !this.player.isMuted()) { this.player.setVolume(0); this.showSharedVideoMutedPopup(true); } @@ -415,30 +444,10 @@ export default class SharedVideoManager { * @param show boolean, show or hide the notification */ showMicMutedPopup (show) { - var micMutedPopupSelector = $("#micMutedPopup"); - if(show) { + if(show) this.showSharedVideoMutedPopup(false); - if (!micMutedPopupSelector.is(":visible")) - micMutedPopupSelector.css("display", "inline-block"); - - // FIXME: we need an utility method for that. - micMutedPopupSelector.fadeIn(300, - () => {micMutedPopupSelector.css({opacity: 1});} - ); - - setTimeout( - function () { - micMutedPopupSelector.fadeOut(300, - () => {micMutedPopupSelector.css({opacity: 0});} - ); - }, 5000); - } - else { - micMutedPopupSelector.fadeOut(300, - () => {micMutedPopupSelector.css({opacity: 0});} - ); - } + UIUtil.animateShowElement($("#micMutedPopup"), show, 5000); } /** @@ -448,30 +457,10 @@ export default class SharedVideoManager { * @param show boolean, show or hide the notification */ showSharedVideoMutedPopup (show) { - var sharedVideoMutedPopupSelector = $("#sharedVideoMutedPopup"); - if(show) { + if(show) this.showMicMutedPopup(false); - if (!sharedVideoMutedPopupSelector.is(":visible")) - sharedVideoMutedPopupSelector.css("display", "inline-block"); - - // FIXME: we need an utility method for that. - sharedVideoMutedPopupSelector.fadeIn(300, - () => {sharedVideoMutedPopupSelector.css({opacity: 1});} - ); - - setTimeout( - function () { - sharedVideoMutedPopupSelector.fadeOut(300, - () => {sharedVideoMutedPopupSelector.css({opacity: 0});} - ); - }, 5000); - } - else { - sharedVideoMutedPopupSelector.fadeOut(300, - () => {sharedVideoMutedPopupSelector.css({opacity: 0});} - ); - } + UIUtil.animateShowElement($("#sharedVideoMutedPopup"), show, 5000); } } diff --git a/modules/UI/toolbars/Toolbar.js b/modules/UI/toolbars/Toolbar.js index 7a95487ee..e98abc9cc 100644 --- a/modules/UI/toolbars/Toolbar.js +++ b/modules/UI/toolbars/Toolbar.js @@ -44,12 +44,25 @@ function openLinkDialog () { const buttonHandlers = { "toolbar_button_mute": function () { + let sharedVideoManager = APP.UI.getSharedVideoManager(); + if (APP.conference.audioMuted) { - AnalyticsAdapter.sendEvent('toolbar.audio.unmuted'); - emitter.emit(UIEvents.AUDIO_MUTED, false); + // If there's a shared video with the volume "on" and we aren't + // the video owner, we warn the user + // that currently it's not possible to unmute. + if (sharedVideoManager + && sharedVideoManager.isSharedVideoVolumeOn() + && !sharedVideoManager.isSharedVideoOwner()) { + UIUtil.animateShowElement( + $("#unableToUnmutePopup"), true, 5000); + } + else { + AnalyticsAdapter.sendEvent('toolbar.audio.unmuted'); + emitter.emit(UIEvents.AUDIO_MUTED, false, true); + } } else { AnalyticsAdapter.sendEvent('toolbar.audio.muted'); - emitter.emit(UIEvents.AUDIO_MUTED, true); + emitter.emit(UIEvents.AUDIO_MUTED, true, true); } }, "toolbar_button_camera": function () { diff --git a/modules/UI/util/UIUtil.js b/modules/UI/util/UIUtil.js index 4e3e381e3..0e37b5b11 100644 --- a/modules/UI/util/UIUtil.js +++ b/modules/UI/util/UIUtil.js @@ -166,6 +166,39 @@ */ isVisible(el) { return (el.offsetParent !== null); + }, + + /** + * Shows / hides the element given by {selector} and sets a timeout if the + * {hideDelay} is set to a value > 0. + * @param selector the jquery selector of the element to show/hide. + * @param show a {boolean} that indicates if the element should be shown or + * hidden + * @param hideDelay the value in milliseconds to wait before hiding the + * element + */ + animateShowElement(selector, show, hideDelay) { + if(show) { + if (!selector.is(":visible")) + selector.css("display", "inline-block"); + + selector.fadeIn(300, + () => {selector.css({opacity: 1});} + ); + + if (hideDelay && hideDelay > 0) + setTimeout( + function () { + selector.fadeOut(300, + () => {selector.css({opacity: 0});} + ); + }, hideDelay); + } + else { + selector.fadeOut(300, + () => {selector.css({opacity: 0});} + ); + } } };