Merge pull request #1071 from jitsi/ongoing-work-video-thumbnails
Ongoing work video thumbnails
This commit is contained in:
commit
2fe69d409b
|
@ -1279,10 +1279,6 @@ export default {
|
|||
APP.UI.updateRecordingState(status);
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
|
||||
APP.UI.updateUserStatus(id, status);
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.KICKED, () => {
|
||||
APP.UI.hideStats();
|
||||
APP.UI.notifyKicked();
|
||||
|
|
|
@ -50,6 +50,15 @@
|
|||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute position the element at the top left corner
|
||||
**/
|
||||
@mixin topLeft() {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@mixin absoluteAligning($sizeX, $sizeY) {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
|
|
@ -11,10 +11,10 @@ $hangupFontSize: 2em;
|
|||
$defaultToolbarSize: 50px;
|
||||
|
||||
// Video layout.
|
||||
$thumbnailIndicatorSize: 23px;
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
$thumbnailIndicatorBorder: 0px;
|
||||
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailToolbarHeight: 25px;
|
||||
|
||||
/**
|
||||
* Color variables.
|
||||
|
@ -46,6 +46,7 @@ $thumbnailPictogramColor: #fff;
|
|||
$dominantSpeakerBg: #165ecc;
|
||||
$raiseHandBg: #D6D61E;
|
||||
$audioLevelBg: #44A5FF;
|
||||
$connectionIndicatorBg: #165ecc;
|
||||
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
||||
$videoStateIndicatorColor: $defaultColor;
|
||||
$videoStateIndicatorBackground: $toolbarBackground;
|
||||
|
|
|
@ -58,17 +58,36 @@
|
|||
/**
|
||||
* The toolbar of the video thumbnail.
|
||||
*/
|
||||
.videocontainer__toolbar {
|
||||
.videocontainer__toolbar,
|
||||
.videocontainer__toptoolbar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
width: 100%;
|
||||
box-sizing: border-box; // Includes the padding in the 100% width.
|
||||
height: $thumbnailToolbarHeight;
|
||||
max-height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.videocontainer__toolbar {
|
||||
bottom: 0;
|
||||
padding: 0 5px 0 5px;
|
||||
height: $thumbnailToolbarHeight;
|
||||
}
|
||||
|
||||
.videocontainer__toptoolbar {
|
||||
$toolbarPadding: 5px;
|
||||
top: 0;
|
||||
padding: $toolbarPadding;
|
||||
padding-bottom: 0;
|
||||
height: $thumbnailIndicatorSize + $toolbarPadding;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
background: rgba(0,0,0,.6);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer.videoContainerFocused,
|
||||
|
@ -176,8 +195,10 @@
|
|||
.videocontainer .editdisplayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
width: 40%;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
top: 50%;
|
||||
@include transform(translateY(-40%));
|
||||
color: $participantNameColor;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -200,72 +221,10 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.videocontainer>span.status {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
background: rgba(0,0,0,.7);
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 70%;
|
||||
height: 15%;
|
||||
left: 15%;
|
||||
bottom: 2%;
|
||||
padding: 5px;
|
||||
font-size: 10pt;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
z-index: 2;
|
||||
border-radius:3px;
|
||||
}
|
||||
|
||||
.connectionindicator
|
||||
{
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0;
|
||||
padding: 0px 5px;
|
||||
z-index: 3;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.connection.connection_empty
|
||||
{
|
||||
color: #8B8B8B;/*#FFFFFF*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.connection.connection_lost
|
||||
{
|
||||
color: #8B8B8B;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.connection.connection_full
|
||||
{
|
||||
color: #FFFFFF;/*#15A1ED*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.connection
|
||||
{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
font-size: 8pt;
|
||||
border: 0px;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
#localVideoContainer>span.status:hover,
|
||||
#localVideoContainer .displayname:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.videocontainer>span.status,
|
||||
.videocontainer .displayname {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -278,7 +237,6 @@
|
|||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.videocontainer>a.status,
|
||||
.videocontainer>a.displayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
|
@ -323,25 +281,92 @@
|
|||
margin: 0px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.videocontainer>span.indicator {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
z-index: 3;
|
||||
text-align: center;
|
||||
background: $dominantSpeakerBg;
|
||||
margin: 7px;
|
||||
display: inline-block;
|
||||
color: $thumbnailPictogramColor;
|
||||
font-size: 8pt;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
#raisehandindicator {
|
||||
background: $raiseHandBg;
|
||||
}
|
||||
|
||||
.videocontainer>#raisehandindicator {
|
||||
background: $raiseHandBg;
|
||||
#connectionindicator {
|
||||
background: $connectionIndicatorBg;
|
||||
}
|
||||
|
||||
.videocontainer__toptoolbar span.indicator {
|
||||
position: relative;
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
display: none;
|
||||
padding: 0;
|
||||
margin-right: 5px;
|
||||
float: left;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
z-index: 3;
|
||||
background: $dominantSpeakerBg;
|
||||
color: $thumbnailPictogramColor;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
|
||||
.indicatoricon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
@include transform(translateY(-50%));
|
||||
width: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
|
||||
height: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
|
||||
line-height: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
|
||||
}
|
||||
|
||||
.connection {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: 12px;
|
||||
height: 8px;
|
||||
|
||||
&_empty
|
||||
{
|
||||
@include topLeft();
|
||||
max-width: 12px;
|
||||
width: 12px;
|
||||
color: #8B8B8B;/*#FFFFFF*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&_lost
|
||||
{
|
||||
@include topLeft();
|
||||
max-width: 12px;
|
||||
width: 12px;
|
||||
color: #8B8B8B;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&_full
|
||||
{
|
||||
@include topLeft();
|
||||
max-width: 12px;
|
||||
width: 12px;
|
||||
color: #FFFFFF;/*#15A1ED*/
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-connection,
|
||||
.icon-connection-lost {
|
||||
font-size: 6pt;
|
||||
}
|
||||
}
|
||||
|
||||
.remotevideomenu
|
||||
{
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
margin: 7px;
|
||||
z-index: 3;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
color: #FFF;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -384,12 +409,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
#indicatoricon {
|
||||
width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
}
|
||||
|
||||
#reloadPresentation {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
|
|
@ -254,6 +254,8 @@
|
|||
</span>
|
||||
<audio id="localAudio" autoplay muted></audio>
|
||||
<div class="videocontainer__toolbar"></div>
|
||||
<div class="videocontainer__toptoolbar"></div>
|
||||
<div class="videocontainer__hoverOverlay"></div>
|
||||
</span>
|
||||
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
|
||||
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
|
||||
|
|
|
@ -609,10 +609,6 @@ UI.removeUser = function (id, displayName) {
|
|||
VideoLayout.removeParticipantContainer(id);
|
||||
};
|
||||
|
||||
UI.updateUserStatus = function (id, status) {
|
||||
VideoLayout.setPresenceStatus(id, status);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update videotype for specified user.
|
||||
* @param {string} id user id
|
||||
|
|
|
@ -625,7 +625,7 @@ function SharedVideoThumb (url)
|
|||
this.videoSpanId = "sharedVideoContainer";
|
||||
this.container = this.createContainer(this.videoSpanId);
|
||||
this.container.onclick = this.videoClick.bind(this);
|
||||
|
||||
this.bindHoverHandler();
|
||||
SmallVideo.call(this, VideoLayout);
|
||||
this.isVideoMuted = true;
|
||||
}
|
||||
|
|
|
@ -98,14 +98,27 @@ var JitsiPopover = (function () {
|
|||
var self = this;
|
||||
$(".jitsipopover").on("mouseenter", function () {
|
||||
self.popoverIsHovered = true;
|
||||
if(typeof self.onHoverPopover === "function") {
|
||||
self.onHoverPopover(self.popoverIsHovered);
|
||||
}
|
||||
}).on("mouseleave", function () {
|
||||
self.popoverIsHovered = false;
|
||||
self.hide();
|
||||
if(typeof self.onHoverPopover === "function") {
|
||||
self.onHoverPopover(self.popoverIsHovered);
|
||||
}
|
||||
});
|
||||
|
||||
this.refreshPosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a hover listener to the popover.
|
||||
*/
|
||||
JitsiPopover.prototype.addOnHoverPopover = function (listener) {
|
||||
this.onHoverPopover = listener;
|
||||
};
|
||||
|
||||
/**
|
||||
* Refreshes the position of the popover.
|
||||
*/
|
||||
|
|
|
@ -237,6 +237,18 @@ const TOOLTIP_POSITIONS = {
|
|||
$("#"+id).addClass("hide");
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows / hides the element with the given jQuery selector.
|
||||
*
|
||||
* @param {jQuery} selector the jQuery selector of the element to show/hide
|
||||
* @param {boolean} isVisible
|
||||
*/
|
||||
setVisibility(selector, isVisible) {
|
||||
if (selector && selector.length > 0) {
|
||||
selector.css("visibility", isVisible ? "visible" : "hidden");
|
||||
}
|
||||
},
|
||||
|
||||
hideDisabledButtons: function (mappings) {
|
||||
var selector = Object.keys(mappings)
|
||||
.map(function (buttonName) {
|
||||
|
@ -376,6 +388,49 @@ const TOOLTIP_POSITIONS = {
|
|||
"cursor": "default"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets an "indicator" span for a video thumbnail.
|
||||
* If element doesn't exist then creates it and appends
|
||||
* video span container.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param opts.indicatorId {String} - identificator of indicator
|
||||
* @param opts.videoSpanId {String} - identificator of video span
|
||||
* @param opts.content {String} HTML content of indicator
|
||||
* @param opts.tooltip {String} - tooltip key for translation
|
||||
*
|
||||
* @returns {HTMLSpanElement} indicatorSpan
|
||||
*/
|
||||
getVideoThumbnailIndicatorSpan(opts = {}) {
|
||||
let indicatorId = opts.indicatorId;
|
||||
let videoSpanId = opts.videoSpanId;
|
||||
let indicators = $(`#${videoSpanId} [id="${indicatorId}"]`);
|
||||
let indicatorSpan;
|
||||
|
||||
if (indicators.length <= 0) {
|
||||
indicatorSpan = document.createElement('span');
|
||||
indicatorSpan.className = 'indicator';
|
||||
indicatorSpan.id = indicatorId;
|
||||
|
||||
if(opts.content) {
|
||||
indicatorSpan.innerHTML = opts.content;
|
||||
}
|
||||
|
||||
if (opts.tooltip) {
|
||||
this.setTooltip(indicatorSpan, opts.tooltip, "top");
|
||||
APP.translation.translateElement($(indicatorSpan));
|
||||
}
|
||||
|
||||
document.getElementById(videoSpanId)
|
||||
.querySelector('.videocontainer__toptoolbar')
|
||||
.appendChild(indicatorSpan);
|
||||
} else {
|
||||
indicatorSpan = indicators[0];
|
||||
}
|
||||
|
||||
return indicatorSpan;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
/* jshint -W101 */
|
||||
import JitsiPopover from "../util/JitsiPopover";
|
||||
import VideoLayout from "./VideoLayout";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
/**
|
||||
* Constructs new connection indicator.
|
||||
* @param videoContainer the video container associated with the indicator.
|
||||
* @param videoId the identifier of the video
|
||||
* @constructor
|
||||
*/
|
||||
function ConnectionIndicator(videoContainer, id) {
|
||||
function ConnectionIndicator(videoContainer, videoId) {
|
||||
this.videoContainer = videoContainer;
|
||||
this.bandwidth = null;
|
||||
this.packetLoss = null;
|
||||
|
@ -18,7 +20,7 @@ function ConnectionIndicator(videoContainer, id) {
|
|||
this.isResolutionHD = null;
|
||||
this.transport = [];
|
||||
this.popover = null;
|
||||
this.id = id;
|
||||
this.id = videoId;
|
||||
this.create();
|
||||
}
|
||||
|
||||
|
@ -32,12 +34,12 @@ function ConnectionIndicator(videoContainer, id) {
|
|||
* 0: string}}
|
||||
*/
|
||||
ConnectionIndicator.connectionQualityValues = {
|
||||
98: "18px", //full
|
||||
81: "15px",//4 bars
|
||||
64: "11px",//3 bars
|
||||
47: "7px",//2 bars
|
||||
30: "3px",//1 bar
|
||||
0: "0px"//empty
|
||||
98: "100%", //full
|
||||
81: "80%",//4 bars
|
||||
64: "55%",//3 bars
|
||||
47: "40%",//2 bars
|
||||
30: "20%",//1 bar
|
||||
0: "0"//empty
|
||||
};
|
||||
|
||||
ConnectionIndicator.getIP = function(value) {
|
||||
|
@ -259,15 +261,19 @@ function createIcon(classes, iconClass) {
|
|||
* Creates the indicator
|
||||
*/
|
||||
ConnectionIndicator.prototype.create = function () {
|
||||
this.connectionIndicatorContainer = document.createElement("div");
|
||||
this.connectionIndicatorContainer.className = "connectionindicator";
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
this.videoContainer.container.appendChild(
|
||||
this.connectionIndicatorContainer);
|
||||
this.popover = new JitsiPopover(
|
||||
$("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), {
|
||||
content: "<div class=\"connection-info\" " +
|
||||
"data-i18n='connectionindicator.na'></div>",
|
||||
let indicatorId = 'connectionindicator';
|
||||
let element = UIUtil.getVideoThumbnailIndicatorSpan({
|
||||
videoSpanId: this.videoContainer.videoSpanId,
|
||||
indicatorId
|
||||
});
|
||||
element.classList.add('show');
|
||||
this.connectionIndicatorContainer = element;
|
||||
|
||||
let popoverContent = (
|
||||
`<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
|
||||
);
|
||||
this.popover = new JitsiPopover($(element), {
|
||||
content: popoverContent,
|
||||
skin: "black",
|
||||
onBeforePosition: el => APP.translation.translateElement(el)
|
||||
});
|
||||
|
@ -283,13 +289,19 @@ ConnectionIndicator.prototype.create = function () {
|
|||
origShowFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
|
||||
this.emptyIcon = this.connectionIndicatorContainer.appendChild(
|
||||
createIcon(["connection", "connection_empty"], "icon-connection"));
|
||||
this.fullIcon = this.connectionIndicatorContainer.appendChild(
|
||||
createIcon(["connection", "connection_full"], "icon-connection"));
|
||||
this.interruptedIndicator = this.connectionIndicatorContainer.appendChild(
|
||||
createIcon(["connection", "connection_lost"],"icon-connection-lost"));
|
||||
let connectionIconContainer = document.createElement('div');
|
||||
connectionIconContainer.className = 'connection indicatoricon';
|
||||
|
||||
|
||||
this.emptyIcon = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_empty"], "icon-connection"));
|
||||
this.fullIcon = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_full"], "icon-connection"));
|
||||
this.interruptedIndicator = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_lost"],"icon-connection-lost"));
|
||||
|
||||
$(this.interruptedIndicator).hide();
|
||||
this.connectionIndicatorContainer.appendChild(connectionIconContainer);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -320,7 +332,6 @@ ConnectionIndicator.prototype.updateConnectionStatusIndicator
|
|||
$(this.interruptedIndicator).show();
|
||||
$(this.emptyIcon).hide();
|
||||
$(this.fullIcon).hide();
|
||||
this.updateConnectionQuality(0 /* zero bars */);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -427,4 +438,11 @@ ConnectionIndicator.prototype.updateResolutionIndicator = function () {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a hover listener to the popover.
|
||||
*/
|
||||
ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) {
|
||||
this.popover.addOnHoverPopover(listener);
|
||||
};
|
||||
|
||||
export default ConnectionIndicator;
|
||||
|
|
|
@ -11,6 +11,8 @@ function LocalVideo(VideoLayout, emitter) {
|
|||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.localVideoId = null;
|
||||
this.createConnectionIndicator();
|
||||
this.bindHoverHandler();
|
||||
if(config.enableLocalVideoFlip)
|
||||
this._buildContextMenu();
|
||||
this.isLocal = true;
|
||||
|
@ -27,7 +29,6 @@ function LocalVideo(VideoLayout, emitter) {
|
|||
// Set default display name.
|
||||
this.setDisplayName();
|
||||
|
||||
this.createConnectionIndicator();
|
||||
this.addAudioLevelIndicator();
|
||||
}
|
||||
|
||||
|
@ -70,7 +71,6 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
|
|||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
document.getElementById(this.videoSpanId)
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(nameSpan);
|
||||
|
||||
|
||||
|
@ -104,7 +104,6 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
|
|||
APP.translation.translateElement($(editableText));
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(editableText);
|
||||
|
||||
var self = this;
|
||||
|
@ -115,7 +114,7 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
|
|||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$localDisplayName.hide();
|
||||
UIUtil.setVisibility($localDisplayName, false);
|
||||
$editDisplayName.show();
|
||||
$editDisplayName.focus();
|
||||
$editDisplayName.select();
|
||||
|
@ -123,7 +122,7 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
|
|||
$editDisplayName.one("focusout", function () {
|
||||
self.emitter.emit(UIEvents.NICKNAME_CHANGED, this.value);
|
||||
$editDisplayName.hide();
|
||||
$localDisplayName.show();
|
||||
UIUtil.setVisibility($localDisplayName, true);
|
||||
});
|
||||
|
||||
$editDisplayName.on('keydown', function (e) {
|
||||
|
|
|
@ -31,6 +31,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
|||
this.addRemoteVideoContainer();
|
||||
this.connectionIndicator = new ConnectionIndicator(this, this.id);
|
||||
this.setDisplayName();
|
||||
this.bindHoverHandler();
|
||||
this.flipX = false;
|
||||
this.isLocal = false;
|
||||
/**
|
||||
|
@ -233,11 +234,9 @@ if (!interfaceConfig.filmStripOnly) {
|
|||
RemoteVideo.prototype.addRemoteVideoMenu = function () {
|
||||
|
||||
var spanElement = document.createElement('span');
|
||||
spanElement.className = 'remotevideomenu toolbar-icon right';
|
||||
spanElement.className = 'remotevideomenu';
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(spanElement);
|
||||
this.container.appendChild(spanElement);
|
||||
|
||||
var menuElement = document.createElement('i');
|
||||
menuElement.className = 'icon-menu-up';
|
||||
|
@ -512,9 +511,10 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
|
|||
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
*
|
||||
* @param displayName the display name to set
|
||||
*/
|
||||
RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
RemoteVideo.prototype.setDisplayName = function(displayName) {
|
||||
if (!this.container) {
|
||||
console.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
|
@ -530,10 +530,6 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
|||
if (displaynameSpan.text() !== displayName)
|
||||
displaynameSpan.text(displayName);
|
||||
}
|
||||
else if (key && key.length > 0) {
|
||||
var nameHtml = APP.translation.generateTranslationHTML(key);
|
||||
$('#' + this.videoSpanId + '_name').html(nameHtml);
|
||||
}
|
||||
else
|
||||
$('#' + this.videoSpanId + '_name').text(
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
|
||||
|
@ -541,7 +537,6 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
|||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
$('#' + this.videoSpanId)[0]
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(nameSpan);
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
|
@ -573,10 +568,18 @@ RemoteVideo.createContainer = function (spanId) {
|
|||
container.id = spanId;
|
||||
container.className = 'videocontainer';
|
||||
|
||||
let indicatorBar = document.createElement('div');
|
||||
indicatorBar.className = "videocontainer__toptoolbar";
|
||||
container.appendChild(indicatorBar);
|
||||
|
||||
let toolbar = document.createElement('div');
|
||||
toolbar.className = "videocontainer__toolbar";
|
||||
container.appendChild(toolbar);
|
||||
|
||||
let overlay = document.createElement('div');
|
||||
overlay.className = "videocontainer__hoverOverlay";
|
||||
container.appendChild(overlay);
|
||||
|
||||
var remotes = document.getElementById('remoteVideos');
|
||||
return remotes.appendChild(container);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global $, APP, JitsiMeetJS, interfaceConfig */
|
||||
/* global $, JitsiMeetJS, interfaceConfig */
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
|
@ -21,11 +21,27 @@ const DISPLAY_VIDEO = 0;
|
|||
const DISPLAY_AVATAR = 1;
|
||||
/**
|
||||
* Display mode constant used when neither video nor avatar is being displayed
|
||||
* on the small video.
|
||||
* on the small video. And we just show the display name.
|
||||
* @type {number}
|
||||
* @constant
|
||||
*/
|
||||
const DISPLAY_BLACKNESS = 2;
|
||||
const DISPLAY_BLACKNESS_WITH_NAME = 2;
|
||||
|
||||
/**
|
||||
* Display mode constant used when video is displayed and display name
|
||||
* at the same time.
|
||||
* @type {number}
|
||||
* @constant
|
||||
*/
|
||||
const DISPLAY_VIDEO_WITH_NAME = 3;
|
||||
|
||||
/**
|
||||
* Display mode constant used when neither video nor avatar is being displayed
|
||||
* on the small video. And we just show the display name.
|
||||
* @type {number}
|
||||
* @constant
|
||||
*/
|
||||
const DISPLAY_AVATAR_WITH_NAME = 4;
|
||||
|
||||
function SmallVideo(VideoLayout) {
|
||||
this.isAudioMuted = false;
|
||||
|
@ -34,12 +50,7 @@ function SmallVideo(VideoLayout) {
|
|||
this.videoStream = null;
|
||||
this.audioStream = null;
|
||||
this.VideoLayout = VideoLayout;
|
||||
}
|
||||
|
||||
function setVisibility(selector, show) {
|
||||
if (selector && selector.length > 0) {
|
||||
selector.css("visibility", show ? "visible" : "hidden");
|
||||
}
|
||||
this.videoIsHovered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,18 +71,6 @@ SmallVideo.prototype.isVisible = function () {
|
|||
return $('#' + this.videoSpanId).is(':visible');
|
||||
};
|
||||
|
||||
SmallVideo.prototype.showDisplayName = function(isShow) {
|
||||
var nameSpan = $('#' + this.videoSpanId + ' .displayname').get(0);
|
||||
if (isShow) {
|
||||
if (nameSpan && nameSpan.innerHTML && nameSpan.innerHTML.length)
|
||||
nameSpan.setAttribute("style", "display:inline-block;");
|
||||
}
|
||||
else {
|
||||
if (nameSpan)
|
||||
nameSpan.setAttribute("style", "display:none;");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables / disables the device availability icons for this small video.
|
||||
* @param {enable} set to {true} to enable and {false} to disable
|
||||
|
@ -132,37 +131,6 @@ SmallVideo.prototype.getVideoType = function () {
|
|||
return this.videoType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the presence status message for the given video.
|
||||
*/
|
||||
SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
|
||||
if (!this.container) {
|
||||
// No container
|
||||
return;
|
||||
}
|
||||
|
||||
var statusSpan = $('#' + this.videoSpanId + '>span.status');
|
||||
if (!statusSpan.length) {
|
||||
//Add status span
|
||||
statusSpan = document.createElement('span');
|
||||
statusSpan.className = 'status';
|
||||
statusSpan.id = this.videoSpanId + '_status';
|
||||
$('#' + this.videoSpanId)[0].appendChild(statusSpan);
|
||||
|
||||
statusSpan = $('#' + this.videoSpanId + '>span.status');
|
||||
}
|
||||
|
||||
// Display status
|
||||
if (statusMsg && statusMsg.length) {
|
||||
$('#' + this.videoSpanId + '_status').text(statusMsg);
|
||||
statusSpan.get(0).setAttribute("style", "display:inline-block;");
|
||||
}
|
||||
else {
|
||||
// Hide
|
||||
statusSpan.get(0).setAttribute("style", "display:none;");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an audio or video element for a particular MediaStream.
|
||||
*/
|
||||
|
@ -192,6 +160,29 @@ SmallVideo.getStreamElementID = function (stream) {
|
|||
return (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures hoverIn/hoverOut handlers. Depends on connection indicator.
|
||||
*/
|
||||
SmallVideo.prototype.bindHoverHandler = function () {
|
||||
// Add hover handler
|
||||
$(this.container).hover(
|
||||
() => {
|
||||
this.videoIsHovered = true;
|
||||
this.updateView();
|
||||
},
|
||||
() => {
|
||||
this.videoIsHovered = false;
|
||||
this.updateView();
|
||||
}
|
||||
);
|
||||
if (this.connectionIndicator) {
|
||||
this.connectionIndicator.addPopoverHoverListener(
|
||||
() => {
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the data for the indicator
|
||||
* @param id the id of the indicator
|
||||
|
@ -395,6 +386,16 @@ SmallVideo.prototype.$avatar = function () {
|
|||
return $('#' + this.videoSpanId + ' .userAvatar');
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the display name element, which appears on the video thumbnail.
|
||||
*
|
||||
* @return {jQuery} a jQuery selector pointing to the display name element of
|
||||
* the video thumbnail
|
||||
*/
|
||||
SmallVideo.prototype.$displayName = function () {
|
||||
return $('#' + this.videoSpanId + ' .displayname');
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables / disables the css responsible for focusing/pinning a video
|
||||
* thumbnail.
|
||||
|
@ -445,19 +446,35 @@ SmallVideo.prototype.isVideoPlayable = function() {
|
|||
* Determines what should be display on the thumbnail.
|
||||
*
|
||||
* @return {number} one of <tt>DISPLAY_VIDEO</tt>,<tt>DISPLAY_AVATAR</tt>
|
||||
* or <tt>DISPLAY_BLACKNESS</tt>.
|
||||
* or <tt>DISPLAY_BLACKNESS_WITH_NAME</tt>.
|
||||
*/
|
||||
SmallVideo.prototype.selectDisplayMode = function() {
|
||||
// Display name is always and only displayed when user is on the stage
|
||||
if (this.isCurrentlyOnLargeVideo()) {
|
||||
return DISPLAY_BLACKNESS;
|
||||
return DISPLAY_BLACKNESS_WITH_NAME;
|
||||
} else if (this.isVideoPlayable() && this.selectVideoElement().length) {
|
||||
return DISPLAY_VIDEO;
|
||||
// check hovering and change state to video with name
|
||||
return this._isHovered() ?
|
||||
DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
|
||||
} else {
|
||||
return DISPLAY_AVATAR;
|
||||
// check hovering and change state to avatar with name
|
||||
return this._isHovered() ?
|
||||
DISPLAY_AVATAR_WITH_NAME : DISPLAY_AVATAR;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether current video is considered hovered. Currently it is hovered
|
||||
* if the mouse is over the video, or if the connection
|
||||
* indicator is shown(hovered).
|
||||
* @private
|
||||
*/
|
||||
SmallVideo.prototype._isHovered = function () {
|
||||
return this.videoIsHovered
|
||||
|| (this.connectionIndicator
|
||||
&& this.connectionIndicator.popover.popoverIsHovered);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides or shows the user's avatar.
|
||||
* This update assumes that large video had been updated and we will
|
||||
|
@ -479,10 +496,25 @@ SmallVideo.prototype.updateView = function () {
|
|||
|
||||
// Determine whether video, avatar or blackness should be displayed
|
||||
let displayMode = this.selectDisplayMode();
|
||||
// Show/hide video
|
||||
setVisibility(this.selectVideoElement(), displayMode === DISPLAY_VIDEO);
|
||||
// Show/hide the avatar
|
||||
setVisibility(this.$avatar(), displayMode === DISPLAY_AVATAR);
|
||||
// Show/hide video.
|
||||
UIUtil.setVisibility( this.selectVideoElement(),
|
||||
(displayMode === DISPLAY_VIDEO
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME));
|
||||
// Show/hide the avatar.
|
||||
UIUtil.setVisibility( this.$avatar(),
|
||||
(displayMode === DISPLAY_AVATAR
|
||||
|| displayMode === DISPLAY_AVATAR_WITH_NAME));
|
||||
// Show/hide the display name.
|
||||
UIUtil.setVisibility( this.$displayName(),
|
||||
(displayMode === DISPLAY_BLACKNESS_WITH_NAME
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME
|
||||
|| displayMode === DISPLAY_AVATAR_WITH_NAME));
|
||||
// show hide overlay when there is a video or avatar under
|
||||
// the display name
|
||||
UIUtil.setVisibility( $('#' + this.videoSpanId
|
||||
+ ' .videocontainer__hoverOverlay'),
|
||||
(displayMode === DISPLAY_AVATAR_WITH_NAME
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME));
|
||||
};
|
||||
|
||||
SmallVideo.prototype.avatarChanged = function (avatarUrl) {
|
||||
|
@ -519,13 +551,21 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
|
|||
return;
|
||||
}
|
||||
|
||||
var indicatorSpan = this.getIndicatorSpan({
|
||||
id: 'dominantspeakerindicator',
|
||||
content: '<i id="indicatoricon" class="fa fa-bullhorn"></i>',
|
||||
let indicatorSpanId = "dominantspeakerindicator";
|
||||
let content = `<i id="indicatoricon"
|
||||
' class="indicatoricon fa fa-bullhorn"></i>`;
|
||||
let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
|
||||
videoSpanId: this.videoSpanId,
|
||||
indicatorId: indicatorSpanId,
|
||||
content,
|
||||
tooltip: 'speaker'
|
||||
});
|
||||
|
||||
indicatorSpan.style.display = show ? "" : "none";
|
||||
if (show) {
|
||||
indicatorSpan.classList.add('show');
|
||||
} else {
|
||||
indicatorSpan.classList.remove('show');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -539,43 +579,21 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) {
|
|||
return;
|
||||
}
|
||||
|
||||
var indicatorSpan = this.getIndicatorSpan({
|
||||
id: 'raisehandindicator',
|
||||
content: '<i id="indicatoricon" class="icon-raised-hand"></i>',
|
||||
let indicatorSpanId = "raisehandindicator";
|
||||
let content = `<i id="indicatoricon"
|
||||
class="icon-raised-hand indicatoricon"></i>`;
|
||||
let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
|
||||
indicatorId: indicatorSpanId,
|
||||
videoSpanId: this.videoSpanId,
|
||||
content,
|
||||
tooltip: 'raisedHand'
|
||||
});
|
||||
|
||||
indicatorSpan.style.display = show ? "" : "none";
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets (creating if necessary) the "indicator" span for this SmallVideo.
|
||||
*
|
||||
* @param options.id {String} element ID
|
||||
* @param options.content {String} HTML content of the indicator
|
||||
* @param options.tooltip {String} The key that should be passed to tooltip
|
||||
*
|
||||
* @returns {HTMLElement} DOM represention of the indicator
|
||||
*/
|
||||
SmallVideo.prototype.getIndicatorSpan = function(options) {
|
||||
var indicator = this.container.querySelector('#' + options.id);
|
||||
|
||||
if (indicator) {
|
||||
return indicator;
|
||||
if (show) {
|
||||
indicatorSpan.classList.add('show');
|
||||
} else {
|
||||
indicatorSpan.classList.remove('show');
|
||||
}
|
||||
|
||||
indicator = document.createElement('span');
|
||||
indicator.className = 'indicator';
|
||||
indicator.id = options.id;
|
||||
|
||||
indicator.innerHTML = options.content;
|
||||
|
||||
UIUtil.setTooltip(indicator, options.tooltip, "top");
|
||||
APP.translation.translateElement($(indicator));
|
||||
|
||||
this.container.appendChild(indicator);
|
||||
|
||||
return indicator;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -449,15 +449,6 @@ var VideoLayout = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the presence status message for the given video.
|
||||
*/
|
||||
setPresenceStatus (id, statusMsg) {
|
||||
let remoteVideo = remoteVideos[id];
|
||||
if (remoteVideo)
|
||||
remoteVideo.setPresenceStatus(statusMsg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a visual indicator for the moderator of the conference.
|
||||
* On local or remote participants.
|
||||
|
|
Loading…
Reference in New Issue