From 0013745783d149f53328c512baad7cd5fea9ddb9 Mon Sep 17 00:00:00 2001 From: yanas Date: Wed, 14 Sep 2016 21:20:54 -0500 Subject: [PATCH] Video thumbnails redesign --- css/_font.scss | 2 +- css/_mixins.scss | 13 ++ css/_variables.scss | 27 ++++- css/_videolayout_default.scss | 106 +++++++++------- index.html | 3 +- interface_config.js | 8 +- modules/UI/Feedback.js | 1 + modules/UI/audio_levels/AudioLevels.js | 52 ++++---- modules/UI/toolbars/Toolbar.js | 3 +- modules/UI/videolayout/FilmStrip.js | 160 ++++++++++++++++++++----- modules/UI/videolayout/LocalVideo.js | 28 ++--- modules/UI/videolayout/RemoteVideo.js | 15 ++- modules/UI/videolayout/SmallVideo.js | 50 +++----- modules/UI/videolayout/VideoLayout.js | 26 ++-- package.json | 10 +- 15 files changed, 329 insertions(+), 175 deletions(-) diff --git a/css/_font.scss b/css/_font.scss index 9844e7e51..b5c519b67 100644 --- a/css/_font.scss +++ b/css/_font.scss @@ -16,7 +16,7 @@ font-weight: normal; font-variant: normal; text-transform: none; - line-height: 0.75em; + line-height: 1.22em; font-size: 1.22em; /* Better Font Rendering =========== */ diff --git a/css/_mixins.scss b/css/_mixins.scss index 43aec2431..1ff283e51 100644 --- a/css/_mixins.scss +++ b/css/_mixins.scss @@ -36,6 +36,19 @@ } } +@mixin circle($diameter) { + width: $diameter; + height: $diameter; + border-radius: 50%; +} + +@mixin absoluteAligning($sizeX, $sizeY) { + top: 50%; + left: 50%; + position: absolute; + @include transform(translate(-#{$sizeX / 2}, -#{$sizeY / 2})) +} + @mixin transform($func) { -moz-transform: $func; -ms-transform: $func; diff --git a/css/_variables.scss b/css/_variables.scss index 7ccfc269e..a2f3f9416 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -10,6 +10,11 @@ $hangupFontSize: 2em; */ $defaultToolbarSize: 50px; +// Video layout. +$thumbnailIndicatorSize: 23px; +$thumbnailIndicatorBorder: 0px; +$thumbnailVideoMargin: 2px; + /** * Color variables. */ @@ -17,13 +22,29 @@ $defaultColor: #F1F1F1; $defaultSemiDarkColor: #ACACAC; $defaultDarkColor: #4F4F4F; $defaultBackground: #474747; + +// Toolbar $toolbarSelectBackground: rgba(0, 0, 0, .6); + +// Main controls $inputBackground: rgba(132, 132, 132, .5); $inputSemiBackground: rgba(132, 132, 132, .8); $inputLightBackground: #EBEBEB; $inputBorderColor: #EBEBEB; $buttonBackground: #44A5FF; +// Video layout. +$videoThumbnailHovered: #44A5FF; +$videoThumbnailSelected: #165ecc; +$participantNameColor: #fff; +$thumbnailPictogramColor: #fff; +$dominantSpeakerBg: #165ecc; +$raiseHandBg: #D6D61E; + +$rateStarDefault: #ccc; +$rateStarActivity: #165ecc; +$rateStarLabelColor: #333; + /** * Misc. */ @@ -34,8 +55,4 @@ $defaultWatermarkLink: '../images/watermark.png'; * Z-indexes. TODO: Replace this by a function. */ $toolbarZ: 900; -$overlayZ: 800; - -$rateStarDefault: #ccc; -$rateStarActivity: #f6c342; -$rateStarLabelColor: #333; +$overlayZ: 800; \ No newline at end of file diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index 102253886..2e64daa42 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -13,17 +13,17 @@ display: -ms-flexbox; display: -webkit-flex; display: flex; - flex-direction: row; + flex-direction: row-reverse; flex-wrap: nowrap; - justify-content: flex-end; + justify-content: flex-start; position:absolute; text-align:right; height:196px; - padding: 18px; + padding: 10px 10px 10px 5px; bottom: 0; left: 0; - right: 20px; + right: 0; width:auto; border:1px solid transparent; z-index: 5; @@ -43,10 +43,23 @@ #remoteVideos .videocontainer { display: none; + position: relative; background-color: black; background-size: contain; border-radius:1px; - border: 1px solid #212425; + margin: 0 $thumbnailVideoMargin; + border: 1px solid $defaultDarkColor; +} + +.videocontainer__toolbar { + position: absolute; + bottom: 0; + left: 0; + z-index: 1; + width: 100%; + height: 25px; + max-height: 100%; + background-color: rgba(0, 0, 0, 0.5); } #remoteVideos .videocontainer.videoContainerFocused { @@ -58,18 +71,13 @@ -webkit-animation-iteration-count: 1; } -#remoteVideos .videocontainer:hover { - border: 1px solid #c1c1c1; -} - #remoteVideos .videocontainer.videoContainerFocused { - box-shadow: inset 0 0 28px #006d91; - border: 1px solid #006d91; + border: 1px solid $videoThumbnailSelected; } +#remoteVideos .videocontainer:hover, #remoteVideos .videocontainer.videoContainerFocused:hover { - box-shadow: inset 0 0 5px #c1c1c1, 0 0 10px #c1c1c1, inset 0 0 60px #006d91; - border: 1px solid #c1c1c1; + border: 1px solid $videoThumbnailHovered; } #localVideoWrapper { @@ -145,11 +153,11 @@ #remoteVideos .videocontainer>div.remotevideomenu { position: absolute; color: #FFFFFF; - top: 0; - left: 0; + bottom: 0; + right: 0; padding: 5px 0px; width: 25px; - font-size: 11pt; + font-size: 9pt; text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7); border: 0px; z-index: 2; @@ -166,22 +174,28 @@ .videocontainer>span.displayname, .videocontainer>input.displayname { - display: none; + display: inline-block; position: absolute; - color: #FFFFFF; - background: rgba(0,0,0,.7); + bottom: 4px; + left: 25%; + color: $participantNameColor; text-align: center; text-overflow: ellipsis; - width: 70%; - height: 20%; - left: 15%; - top: 40%; - padding: 5px; - font-size: 11pt; + width: 50%; + font-size: 12px; + font-weight: 100; + letter-spacing: 1px; overflow: hidden; white-space: nowrap; z-index: 2; - border-radius:3px; +} + +.videocontainer>input.displayname { + outline: none; + border: none; + background: none; + box-shadow: none; + padding: 0; } .videocontainer>span.status { @@ -291,7 +305,8 @@ display: inline-block; position: absolute; color: #FFFFFF; - top: 0; + bottom: 0; + left: 0; padding: 8px 5px; width: 25px; font-size: 8pt; @@ -305,8 +320,7 @@ display: inline-block; position: absolute; color: #FFFFFF; - top: 0; - right: 0; + bottom: 0; padding: 8px 5px; width: 25px; font-size: 8pt; @@ -316,24 +330,30 @@ } .videocontainer>span.indicator { - bottom: 0px; + position: absolute; + top: 0px; left: 0px; - width: 25px; - height: 25px; + @include circle($thumbnailIndicatorSize); + box-sizing: border-box; + line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; z-index: 3; text-align: center; - border-radius: 50%; - background: #21B9FC; - margin: 5px; + background: $dominantSpeakerBg; + margin: 7px; display: inline-block; - position: absolute; - color: #FFFFFF; - font-size: 11pt; - border: 0px; + color: $thumbnailPictogramColor; + font-size: 8pt; + border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor; +} + +.videocontainer>#raisehandindicator { + background: $raiseHandBg; } #indicatoricon { - padding-top: 5px; + width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; + height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; + line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; } #reloadPresentation { @@ -395,10 +415,8 @@ } .userAvatar { - height: 100%; - position: absolute; - left: 0; - border-radius: 2px; + @include circle(60px); + @include absoluteAligning(60px, 60px); } .sharedVideoAvatar { diff --git a/index.html b/index.html index 725aaa65b..8a31ba044 100644 --- a/index.html +++ b/index.html @@ -238,12 +238,13 @@
- + +
diff --git a/interface_config.js b/interface_config.js index 9780d27f2..b96556fc6 100644 --- a/interface_config.js +++ b/interface_config.js @@ -34,5 +34,9 @@ var interfaceConfig = { filmStripOnly: false, RANDOM_AVATAR_URL_PREFIX: false, RANDOM_AVATAR_URL_SUFFIX: false, - FILM_STRIP_MAX_HEIGHT: 120 -}; \ No newline at end of file + FILM_STRIP_MAX_HEIGHT: 120, + LOCAL_THUMBNAIL_RATIO_WIDTH: 16, + LOCAL_THUMBNAIL_RATIO_HEIGHT: 9, + REMOTE_THUMBNAIL_RATIO_WIDTH: 1, + REMOTE_THUMBNAIL_RATIO_HEIGHT: 1 +}; diff --git a/modules/UI/Feedback.js b/modules/UI/Feedback.js index 708b762c3..2338721f4 100644 --- a/modules/UI/Feedback.js +++ b/modules/UI/Feedback.js @@ -1,5 +1,6 @@ /* global $, APP, config, interfaceConfig, JitsiMeetJS */ import UIEvents from "../../service/UI/UIEvents"; +import UIUtil from "./util/UIUtil"; /** * Constructs the html for the overall feedback window. diff --git a/modules/UI/audio_levels/AudioLevels.js b/modules/UI/audio_levels/AudioLevels.js index 642ddc925..dc188c48c 100644 --- a/modules/UI/audio_levels/AudioLevels.js +++ b/modules/UI/audio_levels/AudioLevels.js @@ -10,7 +10,7 @@ let ASDrawContext = null; let audioLevelCanvasCache = {}; let dominantSpeakerAudioElement = null; -function initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) { +function _initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) { let ASRadius = dominantSpeakerAvatarSize / 2; let ASCenter = (dominantSpeakerAvatarSize + ASRadius) / 2; @@ -28,7 +28,9 @@ function initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) { /** * Resizes the given audio level canvas to match the given thumbnail size. */ -function resizeAudioLevelCanvas(audioLevelCanvas, thumbnailWidth, thumbnailHeight) { +function _resizeAudioLevelCanvas( audioLevelCanvas, + thumbnailWidth, + thumbnailHeight) { audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA; audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA; } @@ -138,18 +140,18 @@ const AudioLevels = { dominantSpeakerAudioElement.height = dominantSpeakerHeight; let dominantSpeakerAvatar = $("#dominantSpeakerAvatar"); - initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width()); + _initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width()); }, /** * Updates the audio level canvas for the given id. If the canvas * didn't exist we create it. */ - updateAudioLevelCanvas (id, thumbWidth, thumbHeight) { - let videoSpanId = 'localVideoContainer'; - if (id) { - videoSpanId = `participant_${id}`; - } + createAudioLevelCanvas (id, thumbWidth, thumbHeight) { + + let videoSpanId = (id === "local") + ? "localVideoContainer" + : `participant_${id}`; let videoSpan = document.getElementById(videoSpanId); @@ -172,13 +174,13 @@ const AudioLevels = { = `-${interfaceConfig.CANVAS_EXTRA/2}px`; audioLevelCanvas.style.left = `-${interfaceConfig.CANVAS_EXTRA/2}px`; - resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); + _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); videoSpan.appendChild(audioLevelCanvas); } else { audioLevelCanvas = audioLevelCanvas.get(0); - resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); + _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); } }, @@ -242,19 +244,29 @@ const AudioLevels = { ASDrawContext.fill(); }, - updateCanvasSize (thumbWidth, thumbHeight) { - let canvasWidth = thumbWidth + interfaceConfig.CANVAS_EXTRA; - let canvasHeight = thumbHeight + interfaceConfig.CANVAS_EXTRA; + updateCanvasSize (localVideo, remoteVideo) { + let localCanvasWidth + = localVideo.thumbWidth + interfaceConfig.CANVAS_EXTRA; + let localCanvasHeight + = localVideo.thumbHeight + interfaceConfig.CANVAS_EXTRA; + let remoteCanvasWidth + = remoteVideo.thumbWidth + interfaceConfig.CANVAS_EXTRA; + let remoteCanvasHeight + = remoteVideo.thumbHeight + interfaceConfig.CANVAS_EXTRA; - FilmStrip.getThumbs().children('canvas').each(function () { - $(this).attr('width', canvasWidth); - $(this).attr('height', canvasHeight); + let { remoteThumbs, localThumb } = FilmStrip.getThumbs(); + + remoteThumbs.children('canvas').each(function () { + $(this).attr('width', remoteCanvasWidth); + $(this).attr('height', remoteCanvasHeight); }); - Object.keys(audioLevelCanvasCache).forEach(function (id) { - audioLevelCanvasCache[id].width = canvasWidth; - audioLevelCanvasCache[id].height = canvasHeight; - }); + if(localThumb) { + localThumb.children('canvas').each(function () { + $(this).attr('width', localCanvasWidth); + $(this).attr('height', localCanvasHeight); + }); + } } }; diff --git a/modules/UI/toolbars/Toolbar.js b/modules/UI/toolbars/Toolbar.js index 876b6f108..01a36dc39 100644 --- a/modules/UI/toolbars/Toolbar.js +++ b/modules/UI/toolbars/Toolbar.js @@ -7,7 +7,6 @@ import SideContainerToggler from "../side_pannels/SideContainerToggler"; let roomUrl = null; let emitter = null; - /** * Opens the invite link dialog. */ @@ -766,4 +765,4 @@ const Toolbar = { } }; -export default Toolbar; +export default Toolbar; \ No newline at end of file diff --git a/modules/UI/videolayout/FilmStrip.js b/modules/UI/videolayout/FilmStrip.js index db74044f0..125d7c7fa 100644 --- a/modules/UI/videolayout/FilmStrip.js +++ b/modules/UI/videolayout/FilmStrip.js @@ -3,8 +3,6 @@ import UIEvents from "../../../service/UI/UIEvents"; import UIUtil from "../util/UIUtil"; -const thumbAspectRatio = 1 / 1; - const FilmStrip = { /** * @@ -66,13 +64,52 @@ const FilmStrip = { - parseInt(this.filmStrip.css('paddingRight'), 10); }, - /** - * Calculates the thumbnail size. - */ - calculateThumbnailSize () { - let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT; + calculateThumbnailSize() { + let availableSizes = this.calculateAvailableSize(); + let width = availableSizes.availableWidth; + let height = availableSizes.availableHeight; - let numvids = this.getThumbs(true).length; + return this.calculateThumbnailSizeFromAvailable(width, height); + }, + + /** + * Normalizes local and remote thumbnail ratios + */ + normalizeThumbnailRatio () { + let remoteHeightRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_HEIGHT; + let remoteWidthRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_WIDTH; + + let localHeightRatio = interfaceConfig.LOCAL_THUMBNAIL_RATIO_HEIGHT; + let localWidthRatio = interfaceConfig.LOCAL_THUMBNAIL_RATIO_WIDTH; + + let commonHeightRatio = remoteHeightRatio * localHeightRatio; + + let localRatioCoefficient = localWidthRatio / localHeightRatio; + let remoteRatioCoefficient = remoteWidthRatio / remoteHeightRatio; + + remoteWidthRatio = commonHeightRatio * remoteRatioCoefficient; + remoteHeightRatio = commonHeightRatio; + + localWidthRatio = commonHeightRatio * localRatioCoefficient; + localHeightRatio = commonHeightRatio; + + let localRatio = { + widthRatio: localWidthRatio, + heightRatio: localHeightRatio + }; + + let remoteRatio = { + widthRatio: remoteWidthRatio, + heightRatio: remoteHeightRatio + }; + + return { localRatio, remoteRatio }; + }, + + calculateAvailableSize() { + let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT; + let thumbs = this.getThumbs(true); + let numvids = thumbs.remoteThumbs.length; let localVideoContainer = $("#localVideoContainer"); @@ -92,11 +129,10 @@ const FilmStrip = { let availableWidth = videoAreaAvailableWidth; - // If the number of videos is 0 or undefined we don't need to calculate - // further. - if (numvids) + // If local thumb is not hidden + if(thumbs.localThumb) { availableWidth = Math.floor( - (videoAreaAvailableWidth - numvids * ( + (videoAreaAvailableWidth - ( UIUtil.parseCssInt( localVideoContainer.css('borderLeftWidth'), 10) + UIUtil.parseCssInt( @@ -109,36 +145,90 @@ const FilmStrip = { localVideoContainer.css('marginLeft'), 10) + UIUtil.parseCssInt( localVideoContainer.css('marginRight'), 10))) - / numvids); + ); + } + + // If the number of videos is 0 or undefined we don't need to calculate + // further. + if (numvids) { + let remoteVideoContainer = thumbs.remoteThumbs.eq(0); + availableWidth = Math.floor( + (videoAreaAvailableWidth - numvids * ( + UIUtil.parseCssInt( + remoteVideoContainer.css('borderLeftWidth'), 10) + + UIUtil.parseCssInt( + remoteVideoContainer.css('borderRightWidth'), 10) + + UIUtil.parseCssInt( + remoteVideoContainer.css('paddingLeft'), 10) + + UIUtil.parseCssInt( + remoteVideoContainer.css('paddingRight'), 10) + + UIUtil.parseCssInt( + remoteVideoContainer.css('marginLeft'), 10) + + UIUtil.parseCssInt( + remoteVideoContainer.css('marginRight'), 10))) + ); + } let maxHeight // If the MAX_HEIGHT property hasn't been specified // we have the static value. - = Math.min( interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120, - availableHeight); + = Math.min(interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120, + availableHeight); availableHeight - = Math.min( maxHeight, window.innerHeight - 18); + = Math.min(maxHeight, window.innerHeight - 18); - if (availableHeight < availableWidth) { - availableWidth = availableHeight; + return { availableWidth, availableHeight }; + }, + + calculateThumbnailSizeFromAvailable(availableWidth, availableHeight) { + let { localRatio, remoteRatio } = this.normalizeThumbnailRatio(); + let { remoteThumbs } = this.getThumbs(true); + let remoteProportion = remoteRatio.widthRatio * remoteThumbs.length; + let widthProportion = remoteProportion + localRatio.widthRatio; + + let heightUnit = availableHeight / localRatio.heightRatio; + let widthUnit = availableWidth / widthProportion; + + if (heightUnit < widthUnit) { + widthUnit = heightUnit; } else - availableHeight = availableWidth; + heightUnit = widthUnit; + + let localVideo = { + thumbWidth: widthUnit * localRatio.widthRatio, + thumbHeight: heightUnit * localRatio.heightRatio + }; + let remoteVideo = { + thumbWidth: widthUnit * remoteRatio.widthRatio, + thumbHeight: widthUnit * remoteRatio.heightRatio + }; return { - thumbWidth: availableWidth, - thumbHeight: availableHeight + localVideo, + remoteVideo }; }, - resizeThumbnails (thumbWidth, thumbHeight, + resizeThumbnails (local, remote, animate = false, forceUpdate = false) { return new Promise(resolve => { - this.getThumbs(!forceUpdate).animate({ - height: thumbHeight, - width: thumbWidth + let thumbs = this.getThumbs(!forceUpdate); + + thumbs.localThumb.animate({ + height: local.thumbHeight, + width: local.thumbWidth + }, { + queue: false, + duration: animate ? 500 : 0, + complete: resolve + }); + + thumbs.remoteThumbs.animate({ + height: remote.thumbHeight, + width: remote.thumbWidth }, { queue: false, duration: animate ? 500 : 0, @@ -147,7 +237,7 @@ const FilmStrip = { this.filmStrip.animate({ // adds 2 px because of small video 1px border - height: thumbHeight + 2 + height: remote.thumbHeight + 2 }, { queue: false, duration: animate ? 500 : 0 @@ -165,13 +255,19 @@ const FilmStrip = { selector += ':visible'; } + let localThumb = $("#localVideoContainer"); + let remoteThumbs = this.filmStrip.children(selector) + .not("#localVideoContainer"); + // Exclude the local video container if it has been hidden. - if ($("#localVideoContainer").hasClass("hidden")) - return this.filmStrip.children(selector) - .not("#localVideoContainer"); - else - return this.filmStrip.children(selector); - } + if (localThumb.hasClass("hidden")) { + return { remoteThumbs }; + } else { + return { remoteThumbs, localThumb }; + } + + }, + }; export default FilmStrip; diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index b245ee8e6..a368fcc8c 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -11,7 +11,6 @@ function LocalVideo(VideoLayout, emitter) { this.videoSpanId = "localVideoContainer"; this.container = $("#localVideoContainer").get(0); this.localVideoId = null; - this.bindHoverHandler(); if(config.enableLocalVideoFlip) this._buildContextMenu(); this.isLocal = true; @@ -44,7 +43,7 @@ function createEditDisplayNameButton() { editButton.className = 'displayname'; UIUtil.setTooltip(editButton, "videothumbnail.editnickname", - "top"); + "left"); editButton.innerHTML = ''; return editButton; @@ -72,7 +71,7 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) { if (displayName && displayName.length > 0) { meHTML = APP.translation.generateTranslationHTML("me"); $('#localDisplayName').html( - UIUtil.escapeHtml(displayName) + ' (' + meHTML + ')' + `${UIUtil.escapeHtml(displayName)} (${meHTML})` ); } else { $('#localDisplayName').html(defaultLocalDisplayName); @@ -80,11 +79,9 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) { } this.updateView(); } else { - var editButton = createEditDisplayNameButton(); - nameSpan = document.createElement('span'); nameSpan.className = 'displayname'; - $('#' + this.videoSpanId)[0].appendChild(nameSpan); + document.getElementById(this.videoSpanId).appendChild(nameSpan); if (displayName && displayName.length > 0) { @@ -97,7 +94,6 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) { nameSpan.id = 'localDisplayName'; - this.container.appendChild(editButton); //translates popover of edit button APP.translation.translateElement($("a.displayname")); @@ -124,21 +120,23 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) { var self = this; $('#localVideoContainer .displayname') .bind("click", function (e) { + let $editDisplayName = $('#editDisplayName'); + let $localDisplayName = $('#localDisplayName'); - var editDisplayName = $('#editDisplayName'); e.preventDefault(); e.stopPropagation(); - $('#localDisplayName').hide(); - editDisplayName.show(); - editDisplayName.focus(); - editDisplayName.select(); + $localDisplayName.hide(); + $editDisplayName.show(); + $editDisplayName.focus(); + $editDisplayName.select(); - editDisplayName.one("focusout", function (e) { + $editDisplayName.one("focusout", function (e) { self.emitter.emit(UIEvents.NICKNAME_CHANGED, this.value); - $('#editDisplayName').hide(); + $editDisplayName.hide(); + $localDisplayName.show(); }); - editDisplayName.on('keydown', function (e) { + $editDisplayName.on('keydown', function (e) { if (e.keyCode === 13) { e.preventDefault(); $('#editDisplayName').hide(); diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index badd626f7..dc6d68118 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -17,7 +17,6 @@ function RemoteVideo(id, VideoLayout, emitter) { this.addRemoteVideoContainer(); this.connectionIndicator = new ConnectionIndicator(this, id); this.setDisplayName(); - this.bindHoverHandler(); this.flipX = false; this.isLocal = false; this.isMuted = false; @@ -34,8 +33,10 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() { if (APP.conference.isModerator) { this.addRemoteVideoMenu(); } - let {thumbWidth, thumbHeight} = this.VideoLayout.resizeThumbnails(); - AudioLevels.updateAudioLevelCanvas(this.id, thumbWidth, thumbHeight); + + let { remoteVideo } = this.VideoLayout.resizeThumbnails(); + let { thumbHeight, thumbWidth } = remoteVideo; + AudioLevels.createAudioLevelCanvas(this.id, thumbWidth, thumbHeight); return this.container; }; @@ -427,12 +428,16 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() { }; RemoteVideo.createContainer = function (spanId) { - var container = document.createElement('span'); + let container = document.createElement('span'); container.id = spanId; container.className = 'videocontainer'; + + let toolbar = document.createElement('div'); + toolbar.className = "videocontainer__toolbar"; + container.appendChild(toolbar); + var remotes = document.getElementById('remoteVideos'); return remotes.appendChild(container); }; - export default RemoteVideo; diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index cf42a16dc..b905b6eb8 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -171,26 +171,6 @@ SmallVideo.getStreamElementID = function (stream) { return (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId(); }; -/** - * Configures hoverIn/hoverOut handlers. - */ -SmallVideo.prototype.bindHoverHandler = function () { - // Add hover handler - var self = this; - $(this.container).hover( - function () { - self.showDisplayName(true); - }, - function () { - // If the video has been "pinned" by the user we want to - // keep the display name on place. - if (!self.VideoLayout.isLargeVideoVisible() || - !self.VideoLayout.isCurrentlyOnLarge(self.id)) - self.showDisplayName(false); - } - ); -}; - /** * Updates the data for the indicator * @param id the id of the indicator @@ -219,6 +199,7 @@ SmallVideo.prototype.showAudioIndicator = function(isMuted) { if (audioMutedSpan.length > 0) { audioMutedSpan.popover('hide'); audioMutedSpan.remove(); + this.updateIconPositions(); } } else { @@ -230,12 +211,14 @@ SmallVideo.prototype.showAudioIndicator = function(isMuted) { "top"); this.container.appendChild(audioMutedSpan); - APP.translation.translateElement($('#' + this.videoSpanId + " > span")); + APP.translation + .translateElement($('#' + this.videoSpanId + " > span")); var mutedIndicator = document.createElement('i'); mutedIndicator.className = 'icon-mic-disabled'; audioMutedSpan.appendChild(mutedIndicator); } + this.updateIconPositions(); } this.isMuted = isMuted; @@ -254,6 +237,7 @@ SmallVideo.prototype.setMutedView = function(isMuted) { if (isMuted === false) { if (videoMutedSpan.length > 0) { videoMutedSpan.remove(); + this.updateIconPositions(); } } else { @@ -270,7 +254,8 @@ SmallVideo.prototype.setMutedView = function(isMuted) { "top"); videoMutedSpan.appendChild(mutedIndicator); //translate texts for muted indicator - APP.translation.translateElement($('#' + this.videoSpanId + " > span > i")); + APP.translation + .translateElement($('#' + this.videoSpanId + " > span > i")); } this.updateIconPositions(); @@ -278,13 +263,18 @@ SmallVideo.prototype.setMutedView = function(isMuted) { }; SmallVideo.prototype.updateIconPositions = function () { - var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted'); - var connectionIndicator = $('#' + this.videoSpanId + '>div.connectionindicator'); - var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted'); + let audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted'); + let videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted'); + audioMutedSpan.css({left: "0px"}); + videoMutedSpan.css({left: (audioMutedSpan.length > 0? 25 : 0) + "px"}); + + var connectionIndicator + = $('#' + this.videoSpanId + '>div.connectionindicator'); if(connectionIndicator.length > 0 && connectionIndicator[0].style.display != "none") { audioMutedSpan.css({right: "23px"}); - videoMutedSpan.css({right: ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"}); + videoMutedSpan.css({right: + ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"}); } else { audioMutedSpan.css({right: "0px"}); videoMutedSpan.css({right: (audioMutedSpan.length > 0? 30 : 0) + "px"}); @@ -317,7 +307,8 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () { "top"); //translates text in focus indicators - APP.translation.translateElement($('#' + this.videoSpanId + ' .focusindicator')); + APP.translation + .translateElement($('#' + this.videoSpanId + ' .focusindicator')); }; /** @@ -406,8 +397,6 @@ SmallVideo.prototype.updateView = function () { setVisibility(video, showVideo); } setVisibility(avatar, showAvatar); - - this.showDisplayName(!showVideo && !showAvatar); }; SmallVideo.prototype.avatarChanged = function (avatarUrl) { @@ -465,9 +454,8 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) { var indicatorSpanId = "raisehandindicator"; var indicatorSpan = this.getIndicatorSpan(indicatorSpanId); - indicatorSpan.style.background = "#D6D61E"; indicatorSpan.innerHTML - = ""; + = ""; // adds a tooltip UIUtil.setTooltip(indicatorSpan, "raisedHand", "left"); diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index d574265ab..118c2d87f 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -105,8 +105,9 @@ var VideoLayout = { localVideoThumbnail.setVideoType(VIDEO_CONTAINER_TYPE); // if we do not resize the thumbs here, if there is no video device // the local video thumb maybe one pixel - let {thumbWidth, thumbHeight} = this.resizeThumbnails(false, true); - AudioLevels.updateAudioLevelCanvas(null, thumbWidth, thumbHeight); + let { localVideo } = this.resizeThumbnails(false, true); + AudioLevels.createAudioLevelCanvas( + "local", localVideo.thumbWidth, localVideo.thumbHeight); emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked); this.lastNCount = config.channelLastN; @@ -254,7 +255,8 @@ var VideoLayout = { electLastVisibleVideo () { // pick the last visible video in the row // if nobody else is left, this picks the local video - let thumbs = FilmStrip.getThumbs(true).filter('[id!="mixedstream"]'); + let remoteThumbs = FilmStrip.getThumbs(true).remoteThumbs; + let thumbs = remoteThumbs.filter('[id!="mixedstream"]'); let lastVisible = thumbs.filter(':visible:last'); if (lastVisible.length) { @@ -268,7 +270,7 @@ var VideoLayout = { } console.info("Last visible video no longer exists"); - thumbs = FilmStrip.getThumbs(); + thumbs = FilmStrip.getThumbs().remoteThumbs; if (thumbs.length) { let id = getPeerContainerResourceId(thumbs[0]); if (remoteVideos[id]) { @@ -401,7 +403,7 @@ var VideoLayout = { // In case this is not currently in the last n we don't show it. if (localLastNCount && localLastNCount > 0 && - FilmStrip.getThumbs().length >= localLastNCount + 2) { + FilmStrip.getThumbs().remoteThumbs.length >= localLastNCount + 2) { remoteVideo.showPeerContainer('hide'); } else { VideoLayout.resizeThumbnails(false, true); @@ -486,19 +488,19 @@ var VideoLayout = { forceUpdate = false, onComplete = null) { - let {thumbWidth, thumbHeight} + let { localVideo, remoteVideo } = FilmStrip.calculateThumbnailSize(); - $('.userAvatar').css('left', (thumbWidth - thumbHeight) / 2); + let {thumbWidth, thumbHeight} = remoteVideo; - FilmStrip.resizeThumbnails(thumbWidth, thumbHeight, + FilmStrip.resizeThumbnails(localVideo, remoteVideo, animate, forceUpdate) .then(function () { - AudioLevels.updateCanvasSize(thumbWidth, thumbHeight); + AudioLevels.updateCanvasSize(localVideo, remoteVideo); if (onComplete && typeof onComplete === "function") onComplete(); - }); - return {thumbWidth, thumbHeight}; + }); + return { localVideo, remoteVideo }; }, /** @@ -656,7 +658,7 @@ var VideoLayout = { var updateLargeVideo = false; // Handle LastN/local LastN changes. - FilmStrip.getThumbs().each(( index, element ) => { + FilmStrip.getThumbs().remoteThumbs.each(( index, element ) => { var resourceJid = getPeerContainerResourceId(element); var smallVideo = remoteVideos[resourceJid]; diff --git a/package.json b/package.json index c744fc911..1a6cfa30a 100644 --- a/package.json +++ b/package.json @@ -21,18 +21,18 @@ "bootstrap": "3.1.1", "events": "*", "i18next-client": "1.7.7", - "jquery": "~2.1.1", "jQuery-Impromptu": "git+https://github.com/trentrichardson/jQuery-Impromptu.git#v6.0.0", - "lib-jitsi-meet": "git+https://github.com/jitsi/lib-jitsi-meet.git", + "jquery": "~2.1.1", "jquery-contextmenu": "*", "jquery-ui": "1.10.5", "jssha": "1.5.0", + "jws": "*", + "lib-jitsi-meet": "git+https://github.com/jitsi/lib-jitsi-meet.git", + "postis": "^2.2.0", "retry": "0.6.1", "strophe": "^1.2.2", "strophejs-plugins": "^0.0.6", - "toastr": "^2.0.3", - "postis": "^2.2.0", - "jws": "*" + "toastr": "^2.0.3" }, "devDependencies": { "babel-polyfill": "*",