Bläddra i källkod

Merge pull request #1071 from jitsi/ongoing-work-video-thumbnails

Ongoing work video thumbnails
j8
Paweł Domas 8 år sedan
förälder
incheckning
2fe69d409b

+ 0
- 4
conference.js Visa fil

@@ -1279,10 +1279,6 @@ export default {
1279 1279
             APP.UI.updateRecordingState(status);
1280 1280
         });
1281 1281
 
1282
-        room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
1283
-            APP.UI.updateUserStatus(id, status);
1284
-        });
1285
-
1286 1282
         room.on(ConferenceEvents.KICKED, () => {
1287 1283
             APP.UI.hideStats();
1288 1284
             APP.UI.notifyKicked();

+ 9
- 0
css/_mixins.scss Visa fil

@@ -50,6 +50,15 @@
50 50
   border-radius: 50%;
51 51
 }
52 52
 
53
+/**
54
+* Absolute position the element at the top left corner
55
+**/
56
+@mixin topLeft() {
57
+    position: absolute;
58
+    top: 0;
59
+    left: 0;
60
+}
61
+
53 62
 @mixin absoluteAligning($sizeX, $sizeY) {
54 63
   top: 50%;
55 64
   left: 50%;

+ 3
- 2
css/_variables.scss Visa fil

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

+ 107
- 88
css/_videolayout_default.scss Visa fil

@@ -58,17 +58,36 @@
58 58
 /**
59 59
  * The toolbar of the video thumbnail.
60 60
  */
61
-.videocontainer__toolbar {
61
+.videocontainer__toolbar,
62
+.videocontainer__toptoolbar {
62 63
     position: absolute;
63
-    bottom: 0;
64 64
     left: 0;
65
-    z-index: 1;
65
+    z-index: 3;
66 66
     width: 100%;
67 67
     box-sizing: border-box; // Includes the padding in the 100% width.
68
-    height: $thumbnailToolbarHeight;
69
-    max-height: 100%;
70
-    background-color: rgba(0, 0, 0, 0.5);
68
+}
69
+
70
+.videocontainer__toolbar {
71
+    bottom: 0;
71 72
     padding: 0 5px 0 5px;
73
+    height: $thumbnailToolbarHeight;
74
+}
75
+
76
+.videocontainer__toptoolbar {
77
+    $toolbarPadding: 5px;
78
+    top: 0;
79
+    padding: $toolbarPadding;
80
+    padding-bottom: 0;
81
+    height: $thumbnailIndicatorSize + $toolbarPadding;
82
+}
83
+
84
+.videocontainer__hoverOverlay {
85
+    position: relative;
86
+    width: 100%;
87
+    height: 100%;
88
+    visibility: hidden;
89
+    background: rgba(0,0,0,.6);
90
+    z-index: 2;
72 91
 }
73 92
 
74 93
 #remoteVideos .videocontainer.videoContainerFocused,
@@ -176,8 +195,10 @@
176 195
 .videocontainer .editdisplayname {
177 196
     display: inline-block;
178 197
     position: absolute;
179
-    left: 30%;
180
-    width: 40%;
198
+    left: 10%;
199
+    width: 80%;
200
+    top: 50%;
201
+    @include transform(translateY(-40%));
181 202
     color: $participantNameColor;
182 203
     text-align: center;
183 204
     text-overflow: ellipsis;
@@ -200,72 +221,10 @@
200 221
     padding: 0;
201 222
 }
202 223
 
203
-.videocontainer>span.status {
204
-    display: inline-block;
205
-    position: absolute;
206
-    color: #FFFFFF;
207
-    background: rgba(0,0,0,.7);
208
-    text-align: center;
209
-    text-overflow: ellipsis;
210
-    width: 70%;
211
-    height: 15%;
212
-    left: 15%;
213
-    bottom: 2%;
214
-    padding: 5px;
215
-    font-size: 10pt;
216
-    overflow: hidden;
217
-    white-space: nowrap;
218
-    z-index: 2;
219
-    border-radius:3px;
220
-}
221
-
222
-.connectionindicator
223
-{
224
-    display: inline-block;
225
-    position: absolute;
226
-    top: 7px;
227
-    right: 0;
228
-    padding: 0px 5px;
229
-    z-index: 3;
230
-    width: 18px;
231
-    height: 13px;
232
-}
233
-
234
-.connection.connection_empty
235
-{
236
-    color: #8B8B8B;/*#FFFFFF*/
237
-    overflow: hidden;
238
-}
239
-
240
-.connection.connection_lost
241
-{
242
-    color: #8B8B8B;
243
-    overflow: visible;
244
-}
245
-
246
-.connection.connection_full
247
-{
248
-    color: #FFFFFF;/*#15A1ED*/
249
-    overflow: hidden;
250
-}
251
-
252
-.connection
253
-{
254
-    position: absolute;
255
-    left: 0px;
256
-    top: 0px;
257
-    font-size: 8pt;
258
-    border: 0px;
259
-    width: 18px;
260
-    height: 13px;
261
-}
262
-
263
-#localVideoContainer>span.status:hover,
264 224
 #localVideoContainer .displayname:hover {
265 225
     cursor: text;
266 226
 }
267 227
 
268
-.videocontainer>span.status,
269 228
 .videocontainer .displayname {
270 229
     pointer-events: none;
271 230
 }
@@ -278,7 +237,6 @@
278 237
     pointer-events: auto !important;
279 238
 }
280 239
 
281
-.videocontainer>a.status,
282 240
 .videocontainer>a.displayname {
283 241
     display: inline-block;
284 242
     position: absolute;
@@ -323,25 +281,92 @@
323 281
   margin: 0px 0px 0px 5px;
324 282
 }
325 283
 
326
-.videocontainer>span.indicator {
327
-    position: absolute;
328
-    top: 0px;
329
-    left: 0px;
284
+#raisehandindicator {
285
+  background: $raiseHandBg;
286
+}
287
+
288
+#connectionindicator {
289
+  background: $connectionIndicatorBg;
290
+}
291
+
292
+.videocontainer__toptoolbar span.indicator {
293
+    position: relative;
294
+    font-size: 8pt;
295
+    text-align: center;
296
+    line-height: $thumbnailToolbarHeight;
297
+    display: none;
298
+    padding: 0;
299
+    margin-right: 5px;
300
+    float: left;
330 301
     @include circle($thumbnailIndicatorSize);
331 302
     box-sizing: border-box;
332
-    line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
333 303
     z-index: 3;
334
-    text-align: center;
335 304
     background: $dominantSpeakerBg;
336
-    margin: 7px;
337
-    display: inline-block;
338 305
     color: $thumbnailPictogramColor;
339
-    font-size: 8pt;
340 306
     border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
307
+
308
+    .indicatoricon {
309
+        position: absolute;
310
+        top: 50%;
311
+        left: 0;
312
+        @include transform(translateY(-50%));
313
+        width: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
314
+        height: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
315
+        line-height: $thumbnailIndicatorSize - 2 * $thumbnailIndicatorBorder;
316
+    }
317
+
318
+    .connection {
319
+        position: relative;
320
+        margin: 0 auto;
321
+        width: 12px;
322
+        height: 8px;
323
+
324
+        &_empty
325
+        {
326
+            @include topLeft();
327
+            max-width: 12px;
328
+            width: 12px;
329
+            color: #8B8B8B;/*#FFFFFF*/
330
+            overflow: hidden;
331
+        }
332
+
333
+        &_lost
334
+        {
335
+            @include topLeft();
336
+            max-width: 12px;
337
+            width: 12px;
338
+            color: #8B8B8B;
339
+            overflow: visible;
340
+        }
341
+
342
+        &_full
343
+        {
344
+            @include topLeft();
345
+            max-width: 12px;
346
+            width: 12px;
347
+            color: #FFFFFF;/*#15A1ED*/
348
+            overflow: hidden;
349
+        }
350
+    }
351
+
352
+    .icon-connection,
353
+    .icon-connection-lost {
354
+        font-size: 6pt;
355
+    }
341 356
 }
342 357
 
343
-.videocontainer>#raisehandindicator {
344
-    background: $raiseHandBg;
358
+.remotevideomenu
359
+{
360
+    display: inline-block;
361
+    position: absolute;
362
+    top: 0px;
363
+    right: 0;
364
+    margin: 7px;
365
+    z-index: 3;
366
+    width: 18px;
367
+    height: 13px;
368
+    color: #FFF;
369
+    font-size: 8pt;
345 370
 }
346 371
 
347 372
 /**
@@ -384,12 +409,6 @@
384 409
     }
385 410
 }
386 411
 
387
-#indicatoricon {
388
-    width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
389
-    height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
390
-    line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
391
-}
392
-
393 412
 #reloadPresentation {
394 413
     display: none;
395 414
     position: absolute;

+ 2
- 0
index.html Visa fil

@@ -254,6 +254,8 @@
254 254
                     </span>
255 255
                     <audio id="localAudio" autoplay muted></audio>
256 256
                     <div class="videocontainer__toolbar"></div>
257
+                    <div class="videocontainer__toptoolbar"></div>
258
+                    <div class="videocontainer__hoverOverlay"></div>
257 259
                 </span>
258 260
                 <audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
259 261
                 <audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>

+ 0
- 4
modules/UI/UI.js Visa fil

@@ -609,10 +609,6 @@ UI.removeUser = function (id, displayName) {
609 609
     VideoLayout.removeParticipantContainer(id);
610 610
 };
611 611
 
612
-UI.updateUserStatus = function (id, status) {
613
-    VideoLayout.setPresenceStatus(id, status);
614
-};
615
-
616 612
 /**
617 613
  * Update videotype for specified user.
618 614
  * @param {string} id user id

+ 1
- 1
modules/UI/shared_video/SharedVideo.js Visa fil

@@ -625,7 +625,7 @@ function SharedVideoThumb (url)
625 625
     this.videoSpanId = "sharedVideoContainer";
626 626
     this.container = this.createContainer(this.videoSpanId);
627 627
     this.container.onclick = this.videoClick.bind(this);
628
-
628
+    this.bindHoverHandler();
629 629
     SmallVideo.call(this, VideoLayout);
630 630
     this.isVideoMuted = true;
631 631
 }

+ 13
- 0
modules/UI/util/JitsiPopover.js Visa fil

@@ -98,14 +98,27 @@ var JitsiPopover = (function () {
98 98
         var self = this;
99 99
         $(".jitsipopover").on("mouseenter", function () {
100 100
             self.popoverIsHovered = true;
101
+            if(typeof self.onHoverPopover === "function") {
102
+                self.onHoverPopover(self.popoverIsHovered);
103
+            }
101 104
         }).on("mouseleave", function () {
102 105
             self.popoverIsHovered = false;
103 106
             self.hide();
107
+            if(typeof self.onHoverPopover === "function") {
108
+                self.onHoverPopover(self.popoverIsHovered);
109
+            }
104 110
         });
105 111
 
106 112
         this.refreshPosition();
107 113
     };
108 114
 
115
+    /**
116
+     * Adds a hover listener to the popover.
117
+     */
118
+    JitsiPopover.prototype.addOnHoverPopover = function (listener) {
119
+        this.onHoverPopover = listener;
120
+    };
121
+
109 122
     /**
110 123
      * Refreshes the position of the popover.
111 124
      */

+ 56
- 1
modules/UI/util/UIUtil.js Visa fil

@@ -136,7 +136,7 @@ const TOOLTIP_POSITIONS = {
136 136
             element.setAttribute('data-i18n', '[content]' + key);
137 137
 
138 138
             APP.translation.translateElement($(element));
139
-        }      
139
+        }
140 140
     },
141 141
 
142 142
     /**
@@ -237,6 +237,18 @@ const TOOLTIP_POSITIONS = {
237 237
         $("#"+id).addClass("hide");
238 238
     },
239 239
 
240
+    /**
241
+     * Shows / hides the element with the given jQuery selector.
242
+     *
243
+     * @param {jQuery} selector the jQuery selector of the element to show/hide
244
+     * @param {boolean} isVisible
245
+     */
246
+    setVisibility(selector, isVisible) {
247
+        if (selector && selector.length > 0) {
248
+            selector.css("visibility", isVisible ? "visible" : "hidden");
249
+        }
250
+    },
251
+
240 252
     hideDisabledButtons: function (mappings) {
241 253
         var selector = Object.keys(mappings)
242 254
           .map(function (buttonName) {
@@ -376,6 +388,49 @@ const TOOLTIP_POSITIONS = {
376 388
                 "cursor": "default"
377 389
             });
378 390
         }
391
+    },
392
+
393
+    /**
394
+     * Gets an "indicator" span for a video thumbnail.
395
+     * If element doesn't exist then creates it and appends
396
+     * video span container.
397
+     *
398
+     * @param {object} opts
399
+     * @param opts.indicatorId {String} - identificator of indicator
400
+     * @param opts.videoSpanId {String} - identificator of video span
401
+     * @param opts.content {String} HTML content of indicator
402
+     * @param opts.tooltip {String} - tooltip key for translation
403
+     *
404
+     * @returns {HTMLSpanElement} indicatorSpan
405
+     */
406
+    getVideoThumbnailIndicatorSpan(opts = {}) {
407
+        let indicatorId = opts.indicatorId;
408
+        let videoSpanId = opts.videoSpanId;
409
+        let indicators = $(`#${videoSpanId} [id="${indicatorId}"]`);
410
+        let indicatorSpan;
411
+
412
+        if (indicators.length <= 0) {
413
+            indicatorSpan = document.createElement('span');
414
+            indicatorSpan.className = 'indicator';
415
+            indicatorSpan.id = indicatorId;
416
+
417
+            if(opts.content) {
418
+                indicatorSpan.innerHTML = opts.content;
419
+            }
420
+
421
+            if (opts.tooltip) {
422
+                this.setTooltip(indicatorSpan, opts.tooltip, "top");
423
+                APP.translation.translateElement($(indicatorSpan));
424
+            }
425
+
426
+            document.getElementById(videoSpanId)
427
+                .querySelector('.videocontainer__toptoolbar')
428
+                .appendChild(indicatorSpan);
429
+        } else {
430
+            indicatorSpan = indicators[0];
431
+        }
432
+
433
+        return indicatorSpan;
379 434
     }
380 435
 };
381 436
 

+ 45
- 27
modules/UI/videolayout/ConnectionIndicator.js Visa fil

@@ -2,13 +2,15 @@
2 2
 /* jshint -W101 */
3 3
 import JitsiPopover from "../util/JitsiPopover";
4 4
 import VideoLayout from "./VideoLayout";
5
+import UIUtil from "../util/UIUtil";
5 6
 
6 7
 /**
7 8
  * Constructs new connection indicator.
8 9
  * @param videoContainer the video container associated with the indicator.
10
+ * @param videoId the identifier of the video
9 11
  * @constructor
10 12
  */
11
-function ConnectionIndicator(videoContainer, id) {
13
+function ConnectionIndicator(videoContainer, videoId) {
12 14
     this.videoContainer = videoContainer;
13 15
     this.bandwidth = null;
14 16
     this.packetLoss = null;
@@ -18,7 +20,7 @@ function ConnectionIndicator(videoContainer, id) {
18 20
     this.isResolutionHD = null;
19 21
     this.transport = [];
20 22
     this.popover = null;
21
-    this.id = id;
23
+    this.id = videoId;
22 24
     this.create();
23 25
 }
24 26
 
@@ -32,12 +34,12 @@ function ConnectionIndicator(videoContainer, id) {
32 34
  *         0: string}}
33 35
  */
34 36
 ConnectionIndicator.connectionQualityValues = {
35
-    98: "18px", //full
36
-    81: "15px",//4 bars
37
-    64: "11px",//3 bars
38
-    47: "7px",//2 bars
39
-    30: "3px",//1 bar
40
-    0: "0px"//empty
37
+    98: "100%", //full
38
+    81: "80%",//4 bars
39
+    64: "55%",//3 bars
40
+    47: "40%",//2 bars
41
+    30: "20%",//1 bar
42
+    0: "0"//empty
41 43
 };
42 44
 
43 45
 ConnectionIndicator.getIP = function(value) {
@@ -259,18 +261,22 @@ function createIcon(classes, iconClass) {
259 261
  * Creates the indicator
260 262
  */
261 263
 ConnectionIndicator.prototype.create = function () {
262
-    this.connectionIndicatorContainer = document.createElement("div");
263
-    this.connectionIndicatorContainer.className = "connectionindicator";
264
-    this.connectionIndicatorContainer.style.display = "none";
265
-    this.videoContainer.container.appendChild(
266
-        this.connectionIndicatorContainer);
267
-    this.popover = new JitsiPopover(
268
-        $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), {
269
-            content: "<div class=\"connection-info\" " +
270
-                        "data-i18n='connectionindicator.na'></div>",
271
-            skin: "black",
272
-            onBeforePosition: el => APP.translation.translateElement(el)
273
-        });
264
+    let indicatorId = 'connectionindicator';
265
+    let element = UIUtil.getVideoThumbnailIndicatorSpan({
266
+        videoSpanId: this.videoContainer.videoSpanId,
267
+        indicatorId
268
+    });
269
+    element.classList.add('show');
270
+    this.connectionIndicatorContainer = element;
271
+
272
+    let popoverContent = (
273
+        `<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
274
+    );
275
+    this.popover = new JitsiPopover($(element), {
276
+        content: popoverContent,
277
+        skin: "black",
278
+        onBeforePosition: el => APP.translation.translateElement(el)
279
+    });
274 280
 
275 281
     // override popover show method to make sure we will update the content
276 282
     // before showing the popover
@@ -283,13 +289,19 @@ ConnectionIndicator.prototype.create = function () {
283 289
         origShowFunc.call(this.popover);
284 290
     }.bind(this);
285 291
 
286
-    this.emptyIcon = this.connectionIndicatorContainer.appendChild(
287
-        createIcon(["connection", "connection_empty"], "icon-connection"));
288
-    this.fullIcon = this.connectionIndicatorContainer.appendChild(
289
-        createIcon(["connection", "connection_full"], "icon-connection"));
290
-    this.interruptedIndicator = this.connectionIndicatorContainer.appendChild(
291
-        createIcon(["connection", "connection_lost"],"icon-connection-lost"));
292
+    let connectionIconContainer = document.createElement('div');
293
+    connectionIconContainer.className = 'connection indicatoricon';
294
+
295
+
296
+    this.emptyIcon = connectionIconContainer.appendChild(
297
+        createIcon(["connection_empty"], "icon-connection"));
298
+    this.fullIcon = connectionIconContainer.appendChild(
299
+        createIcon(["connection_full"], "icon-connection"));
300
+    this.interruptedIndicator = connectionIconContainer.appendChild(
301
+        createIcon(["connection_lost"],"icon-connection-lost"));
302
+
292 303
     $(this.interruptedIndicator).hide();
304
+    this.connectionIndicatorContainer.appendChild(connectionIconContainer);
293 305
 };
294 306
 
295 307
 /**
@@ -320,7 +332,6 @@ ConnectionIndicator.prototype.updateConnectionStatusIndicator
320 332
         $(this.interruptedIndicator).show();
321 333
         $(this.emptyIcon).hide();
322 334
         $(this.fullIcon).hide();
323
-        this.updateConnectionQuality(0 /* zero bars */);
324 335
     }
325 336
 };
326 337
 
@@ -427,4 +438,11 @@ ConnectionIndicator.prototype.updateResolutionIndicator = function () {
427 438
     }
428 439
 };
429 440
 
441
+/**
442
+ * Adds a hover listener to the popover.
443
+ */
444
+ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) {
445
+    this.popover.addOnHoverPopover(listener);
446
+};
447
+
430 448
 export default ConnectionIndicator;

+ 4
- 5
modules/UI/videolayout/LocalVideo.js Visa fil

@@ -11,6 +11,8 @@ function LocalVideo(VideoLayout, emitter) {
11 11
     this.videoSpanId = "localVideoContainer";
12 12
     this.container = $("#localVideoContainer").get(0);
13 13
     this.localVideoId = null;
14
+    this.createConnectionIndicator();
15
+    this.bindHoverHandler();
14 16
     if(config.enableLocalVideoFlip)
15 17
         this._buildContextMenu();
16 18
     this.isLocal = true;
@@ -27,7 +29,6 @@ function LocalVideo(VideoLayout, emitter) {
27 29
     // Set default display name.
28 30
     this.setDisplayName();
29 31
 
30
-    this.createConnectionIndicator();
31 32
     this.addAudioLevelIndicator();
32 33
 }
33 34
 
@@ -70,7 +71,6 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
70 71
         nameSpan = document.createElement('span');
71 72
         nameSpan.className = 'displayname';
72 73
         document.getElementById(this.videoSpanId)
73
-            .querySelector('.videocontainer__toolbar')
74 74
             .appendChild(nameSpan);
75 75
 
76 76
 
@@ -104,7 +104,6 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
104 104
         APP.translation.translateElement($(editableText));
105 105
 
106 106
         this.container
107
-            .querySelector('.videocontainer__toolbar')
108 107
             .appendChild(editableText);
109 108
 
110 109
         var self = this;
@@ -115,7 +114,7 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
115 114
 
116 115
                 e.preventDefault();
117 116
                 e.stopPropagation();
118
-                $localDisplayName.hide();
117
+                UIUtil.setVisibility($localDisplayName, false);
119 118
                 $editDisplayName.show();
120 119
                 $editDisplayName.focus();
121 120
                 $editDisplayName.select();
@@ -123,7 +122,7 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
123 122
                 $editDisplayName.one("focusout", function () {
124 123
                     self.emitter.emit(UIEvents.NICKNAME_CHANGED, this.value);
125 124
                     $editDisplayName.hide();
126
-                    $localDisplayName.show();
125
+                    UIUtil.setVisibility($localDisplayName, true);
127 126
                 });
128 127
 
129 128
                 $editDisplayName.on('keydown', function (e) {

+ 14
- 11
modules/UI/videolayout/RemoteVideo.js Visa fil

@@ -31,6 +31,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
31 31
     this.addRemoteVideoContainer();
32 32
     this.connectionIndicator = new ConnectionIndicator(this, this.id);
33 33
     this.setDisplayName();
34
+    this.bindHoverHandler();
34 35
     this.flipX = false;
35 36
     this.isLocal = false;
36 37
     /**
@@ -233,11 +234,9 @@ if (!interfaceConfig.filmStripOnly) {
233 234
     RemoteVideo.prototype.addRemoteVideoMenu = function () {
234 235
 
235 236
         var spanElement = document.createElement('span');
236
-        spanElement.className = 'remotevideomenu toolbar-icon right';
237
+        spanElement.className = 'remotevideomenu';
237 238
 
238
-        this.container
239
-            .querySelector('.videocontainer__toolbar')
240
-            .appendChild(spanElement);
239
+        this.container.appendChild(spanElement);
241 240
 
242 241
         var menuElement = document.createElement('i');
243 242
         menuElement.className = 'icon-menu-up';
@@ -512,9 +511,10 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
512 511
 
513 512
 /**
514 513
  * Sets the display name for the given video span id.
514
+ *
515
+ * @param displayName the display name to set
515 516
  */
516
-RemoteVideo.prototype.setDisplayName = function(displayName, key) {
517
-
517
+RemoteVideo.prototype.setDisplayName = function(displayName) {
518 518
     if (!this.container) {
519 519
         console.warn( "Unable to set displayName - " + this.videoSpanId +
520 520
                 " does not exist");
@@ -530,10 +530,6 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
530 530
             if (displaynameSpan.text() !== displayName)
531 531
                 displaynameSpan.text(displayName);
532 532
         }
533
-        else if (key && key.length > 0) {
534
-            var nameHtml = APP.translation.generateTranslationHTML(key);
535
-            $('#' + this.videoSpanId + '_name').html(nameHtml);
536
-        }
537 533
         else
538 534
             $('#' + this.videoSpanId + '_name').text(
539 535
                 interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
@@ -541,7 +537,6 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
541 537
         nameSpan = document.createElement('span');
542 538
         nameSpan.className = 'displayname';
543 539
         $('#' + this.videoSpanId)[0]
544
-            .querySelector('.videocontainer__toolbar')
545 540
             .appendChild(nameSpan);
546 541
 
547 542
         if (displayName && displayName.length > 0) {
@@ -573,10 +568,18 @@ RemoteVideo.createContainer = function (spanId) {
573 568
     container.id = spanId;
574 569
     container.className = 'videocontainer';
575 570
 
571
+    let indicatorBar = document.createElement('div');
572
+    indicatorBar.className = "videocontainer__toptoolbar";
573
+    container.appendChild(indicatorBar);
574
+
576 575
     let toolbar = document.createElement('div');
577 576
     toolbar.className = "videocontainer__toolbar";
578 577
     container.appendChild(toolbar);
579 578
 
579
+    let overlay = document.createElement('div');
580
+    overlay.className = "videocontainer__hoverOverlay";
581
+    container.appendChild(overlay);
582
+
580 583
     var remotes = document.getElementById('remoteVideos');
581 584
     return remotes.appendChild(container);
582 585
 };

+ 115
- 97
modules/UI/videolayout/SmallVideo.js Visa fil

@@ -1,4 +1,4 @@
1
-/* global $, APP, JitsiMeetJS, interfaceConfig */
1
+/* global $, JitsiMeetJS, interfaceConfig */
2 2
 import Avatar from "../avatar/Avatar";
3 3
 import UIUtil from "../util/UIUtil";
4 4
 import UIEvents from "../../../service/UI/UIEvents";
@@ -21,11 +21,27 @@ const DISPLAY_VIDEO = 0;
21 21
 const DISPLAY_AVATAR = 1;
22 22
 /**
23 23
  * Display mode constant used when neither video nor avatar is being displayed
24
- * on the small video.
24
+ * on the small video. And we just show the display name.
25 25
  * @type {number}
26 26
  * @constant
27 27
  */
28
-const DISPLAY_BLACKNESS = 2;
28
+const DISPLAY_BLACKNESS_WITH_NAME = 2;
29
+
30
+/**
31
+ * Display mode constant used when video is displayed and display name
32
+ * at the same time.
33
+ * @type {number}
34
+ * @constant
35
+ */
36
+const DISPLAY_VIDEO_WITH_NAME = 3;
37
+
38
+/**
39
+ * Display mode constant used when neither video nor avatar is being displayed
40
+ * on the small video. And we just show the display name.
41
+ * @type {number}
42
+ * @constant
43
+ */
44
+const DISPLAY_AVATAR_WITH_NAME = 4;
29 45
 
30 46
 function SmallVideo(VideoLayout) {
31 47
     this.isAudioMuted = false;
@@ -34,12 +50,7 @@ function SmallVideo(VideoLayout) {
34 50
     this.videoStream = null;
35 51
     this.audioStream = null;
36 52
     this.VideoLayout = VideoLayout;
37
-}
38
-
39
-function setVisibility(selector, show) {
40
-    if (selector && selector.length > 0) {
41
-        selector.css("visibility", show ? "visible" : "hidden");
42
-    }
53
+    this.videoIsHovered = false;
43 54
 }
44 55
 
45 56
 /**
@@ -60,18 +71,6 @@ SmallVideo.prototype.isVisible = function () {
60 71
     return $('#' + this.videoSpanId).is(':visible');
61 72
 };
62 73
 
63
-SmallVideo.prototype.showDisplayName = function(isShow) {
64
-    var nameSpan = $('#' + this.videoSpanId + ' .displayname').get(0);
65
-    if (isShow) {
66
-        if (nameSpan && nameSpan.innerHTML && nameSpan.innerHTML.length)
67
-            nameSpan.setAttribute("style", "display:inline-block;");
68
-    }
69
-    else {
70
-        if (nameSpan)
71
-            nameSpan.setAttribute("style", "display:none;");
72
-    }
73
-};
74
-
75 74
 /**
76 75
  * Enables / disables the device availability icons for this small video.
77 76
  * @param {enable} set to {true} to enable and {false} to disable
@@ -132,37 +131,6 @@ SmallVideo.prototype.getVideoType = function () {
132 131
     return this.videoType;
133 132
 };
134 133
 
135
-/**
136
- * Shows the presence status message for the given video.
137
- */
138
-SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
139
-    if (!this.container) {
140
-        // No container
141
-        return;
142
-    }
143
-
144
-    var statusSpan = $('#' + this.videoSpanId + '>span.status');
145
-    if (!statusSpan.length) {
146
-        //Add status span
147
-        statusSpan = document.createElement('span');
148
-        statusSpan.className = 'status';
149
-        statusSpan.id = this.videoSpanId + '_status';
150
-        $('#' + this.videoSpanId)[0].appendChild(statusSpan);
151
-
152
-        statusSpan = $('#' + this.videoSpanId + '>span.status');
153
-    }
154
-
155
-    // Display status
156
-    if (statusMsg && statusMsg.length) {
157
-        $('#' + this.videoSpanId + '_status').text(statusMsg);
158
-        statusSpan.get(0).setAttribute("style", "display:inline-block;");
159
-    }
160
-    else {
161
-        // Hide
162
-        statusSpan.get(0).setAttribute("style", "display:none;");
163
-    }
164
-};
165
-
166 134
 /**
167 135
  * Creates an audio or video element for a particular MediaStream.
168 136
  */
@@ -192,6 +160,29 @@ SmallVideo.getStreamElementID = function (stream) {
192 160
     return (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId();
193 161
 };
194 162
 
163
+/**
164
+ * Configures hoverIn/hoverOut handlers. Depends on connection indicator.
165
+ */
166
+SmallVideo.prototype.bindHoverHandler = function () {
167
+    // Add hover handler
168
+    $(this.container).hover(
169
+        () => {
170
+            this.videoIsHovered = true;
171
+            this.updateView();
172
+        },
173
+        () => {
174
+            this.videoIsHovered = false;
175
+            this.updateView();
176
+        }
177
+    );
178
+    if (this.connectionIndicator) {
179
+        this.connectionIndicator.addPopoverHoverListener(
180
+            () => {
181
+                this.updateView();
182
+            });
183
+    }
184
+};
185
+
195 186
 /**
196 187
  * Updates the data for the indicator
197 188
  * @param id the id of the indicator
@@ -395,6 +386,16 @@ SmallVideo.prototype.$avatar = function () {
395 386
     return $('#' + this.videoSpanId + ' .userAvatar');
396 387
 };
397 388
 
389
+/**
390
+ * Returns the display name element, which appears on the video thumbnail.
391
+ *
392
+ * @return {jQuery} a jQuery selector pointing to the display name element of
393
+ * the video thumbnail
394
+ */
395
+SmallVideo.prototype.$displayName = function () {
396
+    return $('#' + this.videoSpanId + ' .displayname');
397
+};
398
+
398 399
 /**
399 400
  * Enables / disables the css responsible for focusing/pinning a video
400 401
  * thumbnail.
@@ -445,19 +446,35 @@ SmallVideo.prototype.isVideoPlayable = function() {
445 446
  * Determines what should be display on the thumbnail.
446 447
  *
447 448
  * @return {number} one of <tt>DISPLAY_VIDEO</tt>,<tt>DISPLAY_AVATAR</tt>
448
- * or <tt>DISPLAY_BLACKNESS</tt>.
449
+ * or <tt>DISPLAY_BLACKNESS_WITH_NAME</tt>.
449 450
  */
450 451
 SmallVideo.prototype.selectDisplayMode = function() {
451 452
     // Display name is always and only displayed when user is on the stage
452 453
     if (this.isCurrentlyOnLargeVideo()) {
453
-        return DISPLAY_BLACKNESS;
454
+        return DISPLAY_BLACKNESS_WITH_NAME;
454 455
     } else if (this.isVideoPlayable() && this.selectVideoElement().length) {
455
-        return DISPLAY_VIDEO;
456
+        // check hovering and change state to video with name
457
+        return this._isHovered() ?
458
+            DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
456 459
     } else {
457
-        return DISPLAY_AVATAR;
460
+        // check hovering and change state to avatar with name
461
+        return this._isHovered() ?
462
+            DISPLAY_AVATAR_WITH_NAME : DISPLAY_AVATAR;
458 463
     }
459 464
 };
460 465
 
466
+/**
467
+ * Checks whether current video is considered hovered. Currently it is hovered
468
+ * if the mouse is over the video, or if the connection
469
+ * indicator is shown(hovered).
470
+ * @private
471
+ */
472
+SmallVideo.prototype._isHovered = function () {
473
+    return this.videoIsHovered
474
+        || (this.connectionIndicator
475
+            && this.connectionIndicator.popover.popoverIsHovered);
476
+};
477
+
461 478
 /**
462 479
  * Hides or shows the user's avatar.
463 480
  * This update assumes that large video had been updated and we will
@@ -479,10 +496,25 @@ SmallVideo.prototype.updateView = function () {
479 496
 
480 497
     // Determine whether video, avatar or blackness should be displayed
481 498
     let displayMode = this.selectDisplayMode();
482
-    // Show/hide video
483
-    setVisibility(this.selectVideoElement(), displayMode === DISPLAY_VIDEO);
484
-    // Show/hide the avatar
485
-    setVisibility(this.$avatar(), displayMode === DISPLAY_AVATAR);
499
+    // Show/hide video.
500
+    UIUtil.setVisibility(   this.selectVideoElement(),
501
+                            (displayMode === DISPLAY_VIDEO
502
+                                || displayMode === DISPLAY_VIDEO_WITH_NAME));
503
+    // Show/hide the avatar.
504
+    UIUtil.setVisibility(   this.$avatar(),
505
+                            (displayMode === DISPLAY_AVATAR
506
+                                || displayMode === DISPLAY_AVATAR_WITH_NAME));
507
+    // Show/hide the display name.
508
+    UIUtil.setVisibility(   this.$displayName(),
509
+                            (displayMode === DISPLAY_BLACKNESS_WITH_NAME
510
+                                || displayMode === DISPLAY_VIDEO_WITH_NAME
511
+                                || displayMode === DISPLAY_AVATAR_WITH_NAME));
512
+    // show hide overlay when there is a video or avatar under
513
+    // the display name
514
+    UIUtil.setVisibility(   $('#' + this.videoSpanId
515
+                                + ' .videocontainer__hoverOverlay'),
516
+                            (displayMode === DISPLAY_AVATAR_WITH_NAME
517
+                                || displayMode === DISPLAY_VIDEO_WITH_NAME));
486 518
 };
487 519
 
488 520
 SmallVideo.prototype.avatarChanged = function (avatarUrl) {
@@ -519,13 +551,21 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
519 551
         return;
520 552
     }
521 553
 
522
-    var indicatorSpan = this.getIndicatorSpan({
523
-        id: 'dominantspeakerindicator',
524
-        content: '<i id="indicatoricon" class="fa fa-bullhorn"></i>',
554
+    let indicatorSpanId = "dominantspeakerindicator";
555
+    let content = `<i id="indicatoricon"
556
+        '             class="indicatoricon fa fa-bullhorn"></i>`;
557
+    let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
558
+        videoSpanId: this.videoSpanId,
559
+        indicatorId: indicatorSpanId,
560
+        content,
525 561
         tooltip: 'speaker'
526 562
     });
527 563
 
528
-    indicatorSpan.style.display = show ? "" : "none";
564
+    if (show) {
565
+        indicatorSpan.classList.add('show');
566
+    } else {
567
+        indicatorSpan.classList.remove('show');
568
+    }
529 569
 };
530 570
 
531 571
 /**
@@ -539,43 +579,21 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) {
539 579
         return;
540 580
     }
541 581
 
542
-    var indicatorSpan = this.getIndicatorSpan({
543
-        id: 'raisehandindicator',
544
-        content: '<i id="indicatoricon" class="icon-raised-hand"></i>',
582
+    let indicatorSpanId = "raisehandindicator";
583
+    let content = `<i id="indicatoricon"
584
+                      class="icon-raised-hand indicatoricon"></i>`;
585
+    let indicatorSpan = UIUtil.getVideoThumbnailIndicatorSpan({
586
+        indicatorId: indicatorSpanId,
587
+        videoSpanId: this.videoSpanId,
588
+        content,
545 589
         tooltip: 'raisedHand'
546 590
     });
547 591
 
548
-    indicatorSpan.style.display = show ? "" : "none";
549
-};
550
-
551
-/**
552
- * Gets (creating if necessary) the "indicator" span for this SmallVideo.
553
- *
554
- * @param options.id {String} element ID
555
- * @param options.content {String} HTML content of the indicator
556
- * @param options.tooltip {String} The key that should be passed to tooltip
557
- *
558
- * @returns {HTMLElement} DOM represention of the indicator
559
- */
560
-SmallVideo.prototype.getIndicatorSpan = function(options) {
561
-    var indicator = this.container.querySelector('#' + options.id);
562
-
563
-    if (indicator) {
564
-        return indicator;
592
+    if (show) {
593
+        indicatorSpan.classList.add('show');
594
+    } else {
595
+        indicatorSpan.classList.remove('show');
565 596
     }
566
-
567
-    indicator = document.createElement('span');
568
-    indicator.className = 'indicator';
569
-    indicator.id = options.id;
570
-
571
-    indicator.innerHTML = options.content;
572
-
573
-    UIUtil.setTooltip(indicator, options.tooltip, "top");
574
-    APP.translation.translateElement($(indicator));
575
-
576
-    this.container.appendChild(indicator);
577
-
578
-    return indicator;
579 597
 };
580 598
 
581 599
 /**

+ 0
- 9
modules/UI/videolayout/VideoLayout.js Visa fil

@@ -449,15 +449,6 @@ var VideoLayout = {
449 449
         }
450 450
     },
451 451
 
452
-    /**
453
-     * Shows the presence status message for the given video.
454
-     */
455
-    setPresenceStatus (id, statusMsg) {
456
-        let remoteVideo = remoteVideos[id];
457
-        if (remoteVideo)
458
-            remoteVideo.setPresenceStatus(statusMsg);
459
-    },
460
-
461 452
     /**
462 453
      * Shows a visual indicator for the moderator of the conference.
463 454
      * On local or remote participants.

Laddar…
Avbryt
Spara