diff --git a/conference.js b/conference.js
index 82537e97c..b233261a2 100644
--- a/conference.js
+++ b/conference.js
@@ -341,6 +341,14 @@ export default {
videoMuted: false,
isSharingScreen: false,
isDesktopSharingEnabled: false,
+ /*
+ * Whether the "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
@@ -1199,6 +1207,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);
});
@@ -1221,6 +1239,11 @@ export default {
APP.UI.changeDisplayName(id, displayName);
});
+ room.on(ConferenceEvents.RAISED_HAND_STATUS_CHANGED,
+ (participant, raisedHandStatus) => {
+ APP.UI.setRaisedHandStatus(participant, raisedHandStatus);
+ });
+
room.on(ConferenceEvents.RECORDER_STATE_CHANGED, (status, error) => {
console.log("Received recorder status change: ", status, error);
APP.UI.updateRecordingState(status);
@@ -1525,5 +1548,29 @@ export default {
*/
addConferenceListener(eventName, callBack) {
room.on(eventName, callBack);
+ },
+
+ /**
+ * Toggle the "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.dominantSpeaker) {
+ this.setRaisedHand(!this.isHandRaised);
+ }
+ },
+
+ /**
+ * Sets the "raised hand" status to a particular value.
+ */
+ setRaisedHand(raisedHand) {
+ if (raisedHand !== this.isHandRaised)
+ {
+ this.isHandRaised = raisedHand;
+ // Advertise the updated status
+ room.setRaisedHand(raisedHand);
+ // Update the view
+ APP.UI.setLocalRaisedHandStatus(raisedHand);
+ }
}
};
diff --git a/css/videolayout_default.css b/css/videolayout_default.css
index 4767ec059..30d6bef69 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;
}
@@ -522,4 +522,4 @@
}
.hidden {
-}
\ No newline at end of file
+}
diff --git a/lang/main.json b/lang/main.json
index f29084d0e..824cc6231 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__",
"welcomepage":{
@@ -139,7 +140,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 7f3d6a788..ad550d536 100644
--- a/modules/UI/UI.js
+++ b/modules/UI/UI.js
@@ -254,7 +254,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 5f2a785b4..2be954e1b 100644
--- a/modules/UI/videolayout/VideoLayout.js
+++ b/modules/UI/videolayout/VideoLayout.js
@@ -562,6 +562,19 @@ 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);
+ }
+ // TODO: also display a message?
+ },
+
/**
* On dominant speaker changed event.
*/
@@ -576,10 +589,11 @@ var VideoLayout = {
if (APP.conference.isLocalId(id)) {
if(oldSpeakerRemoteVideo)
{
- oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false);
- localVideoThumbnail.updateDominantSpeakerIndicator(true);
+ oldSpeakerRemoteVideo.showDominantSpeakerIndicator(false);
+ // XXX Why do we not set this to id?
currentDominantSpeaker = null;
}
+ localVideoThumbnail.showDominantSpeakerIndicator(true);
return;
}
@@ -589,12 +603,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 4f48af97d..5e623c9dd 100644
--- a/modules/keyboardshortcut/keyboardshortcut.js
+++ b/modules/keyboardshortcut/keyboardshortcut.js
@@ -31,6 +31,13 @@ function initShortcutHandlers() {
APP.conference.toggleAudioMuted();
}
},
+ 82: {
+ character: "R",
+ function: function() {
+ APP.conference.maybeToggleRaisedHand();
+ }
+
+ },
84: {
character: "T",
function: function() {