From 4b6ac38058eba70bd1fb1b2824243f9238432c89 Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Fri, 10 Jun 2016 12:43:29 -0500 Subject: [PATCH 1/2] Fixes a failure to show the dominant speaker indicator for the local participant. --- modules/UI/videolayout/VideoLayout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 5f2a785b4..bb72899a8 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -577,9 +577,9 @@ var VideoLayout = { if(oldSpeakerRemoteVideo) { oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false); - localVideoThumbnail.updateDominantSpeakerIndicator(true); currentDominantSpeaker = null; } + localVideoThumbnail.updateDominantSpeakerIndicator(true); return; } From 2d2e27b8d06836c4ad8bdb863de2c5f5a615219c Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Mon, 20 Jun 2016 16:13:17 -0500 Subject: [PATCH 2/2] Implements "raised hand". --- conference.js | 50 ++++++++++++++ css/videolayout_default.css | 4 +- lang/main.json | 4 +- modules/UI/UI.js | 20 +++++- modules/UI/util/MessageHandler.js | 15 +++-- modules/UI/videolayout/SmallVideo.js | 70 ++++++++++++++------ modules/UI/videolayout/VideoLayout.js | 22 ++++-- modules/keyboardshortcut/keyboardshortcut.js | 7 ++ 8 files changed, 158 insertions(+), 34 deletions(-) diff --git a/conference.js b/conference.js index eb4bbe04b..3c5915098 100644 --- a/conference.js +++ b/conference.js @@ -394,6 +394,14 @@ export default { videoMuted: false, isSharingScreen: false, isDesktopSharingEnabled: false, + /* + * Whether the local "raisedHand" flag is on. + */ + isHandRaised: false, + /* + * Whether the local participant is the dominant speaker in the conference. + */ + isDominantSpeaker: false, /** * Open new connection and join to the conference. * @param {object} options @@ -1018,6 +1026,16 @@ export default { APP.UI.handleLastNEndpoints(ids, enteringIds); }); room.on(ConferenceEvents.DOMINANT_SPEAKER_CHANGED, (id) => { + if (this.isLocalId(id)) { + this.isDominantSpeaker = true; + this.setRaisedHand(false); + } else { + this.isDominantSpeaker = false; + var participant = room.getParticipantById(id); + if (participant) { + APP.UI.setRaisedHandStatus(participant, false); + } + } APP.UI.markDominantSpeaker(id); }); @@ -1040,6 +1058,13 @@ export default { APP.UI.changeDisplayName(id, displayName); }); + room.on(ConferenceEvents.PARTICIPANT_PROPERTY_CHANGED, + (participant, name, oldValue, newValue) => { + if (name === "raisedHand") { + APP.UI.setRaisedHandStatus(participant, newValue); + } + }); + room.on(ConferenceEvents.RECORDER_STATE_CHANGED, (status, error) => { console.log("Received recorder status change: ", status, error); APP.UI.updateRecordingState(status); @@ -1432,5 +1457,30 @@ export default { mediaDeviceHelper.setCurrentMediaDevices(devices); APP.UI.onAvailableDevicesChanged(devices); }); + }, + + /** + * Toggles the local "raised hand" status, if the current state allows + * toggling. + */ + maybeToggleRaisedHand() { + // If we are the dominant speaker, we don't enable "raise hand". + if (this.isHandRaised || !this.isDominantSpeaker) { + this.setRaisedHand(!this.isHandRaised); + } + }, + + /** + * Sets the local "raised hand" status to a particular value. + */ + setRaisedHand(raisedHand) { + if (raisedHand !== this.isHandRaised) + { + this.isHandRaised = raisedHand; + // Advertise the updated status + room.setLocalParticipantProperty("raisedHand", raisedHand); + // Update the view + APP.UI.setLocalRaisedHandStatus(raisedHand); + } } }; \ No newline at end of file diff --git a/css/videolayout_default.css b/css/videolayout_default.css index 9d16b77e2..62dcdcbd6 100644 --- a/css/videolayout_default.css +++ b/css/videolayout_default.css @@ -310,7 +310,7 @@ z-index: 3; } -.videocontainer>span.dominantspeakerindicator { +.videocontainer>span.indicator { bottom: 0px; left: 0px; width: 25px; @@ -327,7 +327,7 @@ border: 0px; } -#speakerindicatoricon { +#indicatoricon { padding-top: 5px; } diff --git a/lang/main.json b/lang/main.json index e947c3f72..a35e4e57c 100644 --- a/lang/main.json +++ b/lang/main.json @@ -8,6 +8,7 @@ "participant": "Participant", "me": "me", "speaker": "Speaker", + "raisedHand": "Would like to speak", "defaultNickname": "ex. Jane Pink", "defaultLink": "e.g. __url__", "calling": "Calling __name__ ...", @@ -154,7 +155,8 @@ "grantedTo": "Moderator rights granted to __to__!", "grantedToUnknown": "Moderator rights granted to $t(somebody)!", "muted": "You have started the conversation muted.", - "mutedTitle": "You're muted!" + "mutedTitle": "You're muted!", + "raisedHand": "Would like to speak." }, "dialog": { "kickMessage": "Ouch! You have been kicked out of the meet!", diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 300e00a7e..95baabf45 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -255,7 +255,25 @@ UI.changeDisplayName = function (id, displayName) { }; /** - * Intitialize conference UI. + * Sets the "raised hand" status for a participant. + */ +UI.setRaisedHandStatus = (participant, raisedHandStatus) => { + VideoLayout.setRaisedHandStatus(participant.getId(), raisedHandStatus); + if (raisedHandStatus) { + messageHandler.notify(participant.getDisplayName(), 'notify.somebody', + 'connected', 'notify.raisedHand'); + } +}; + +/** + * Sets the local "raised hand" status. + */ +UI.setLocalRaisedHandStatus = (raisedHandStatus) => { + VideoLayout.setRaisedHandStatus(APP.conference.localId, raisedHandStatus); +}; + +/** + * Initialize conference UI. */ UI.initConference = function () { let id = APP.conference.localId; diff --git a/modules/UI/util/MessageHandler.js b/modules/UI/util/MessageHandler.js index fdf274481..b450361f2 100644 --- a/modules/UI/util/MessageHandler.js +++ b/modules/UI/util/MessageHandler.js @@ -215,16 +215,19 @@ var messageHandler = { }, /** - * Displayes notification. - * @param displayName display name of the participant that is associated with the notification. - * @param displayNameKey the key from the language file for the display name. + * Displays a notification. + * @param displayName the display name of the participant that is + * associated with the notification. + * @param displayNameKey the key from the language file for the display + * name. Only used if displayName i not provided. * @param cls css class for the notification - * @param messageKey the key from the language file for the text of the message. + * @param messageKey the key from the language file for the text of the + * message. * @param messageArguments object with the arguments for the message. * @param options object with language options. */ - notify: function(displayName, displayNameKey, - cls, messageKey, messageArguments, options) { + notify: function(displayName, displayNameKey, cls, messageKey, + messageArguments, options) { if(!notificationsEnabled) return; diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index a750e1156..37a5d1c2a 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -422,37 +422,69 @@ SmallVideo.prototype.avatarChanged = function (avatarUrl) { }; /** - * Updates the Indicator for dominant speaker. - * - * @param isSpeaker indicates the current indicator state + * Shows or hides the dominant speaker indicator. + * @param show whether to show or hide. */ -SmallVideo.prototype.updateDominantSpeakerIndicator = function (isSpeaker) { - +SmallVideo.prototype.showDominantSpeakerIndicator = function (show) { if (!this.container) { console.warn( "Unable to set dominant speaker indicator - " + this.videoSpanId + " does not exist"); return; } - var indicatorSpan - = $('#' + this.videoSpanId + '>span.dominantspeakerindicator'); + var indicatorSpanId = "dominantspeakerindicator"; + var indicatorSpan = this.getIndicatorSpan(indicatorSpanId); - // If we do not have an indicator for this video. - if (indicatorSpan.length <= 0) { - indicatorSpan = document.createElement('span'); + indicatorSpan.innerHTML + = ""; + // adds a tooltip + UIUtil.setTooltip(indicatorSpan, "speaker", "left"); + APP.translation.translateElement($(indicatorSpan)); - indicatorSpan.innerHTML - = ""; - indicatorSpan.className = 'dominantspeakerindicator'; + $(indicatorSpan).css("visibility", show ? "visible" : "hidden"); +}; - $('#' + this.videoSpanId)[0].appendChild(indicatorSpan); - - // adds a tooltip - UIUtil.setTooltip(indicatorSpan, "speaker", "left"); - APP.translation.translateElement($(indicatorSpan)); +/** + * Shows or hides the raised hand indicator. + * @param show whether to show or hide. + */ +SmallVideo.prototype.showRaisedHandIndicator = function (show) { + if (!this.container) { + console.warn( "Unable to raised hand indication - " + + this.videoSpanId + " does not exist"); + return; } - $(indicatorSpan).css("visibility", isSpeaker ? "visible" : "hidden"); + var indicatorSpanId = "raisehandindicator"; + var indicatorSpan = this.getIndicatorSpan(indicatorSpanId); + + indicatorSpan.style.background = "#D6D61E"; + indicatorSpan.innerHTML + = ""; + + // adds a tooltip + UIUtil.setTooltip(indicatorSpan, "raisedHand", "left"); + APP.translation.translateElement($(indicatorSpan)); + + $(indicatorSpan).css("visibility", show ? "visible" : "hidden"); +}; + +/** + * Gets (creating if necessary) the "indicator" span for this SmallVideo + identified by an ID. + */ +SmallVideo.prototype.getIndicatorSpan = function(id) { + var indicatorSpan; + var spans = $(`#${this.videoSpanId}>[id=${id}`); + if (spans.length <= 0) { + indicatorSpan = document.createElement('span'); + indicatorSpan.id = id; + indicatorSpan.className = "indicator"; + $('#' + this.videoSpanId)[0].appendChild(indicatorSpan); + } else { + indicatorSpan = spans[0]; + } + return indicatorSpan; }; export default SmallVideo; diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index bb72899a8..c47be8854 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -562,6 +562,18 @@ var VideoLayout = { } }, + /** + * Sets the "raised hand" status for a participant identified by 'id'. + */ + setRaisedHandStatus(id, raisedHandStatus) { + var video + = APP.conference.isLocalId(id) + ? localVideoThumbnail : remoteVideos[id]; + if (video) { + video.showRaisedHandIndicator(raisedHandStatus); + } + }, + /** * On dominant speaker changed event. */ @@ -576,10 +588,10 @@ var VideoLayout = { if (APP.conference.isLocalId(id)) { if(oldSpeakerRemoteVideo) { - oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false); + oldSpeakerRemoteVideo.showDominantSpeakerIndicator(false); currentDominantSpeaker = null; } - localVideoThumbnail.updateDominantSpeakerIndicator(true); + localVideoThumbnail.showDominantSpeakerIndicator(true); return; } @@ -589,12 +601,12 @@ var VideoLayout = { } // Update the current dominant speaker. - remoteVideo.updateDominantSpeakerIndicator(true); - localVideoThumbnail.updateDominantSpeakerIndicator(false); + remoteVideo.showDominantSpeakerIndicator(true); + localVideoThumbnail.showDominantSpeakerIndicator(false); // let's remove the indications from the remote video if any if (oldSpeakerRemoteVideo) { - oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false); + oldSpeakerRemoteVideo.showDominantSpeakerIndicator(false); } currentDominantSpeaker = id; diff --git a/modules/keyboardshortcut/keyboardshortcut.js b/modules/keyboardshortcut/keyboardshortcut.js index 1d129c2b0..fbcb67e9e 100644 --- a/modules/keyboardshortcut/keyboardshortcut.js +++ b/modules/keyboardshortcut/keyboardshortcut.js @@ -40,6 +40,13 @@ function initShortcutHandlers() { APP.conference.toggleAudioMuted(); } }, + 82: { + character: "R", + function: function() { + APP.conference.maybeToggleRaisedHand(); + } + + }, 84: { character: "T", function: function() {