Ongoing work on video thumbnail layout

This commit is contained in:
yanas 2016-10-25 22:05:32 -05:00 committed by damencho
parent 3fe43abdea
commit a17a98991c
10 changed files with 171 additions and 193 deletions

View File

@ -1279,10 +1279,6 @@ export default {
APP.UI.updateRecordingState(status); APP.UI.updateRecordingState(status);
}); });
room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
APP.UI.updateUserStatus(id, status);
});
room.on(ConferenceEvents.KICKED, () => { room.on(ConferenceEvents.KICKED, () => {
APP.UI.hideStats(); APP.UI.hideStats();
APP.UI.notifyKicked(); APP.UI.notifyKicked();

View File

@ -11,10 +11,10 @@ $hangupFontSize: 2em;
$defaultToolbarSize: 50px; $defaultToolbarSize: 50px;
// Video layout. // Video layout.
$thumbnailIndicatorSize: 23px; $thumbnailToolbarHeight: 22px;
$thumbnailIndicatorBorder: 0px; $thumbnailIndicatorBorder: 0px;
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
$thumbnailVideoMargin: 2px; $thumbnailVideoMargin: 2px;
$thumbnailToolbarHeight: 25px;
/** /**
* Color variables. * Color variables.
@ -46,6 +46,7 @@ $thumbnailPictogramColor: #fff;
$dominantSpeakerBg: #165ecc; $dominantSpeakerBg: #165ecc;
$raiseHandBg: #D6D61E; $raiseHandBg: #D6D61E;
$audioLevelBg: #44A5FF; $audioLevelBg: #44A5FF;
$connectionIndicatorBg: #48CC8C;
$audioLevelShadow: rgba(9, 36, 77, 0.9); $audioLevelShadow: rgba(9, 36, 77, 0.9);
$videoStateIndicatorColor: $defaultColor; $videoStateIndicatorColor: $defaultColor;
$videoStateIndicatorBackground: $toolbarBackground; $videoStateIndicatorBackground: $toolbarBackground;

View File

@ -58,18 +58,25 @@
/** /**
* The toolbar of the video thumbnail. * The toolbar of the video thumbnail.
*/ */
.videocontainer__toolbar { .videocontainer__toolbar,
.videocontainer__toptoolbar {
position: absolute; position: absolute;
bottom: 0;
left: 0; left: 0;
z-index: 1; z-index: 1;
width: 100%; width: 100%;
box-sizing: border-box; // Includes the padding in the 100% width. box-sizing: border-box; // Includes the padding in the 100% width.
height: $thumbnailToolbarHeight; height: $thumbnailToolbarHeight;
max-height: 100%;
padding: 0 5px 0 5px; padding: 0 5px 0 5px;
} }
.videocontainer__toolbar {
bottom: 0;
}
.videocontainer__toptoolbar {
top: 0;
}
#remoteVideos .videocontainer.videoContainerFocused, #remoteVideos .videocontainer.videoContainerFocused,
#remoteVideos .videocontainer:hover { #remoteVideos .videocontainer:hover {
cursor: hand; cursor: hand;
@ -201,72 +208,10 @@
padding: 0; 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 { #localVideoContainer .displayname:hover {
cursor: text; cursor: text;
} }
.videocontainer>span.status,
.videocontainer .displayname { .videocontainer .displayname {
pointer-events: none; pointer-events: none;
} }
@ -279,7 +224,6 @@
pointer-events: auto !important; pointer-events: auto !important;
} }
.videocontainer>a.status,
.videocontainer>a.displayname { .videocontainer>a.displayname {
display: inline-block; display: inline-block;
position: absolute; position: absolute;
@ -324,25 +268,81 @@
margin: 0px 0px 0px 5px; margin: 0px 0px 0px 5px;
} }
.videocontainer>span.indicator { #raisehandindicator {
position: absolute; background: $raiseHandBg;
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;
} }
.videocontainer>#raisehandindicator { #connectionindicator {
background: $raiseHandBg; background: $connectionIndicatorBg;
}
.videocontainer__toptoolbar span.indicator {
font-size: 8pt;
text-align: center;
line-height: $thumbnailToolbarHeight;
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 {
width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
margin-left: 5px; // TOP toolbar padding;
}
.connection.connection_empty
{
color: #8B8B8B;/*#FFFFFF*/
overflow: hidden;
}
.connection.connection_lost
{
color: #8B8B8B;
overflow: visible;
}
.connection.connection_full
{
color: #FFFFFF;/*#15A1ED*/
overflow: hidden;
}
.connection,
.icon-connection,
.icon-connection-lost {
font-size: 6pt;
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
}
.connection
{
position: absolute;
left: 0px;
top: 0px;
border: 0px;
}
}
.remotevideomenu
{
display: inline-block;
position: absolute;
top: 0px;
right: 0;
margin: 7px;
z-index: 3;
width: 18px;
height: 13px;
color: #FFF;
font-size: 8pt;
} }
/** /**
@ -385,12 +385,6 @@
} }
} }
#indicatoricon {
width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
}
#reloadPresentation { #reloadPresentation {
display: none; display: none;
position: absolute; position: absolute;

View File

@ -254,6 +254,7 @@
</span> </span>
<audio id="localAudio" autoplay muted></audio> <audio id="localAudio" autoplay muted></audio>
<div class="videocontainer__toolbar"></div> <div class="videocontainer__toolbar"></div>
<div class="videocontainer__toptoolbar"></div>
</span> </span>
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio> <audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio> <audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>

View File

@ -609,10 +609,6 @@ UI.removeUser = function (id, displayName) {
VideoLayout.removeParticipantContainer(id); VideoLayout.removeParticipantContainer(id);
}; };
UI.updateUserStatus = function (id, status) {
VideoLayout.setPresenceStatus(id, status);
};
/** /**
* Update videotype for specified user. * Update videotype for specified user.
* @param {string} id user id * @param {string} id user id

View File

@ -388,6 +388,45 @@ const TOOLTIP_POSITIONS = {
"cursor": "default" "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} #${indicatorId}]`);
let indicatorSpan;
if (indicators.length <= 0) {
indicatorSpan = document.createElement('span');
indicatorSpan.className = 'indicator';
indicatorSpan.id = indicatorId;
indicatorSpan.innerHTML = opts.content;
this.setTooltip(indicatorSpan, opts.tooltip, "top");
APP.translation.translateElement($(indicatorSpan));
document.getElementById(videoSpanId)
.querySelector('.videocontainer__toptoolbar')
.appendChild(indicatorSpan);
} else {
indicatorSpan = indicators[0];
}
return indicatorSpan;
} }
}; };

View File

@ -2,13 +2,15 @@
/* jshint -W101 */ /* jshint -W101 */
import JitsiPopover from "../util/JitsiPopover"; import JitsiPopover from "../util/JitsiPopover";
import VideoLayout from "./VideoLayout"; import VideoLayout from "./VideoLayout";
import UIUtil from "../util/UIUtil";
/** /**
* Constructs new connection indicator. * Constructs new connection indicator.
* @param videoContainer the video container associated with the indicator. * @param videoContainer the video container associated with the indicator.
* @param videoId the identifier of the video
* @constructor * @constructor
*/ */
function ConnectionIndicator(videoContainer, id) { function ConnectionIndicator(videoContainer, videoId) {
this.videoContainer = videoContainer; this.videoContainer = videoContainer;
this.bandwidth = null; this.bandwidth = null;
this.packetLoss = null; this.packetLoss = null;
@ -18,7 +20,7 @@ function ConnectionIndicator(videoContainer, id) {
this.isResolutionHD = null; this.isResolutionHD = null;
this.transport = []; this.transport = [];
this.popover = null; this.popover = null;
this.id = id; this.id = videoId;
this.create(); this.create();
} }
@ -247,6 +249,7 @@ ConnectionIndicator.prototype.showMore = function () {
function createIcon(classes, iconClass) { function createIcon(classes, iconClass) {
var icon = document.createElement("span"); var icon = document.createElement("span");
icon.classList.add("indicatoricon");
for(var i in classes) { for(var i in classes) {
icon.classList.add(classes[i]); icon.classList.add(classes[i]);
} }
@ -259,15 +262,19 @@ function createIcon(classes, iconClass) {
* Creates the indicator * Creates the indicator
*/ */
ConnectionIndicator.prototype.create = function () { ConnectionIndicator.prototype.create = function () {
this.connectionIndicatorContainer = document.createElement("div"); let indicatorId = 'connectionindicator';
this.connectionIndicatorContainer.className = "connectionindicator"; let element = UIUtil.getVideoThumbnailIndicatorSpan({
this.connectionIndicatorContainer.style.display = "none"; videoSpanId: this.videoContainer.videoSpanId,
this.videoContainer.container.appendChild( indicatorId
this.connectionIndicatorContainer); });
this.popover = new JitsiPopover( element.classList.add('hide');
$("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), { this.connectionIndicatorContainer = element;
content: "<div class=\"connection-info\" " +
"data-i18n='connectionindicator.na'></div>", let popoverContent = (
`<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
);
this.popover = new JitsiPopover(element, {
content: popoverContent,
skin: "black", skin: "black",
onBeforePosition: el => APP.translation.translateElement(el) onBeforePosition: el => APP.translation.translateElement(el)
}); });

View File

@ -233,11 +233,9 @@ if (!interfaceConfig.filmStripOnly) {
RemoteVideo.prototype.addRemoteVideoMenu = function () { RemoteVideo.prototype.addRemoteVideoMenu = function () {
var spanElement = document.createElement('span'); var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu toolbar-icon right'; spanElement.className = 'remotevideomenu';
this.container this.container.appendChild(spanElement);
.querySelector('.videocontainer__toolbar')
.appendChild(spanElement);
var menuElement = document.createElement('i'); var menuElement = document.createElement('i');
menuElement.className = 'icon-menu-up'; menuElement.className = 'icon-menu-up';
@ -569,6 +567,10 @@ RemoteVideo.createContainer = function (spanId) {
container.id = spanId; container.id = spanId;
container.className = 'videocontainer'; container.className = 'videocontainer';
let indicatorBar = document.createElement('div');
indicatorBar.className = "videocontainer__toptoolbar";
container.appendChild(indicatorBar);
let toolbar = document.createElement('div'); let toolbar = document.createElement('div');
toolbar.className = "videocontainer__toolbar"; toolbar.className = "videocontainer__toolbar";
container.appendChild(toolbar); container.appendChild(toolbar);

View File

@ -126,37 +126,6 @@ SmallVideo.prototype.getVideoType = function () {
return this.videoType; 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. * Creates an audio or video element for a particular MediaStream.
*/ */
@ -528,13 +497,19 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
return; return;
} }
var indicatorSpan = this.getIndicatorSpan({ let indicatorSpanId = "dominantspeakerindicator";
id: 'dominantspeakerindicator', let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
videoSpanId: this.videoSpanId,
indicatorId: indicatorSpanId,
content: '<i id="indicatoricon" class="fa fa-bullhorn"></i>', content: '<i id="indicatoricon" class="fa fa-bullhorn"></i>',
tooltip: 'speaker' tooltip: 'speaker'
}); });
indicatorSpan.style.display = show ? "" : "none"; if (show) {
indicatorSpan.classList.add('hide');
} else {
indicatorSpan.classList.remove('hide')
}
}; };
/** /**
@ -548,43 +523,19 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) {
return; return;
} }
var indicatorSpan = this.getIndicatorSpan({ let indicatorSpanId = "raisehandindicator";
id: 'raisehandindicator', let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
indicatorId: indicatorSpanId,
videoSpanId: this.videoSpanId,
content: '<i id="indicatoricon" class="icon-raised-hand"></i>', content: '<i id="indicatoricon" class="icon-raised-hand"></i>',
tooltip: 'raisedHand' tooltip: 'raisedHand'
}); });
indicatorSpan.style.display = show ? "" : "none"; if (show) {
}; indicatorSpan.classList.add('hide');
} else {
/** indicatorSpan.classList.remove('hide')
* 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;
} }
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;
}; };
/** /**

View File

@ -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. * Shows a visual indicator for the moderator of the conference.
* On local or remote participants. * On local or remote participants.