Ver código fonte

feat(connection-indicator): convert to react

- Create a new ConnectionIndicator component for displaying an
  icon for connection quality and for triggering a popover. The
  popover handling has been left in ConnectionIndicator for now,
  which follows the existing implementation.
- Remove the unused method "connectionIndicatorShowMore"
- Change the implementation of existing methods that update the
  connection indicator to call the same method which will rerender
  the indicator completely.
j8
Leonard Kim 8 anos atrás
pai
commit
4ce5888b4c

+ 16
- 7
css/_videolayout_default.scss Ver arquivo

58
         padding: $toolbarPadding;
58
         padding: $toolbarPadding;
59
         padding-bottom: 0;
59
         padding-bottom: 0;
60
 
60
 
61
+        .connection-indicator-container,
62
+        .connection-indicator,
63
+        span.indicator {
64
+            margin-right: em(5, 8);
65
+        }
66
+
67
+        span.indicator {
68
+            display: none;
69
+
70
+            &:last-child {
71
+                margin-right: 0;
72
+            }
73
+        }
74
+
75
+        .connection-indicator,
61
         span.indicator {
76
         span.indicator {
62
             position: relative;
77
             position: relative;
63
             font-size: 8px;
78
             font-size: 8px;
64
             text-align: center;
79
             text-align: center;
65
             line-height: $thumbnailIndicatorSize;
80
             line-height: $thumbnailIndicatorSize;
66
-            display: none;
67
             padding: 0;
81
             padding: 0;
68
-            margin-right: em(5, 8);
69
             float: left;
82
             float: left;
70
             @include circle($thumbnailIndicatorSize);
83
             @include circle($thumbnailIndicatorSize);
71
             box-sizing: border-box;
84
             box-sizing: border-box;
74
             color: $thumbnailPictogramColor;
87
             color: $thumbnailPictogramColor;
75
             border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
88
             border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
76
 
89
 
77
-            &:last-child {
78
-                margin-right: 0;
79
-            }
80
-
81
             .indicatoricon {
90
             .indicatoricon {
82
                 @include absoluteAligning();
91
                 @include absoluteAligning();
83
             }
92
             }
297
   background: $raiseHandBg;
306
   background: $raiseHandBg;
298
 }
307
 }
299
 
308
 
300
-#connectionindicator {
309
+.connection-indicator {
301
   background: $connectionIndicatorBg;
310
   background: $connectionIndicatorBg;
302
 }
311
 }
303
 
312
 

+ 0
- 5
modules/UI/UI.js Ver arquivo

665
     return VideoLayout.getRemoteVideoType(jid);
665
     return VideoLayout.getRemoteVideoType(jid);
666
 };
666
 };
667
 
667
 
668
-UI.connectionIndicatorShowMore = function(id) {
669
-    VideoLayout.showMore(id);
670
-    return false;
671
-};
672
-
673
 // FIXME check if someone user this
668
 // FIXME check if someone user this
674
 UI.showLoginPopup = function(callback) {
669
 UI.showLoginPopup = function(callback) {
675
     logger.log('password is required');
670
     logger.log('password is required');

+ 0
- 273
modules/UI/videolayout/ConnectionIndicator.js Ver arquivo

1
-/* global $, interfaceConfig, JitsiMeetJS */
2
-/* jshint -W101 */
3
-
4
-/* eslint-disable no-unused-vars */
5
-import React from 'react';
6
-import ReactDOM from 'react-dom';
7
-
8
-import { ConnectionStatsTable } from '../../../react/features/connection-stats';
9
-/* eslint-enable no-unused-vars */
10
-
11
-import JitsiPopover from "../util/JitsiPopover";
12
-import UIUtil from "../util/UIUtil";
13
-
14
-const ParticipantConnectionStatus
15
-    = JitsiMeetJS.constants.participantConnectionStatus;
16
-
17
-/**
18
- * Maps a connection quality value (in percent) to the width of the "full" icon.
19
- */
20
-const qualityToWidth = [
21
-    // Full (5 bars)
22
-    {percent: 80, width: "100%"},
23
-    // 4 bars
24
-    {percent: 60, width: "80%"},
25
-    // 3 bars
26
-    {percent: 40, width: "55%"},
27
-    // 2 bars
28
-    {percent: 20, width: "40%"},
29
-    // 1 bar
30
-    {percent: 0, width: "20%"}
31
-    // Note: we never show 0 bars.
32
-];
33
-
34
-/**
35
- * Constructs new connection indicator.
36
- * @param videoContainer the video container associated with the indicator.
37
- * @param videoId the identifier of the video
38
- * @constructor
39
- */
40
-function ConnectionIndicator(videoContainer, videoId) {
41
-    this.videoContainer = videoContainer;
42
-    this.bandwidth = null;
43
-    this.packetLoss = null;
44
-    this.bitrate = null;
45
-    this.showMoreValue = false;
46
-    this.resolution = null;
47
-    this.transport = [];
48
-    this.framerate = null;
49
-    this.popover = null;
50
-    this.id = videoId;
51
-    this.create();
52
-
53
-    this.isLocalVideo
54
-        = this.videoContainer.videoSpanId === 'localVideoContainer';
55
-    this.showMore = this.showMore.bind(this);
56
-}
57
-
58
-/**
59
- * Generates the html content.
60
- * @returns {string} the html content.
61
- */
62
-ConnectionIndicator.prototype.generateText = function () {
63
-    /* jshint ignore:start */
64
-    return (
65
-        <ConnectionStatsTable
66
-            bandwidth = { this.bandwidth }
67
-            bitrate = { this.bitrate }
68
-            isLocalVideo = { this.isLocalVideo }
69
-            framerate = { this.framerate }
70
-            onShowMore = { this.showMore }
71
-            packetLoss = { this.packetLoss}
72
-            resolution = { this.resolution }
73
-            shouldShowMore = { this.showMoreValue }
74
-            transport = { this.transport } />
75
-    );
76
-    /* jshint ignore:end */
77
-};
78
-
79
-/**
80
- * Shows or hide the additional information.
81
- */
82
-ConnectionIndicator.prototype.showMore = function () {
83
-    this.showMoreValue = !this.showMoreValue;
84
-    this.updatePopoverData();
85
-};
86
-
87
-
88
-function createIcon(classes, iconClass) {
89
-    var icon = document.createElement("span");
90
-    for(var i in classes) {
91
-        icon.classList.add(classes[i]);
92
-    }
93
-    icon.appendChild(
94
-        document.createElement("i")).classList.add(iconClass);
95
-    return icon;
96
-}
97
-
98
-/**
99
- * Creates the indicator
100
- */
101
-ConnectionIndicator.prototype.create = function () {
102
-    let indicatorId = 'connectionindicator';
103
-    let element = UIUtil.getVideoThumbnailIndicatorSpan({
104
-        videoSpanId: this.videoContainer.videoSpanId,
105
-        indicatorId
106
-    });
107
-    element.classList.add('show');
108
-    this.connectionIndicatorContainer = element;
109
-
110
-    let popoverContent = (
111
-        `<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
112
-    );
113
-    this.popover = new JitsiPopover($(element), {
114
-        content: popoverContent,
115
-        skin: "black",
116
-        position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
117
-    });
118
-
119
-    // override popover show method to make sure we will update the content
120
-    // before showing the popover
121
-    var origShowFunc = this.popover.show;
122
-    this.popover.show = function () {
123
-        // update content by forcing it, to finish even if popover
124
-        // is not visible
125
-        this.updatePopoverData(true);
126
-        // call the original show, passing its actual this
127
-        origShowFunc.call(this.popover);
128
-    }.bind(this);
129
-
130
-    let connectionIconContainer = document.createElement('div');
131
-    connectionIconContainer.className = 'connection indicatoricon';
132
-
133
-
134
-    this.emptyIcon = connectionIconContainer.appendChild(
135
-        createIcon(["connection_empty"], "icon-connection"));
136
-    this.fullIcon = connectionIconContainer.appendChild(
137
-        createIcon(["connection_full"], "icon-connection"));
138
-    this.interruptedIndicator = connectionIconContainer.appendChild(
139
-        createIcon(["connection_lost"],"icon-connection-lost"));
140
-    this.ninjaIndicator = connectionIconContainer.appendChild(
141
-        createIcon(["connection_ninja"],"icon-ninja"));
142
-
143
-    $(this.interruptedIndicator).hide();
144
-    $(this.ninjaIndicator).hide();
145
-    this.connectionIndicatorContainer.appendChild(connectionIconContainer);
146
-};
147
-
148
-/**
149
- * Removes the indicator
150
- */
151
-ConnectionIndicator.prototype.remove = function() {
152
-    if (this.connectionIndicatorContainer.parentNode) {
153
-        this.connectionIndicatorContainer.parentNode.removeChild(
154
-            this.connectionIndicatorContainer);
155
-    }
156
-    this.popover.forceHide();
157
-};
158
-
159
-/**
160
- * Updates the UI which displays or not a warning about user's connectivity
161
- * problems.
162
- *
163
- * @param {ParticipantConnectionStatus} connectionStatus
164
- */
165
-ConnectionIndicator.prototype.updateConnectionStatusIndicator
166
-= function (connectionStatus) {
167
-    this.connectionStatus = connectionStatus;
168
-    if (connectionStatus === ParticipantConnectionStatus.INTERRUPTED) {
169
-        $(this.interruptedIndicator).show();
170
-        $(this.emptyIcon).hide();
171
-        $(this.fullIcon).hide();
172
-        $(this.ninjaIndicator).hide();
173
-    } else if (connectionStatus === ParticipantConnectionStatus.INACTIVE) {
174
-        $(this.interruptedIndicator).hide();
175
-        $(this.emptyIcon).hide();
176
-        $(this.fullIcon).hide();
177
-        $(this.ninjaIndicator).show();
178
-    } else {
179
-        $(this.interruptedIndicator).hide();
180
-        $(this.emptyIcon).show();
181
-        $(this.fullIcon).show();
182
-        $(this.ninjaIndicator).hide();
183
-    }
184
-};
185
-
186
-/**
187
- * Updates the data of the indicator
188
- * @param percent the percent of connection quality
189
- * @param object the statistics data.
190
- */
191
-ConnectionIndicator.prototype.updateConnectionQuality =
192
-    function (percent, object) {
193
-    if (!percent) {
194
-        this.connectionIndicatorContainer.style.display = "none";
195
-        this.popover.forceHide();
196
-        return;
197
-    } else {
198
-        if(this.connectionIndicatorContainer.style.display == "none") {
199
-            this.connectionIndicatorContainer.style.display = "block";
200
-        }
201
-    }
202
-    if (object) {
203
-        this.bandwidth = object.bandwidth;
204
-        this.bitrate = object.bitrate;
205
-        this.packetLoss = object.packetLoss;
206
-        this.transport = object.transport;
207
-        if (object.resolution) {
208
-            this.resolution = object.resolution;
209
-        }
210
-        if (object.framerate)
211
-            this.framerate = object.framerate;
212
-    }
213
-
214
-    let width = qualityToWidth.find(x => percent >= x.percent);
215
-    this.fullIcon.style.width = width.width;
216
-
217
-    this.updatePopoverData();
218
-};
219
-
220
-/**
221
- * Updates the resolution
222
- * @param resolution the new resolution
223
- */
224
-ConnectionIndicator.prototype.updateResolution = function (resolution) {
225
-    this.resolution = resolution;
226
-    this.updatePopoverData();
227
-};
228
-
229
-/**
230
- * Updates the framerate
231
- * @param framerate the new resolution
232
- */
233
-ConnectionIndicator.prototype.updateFramerate = function (framerate) {
234
-    this.framerate = framerate;
235
-    this.updatePopoverData();
236
-};
237
-
238
-/**
239
- * Updates the content of the popover if its visible
240
- * @param force to work even if popover is not visible
241
- */
242
-ConnectionIndicator.prototype.updatePopoverData = function (force) {
243
-    // generate content, translate it and add it to document only if
244
-    // popover is visible or we force to do so.
245
-    if(this.popover.popoverShown || force) {
246
-        this.popover.updateContent(this.generateText());
247
-    }
248
-};
249
-
250
-/**
251
- * Hides the popover
252
- */
253
-ConnectionIndicator.prototype.hide = function () {
254
-    this.popover.forceHide();
255
-};
256
-
257
-/**
258
- * Hides the indicator
259
- */
260
-ConnectionIndicator.prototype.hideIndicator = function () {
261
-    this.connectionIndicatorContainer.style.display = "none";
262
-    if(this.popover)
263
-        this.popover.forceHide();
264
-};
265
-
266
-/**
267
- * Adds a hover listener to the popover.
268
- */
269
-ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) {
270
-    this.popover.addOnHoverPopover(listener);
271
-};
272
-
273
-export default ConnectionIndicator;

+ 1
- 9
modules/UI/videolayout/LocalVideo.js Ver arquivo

1
 /* global $, config, interfaceConfig, APP, JitsiMeetJS */
1
 /* global $, config, interfaceConfig, APP, JitsiMeetJS */
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
3
 
4
-import ConnectionIndicator from "./ConnectionIndicator";
5
 import UIUtil from "../util/UIUtil";
4
 import UIUtil from "../util/UIUtil";
6
 import UIEvents from "../../../service/UI/UIEvents";
5
 import UIEvents from "../../../service/UI/UIEvents";
7
 import SmallVideo from "./SmallVideo";
6
 import SmallVideo from "./SmallVideo";
13
     this.videoSpanId = "localVideoContainer";
12
     this.videoSpanId = "localVideoContainer";
14
     this.container = $("#localVideoContainer").get(0);
13
     this.container = $("#localVideoContainer").get(0);
15
     this.localVideoId = null;
14
     this.localVideoId = null;
16
-    this.createConnectionIndicator();
17
     this.bindHoverHandler();
15
     this.bindHoverHandler();
18
     if(config.enableLocalVideoFlip)
16
     if(config.enableLocalVideoFlip)
19
         this._buildContextMenu();
17
         this._buildContextMenu();
32
     this.setDisplayName();
30
     this.setDisplayName();
33
 
31
 
34
     this.addAudioLevelIndicator();
32
     this.addAudioLevelIndicator();
33
+    this.updateConnectionIndicator();
35
 }
34
 }
36
 
35
 
37
 LocalVideo.prototype = Object.create(SmallVideo.prototype);
36
 LocalVideo.prototype = Object.create(SmallVideo.prototype);
148
     }
147
     }
149
 };
148
 };
150
 
149
 
151
-LocalVideo.prototype.createConnectionIndicator = function() {
152
-    if(this.connectionIndicator)
153
-        return;
154
-
155
-    this.connectionIndicator = new ConnectionIndicator(this, null);
156
-};
157
-
158
 LocalVideo.prototype.changeVideo = function (stream) {
150
 LocalVideo.prototype.changeVideo = function (stream) {
159
     this.videoStream = stream;
151
     this.videoStream = stream;
160
 
152
 

+ 8
- 22
modules/UI/videolayout/RemoteVideo.js Ver arquivo

15
 
15
 
16
 const logger = require("jitsi-meet-logger").getLogger(__filename);
16
 const logger = require("jitsi-meet-logger").getLogger(__filename);
17
 
17
 
18
-import ConnectionIndicator from './ConnectionIndicator';
19
 
18
 
20
 import SmallVideo from "./SmallVideo";
19
 import SmallVideo from "./SmallVideo";
21
 import UIUtils from "../util/UIUtil";
20
 import UIUtils from "../util/UIUtil";
48
     this.hasRemoteVideoMenu = false;
47
     this.hasRemoteVideoMenu = false;
49
     this._supportsRemoteControl = false;
48
     this._supportsRemoteControl = false;
50
     this.addRemoteVideoContainer();
49
     this.addRemoteVideoContainer();
51
-    this.connectionIndicator = new ConnectionIndicator(this, this.id);
50
+    this.updateConnectionIndicator();
52
     this.setDisplayName();
51
     this.setDisplayName();
53
     this.bindHoverHandler();
52
     this.bindHoverHandler();
54
     this.flipX = false;
53
     this.flipX = false;
497
     // FIXME rename 'mutedWhileDisconnected' to 'mutedWhileNotRendering'
496
     // FIXME rename 'mutedWhileDisconnected' to 'mutedWhileNotRendering'
498
     // Update 'mutedWhileDisconnected' flag
497
     // Update 'mutedWhileDisconnected' flag
499
     this._figureOutMutedWhileDisconnected();
498
     this._figureOutMutedWhileDisconnected();
500
-    if(this.connectionIndicator) {
501
-        this.connectionIndicator.updateConnectionStatusIndicator(
502
-            connectionStatus);
503
-    }
499
+    this.updateConnectionStatus(connectionStatus);
504
 
500
 
505
     const isInterrupted
501
     const isInterrupted
506
         = connectionStatus === ParticipantConnectionStatus.INTERRUPTED;
502
         = connectionStatus === ParticipantConnectionStatus.INTERRUPTED;
621
 };
617
 };
622
 
618
 
623
 RemoteVideo.prototype.updateResolution = function (resolution) {
619
 RemoteVideo.prototype.updateResolution = function (resolution) {
624
-    if (this.connectionIndicator) {
625
-        this.connectionIndicator.updateResolution(resolution);
626
-    }
620
+    this.updateConnectionIndicator({ resolution });
627
 };
621
 };
628
 
622
 
629
 /**
623
 /**
631
  * @param framerate the value to update
625
  * @param framerate the value to update
632
  */
626
  */
633
 RemoteVideo.prototype.updateFramerate = function (framerate) {
627
 RemoteVideo.prototype.updateFramerate = function (framerate) {
634
-    if (this.connectionIndicator) {
635
-        this.connectionIndicator.updateFramerate(framerate);
636
-    }
637
-};
638
-
639
-RemoteVideo.prototype.removeConnectionIndicator = function () {
640
-    if (this.connectionIndicator)
641
-        this.connectionIndicator.remove();
642
-};
643
-
644
-RemoteVideo.prototype.hideConnectionIndicator = function () {
645
-    if (this.connectionIndicator)
646
-        this.connectionIndicator.hide();
628
+    this.updateConnectionIndicator({ framerate });
647
 };
629
 };
648
 
630
 
649
 /**
631
 /**
713
     indicatorBar.className = "videocontainer__toptoolbar";
695
     indicatorBar.className = "videocontainer__toptoolbar";
714
     container.appendChild(indicatorBar);
696
     container.appendChild(indicatorBar);
715
 
697
 
698
+    const connectionIndicatorContainer = document.createElement('span');
699
+    connectionIndicatorContainer.className = 'connection-indicator-container';
700
+    indicatorBar.appendChild(connectionIndicatorContainer);
701
+
716
     let toolbar = document.createElement('div');
702
     let toolbar = document.createElement('div');
717
     toolbar.className = "videocontainer__toolbar";
703
     toolbar.className = "videocontainer__toolbar";
718
     container.appendChild(toolbar);
704
     container.appendChild(toolbar);

+ 90
- 15
modules/UI/videolayout/SmallVideo.js Ver arquivo

6
 
6
 
7
 import { AudioLevelIndicator }
7
 import { AudioLevelIndicator }
8
     from '../../../react/features/audio-level-indicator';
8
     from '../../../react/features/audio-level-indicator';
9
+import {
10
+    ConnectionIndicator
11
+} from '../../../react/features/connection-indicator';
9
 /* eslint-enable no-unused-vars */
12
 /* eslint-enable no-unused-vars */
10
 
13
 
11
 const logger = require("jitsi-meet-logger").getLogger(__filename);
14
 const logger = require("jitsi-meet-logger").getLogger(__filename);
64
     this.hideDisplayName = false;
67
     this.hideDisplayName = false;
65
     // we can stop updating the thumbnail
68
     // we can stop updating the thumbnail
66
     this.disableUpdateView = false;
69
     this.disableUpdateView = false;
70
+
71
+    /**
72
+     * Statistics to display within the connection indicator. With new updates,
73
+     * only changed values are updated through assignment to a new reference.
74
+     *
75
+     * @private
76
+     * @type {object}
77
+     */
78
+    this._cachedConnectionStats = {};
79
+
80
+    /**
81
+     * Whether or not the ConnectionIndicator's popover is hovered. Modifies
82
+     * how the video overlays display based on hover state.
83
+     *
84
+     * @private
85
+     * @type {boolean}
86
+     */
87
+    this._popoverIsHovered = false;
88
+
89
+    // Bind event handlers so they are only bound once for every instance.
90
+    this._onPopoverHover = this._onPopoverHover.bind(this);
91
+    this.updateView = this.updateView.bind(this);
67
 }
92
 }
68
 
93
 
69
 /**
94
 /**
194
             this.updateView();
219
             this.updateView();
195
         }
220
         }
196
     );
221
     );
197
-    if (this.connectionIndicator) {
198
-        this.connectionIndicator.addPopoverHoverListener(
199
-            () => {
200
-                this.updateView();
201
-            });
202
-    }
203
 };
222
 };
204
 
223
 
205
 /**
224
 /**
208
  * @param percent the percent for connection quality
227
  * @param percent the percent for connection quality
209
  * @param object the data
228
  * @param object the data
210
  */
229
  */
211
-SmallVideo.prototype.updateStatsIndicator = function (percent, object) {
212
-    if(this.connectionIndicator)
213
-        this.connectionIndicator.updateConnectionQuality(percent, object);
230
+SmallVideo.prototype.updateConnectionStats = function (percent, object) {
231
+    const newStats = Object.assign({}, object, { percent });
232
+
233
+    this.updateConnectionIndicator(newStats);
214
 };
234
 };
215
 
235
 
216
-SmallVideo.prototype.hideIndicator = function () {
217
-    if(this.connectionIndicator)
218
-        this.connectionIndicator.hideIndicator();
236
+/**
237
+ * Unmounts the ConnectionIndicator component.
238
+
239
+ * @returns {void}
240
+ */
241
+SmallVideo.prototype.removeConnectionIndicator = function () {
242
+    const connectionIndicatorContainer
243
+        = this.container.querySelector('.connection-indicator-container');
244
+
245
+    if (connectionIndicatorContainer) {
246
+        ReactDOM.unmountComponentAtNode(connectionIndicatorContainer);
247
+    }
219
 };
248
 };
220
 
249
 
250
+/**
251
+ * Updates the connectionStatus stat which displays in the ConnectionIndicator.
252
+
253
+ * @returns {void}
254
+ */
255
+SmallVideo.prototype.updateConnectionStatus = function (connectionStatus) {
256
+    this.updateConnectionIndicator({ connectionStatus });
257
+};
221
 
258
 
222
 /**
259
 /**
223
  * Shows / hides the audio muted indicator over small videos.
260
  * Shows / hides the audio muted indicator over small videos.
524
  * @private
561
  * @private
525
  */
562
  */
526
 SmallVideo.prototype._isHovered = function () {
563
 SmallVideo.prototype._isHovered = function () {
527
-    return this.videoIsHovered
528
-        || (this.connectionIndicator
529
-            && this.connectionIndicator.popover.popoverIsHovered);
564
+    return this.videoIsHovered || this._popoverIsHovered;
530
 };
565
 };
531
 
566
 
532
 /**
567
 /**
700
     }
735
     }
701
 };
736
 };
702
 
737
 
738
+/**
739
+ * Creates or updates the connection indicator. Updates the previously known
740
+ * statistics about the participant's connection.
741
+ *
742
+ * @param {Object} newStats - New statistics to merge with previously known
743
+ * statistics about the participant's connection.
744
+ * @returns {void}
745
+ */
746
+SmallVideo.prototype.updateConnectionIndicator = function (newStats = {}) {
747
+    this._cachedConnectionStats
748
+        = Object.assign({}, this._cachedConnectionStats, newStats);
749
+
750
+    const connectionIndicatorContainer
751
+        = this.container.querySelector('.connection-indicator-container');
752
+
753
+    /* jshint ignore:start */
754
+    ReactDOM.render(
755
+        <ConnectionIndicator
756
+            isLocalVideo = { this.isLocal }
757
+            onHover = { this._onPopoverHover }
758
+            showMoreLink = { this.isLocal }
759
+            stats = { this._cachedConnectionStats } />,
760
+        connectionIndicatorContainer
761
+    );
762
+    /* jshint ignore:end */
763
+};
764
+
765
+/**
766
+ * Updates the current state of the connection indicator popover being hovered.
767
+ * If hovered, display the small video as if it is hovered.
768
+ *
769
+ * @param {boolean} popoverIsHovered - Whether or not the mouse cursor is
770
+ * currently over the connection indicator popover.
771
+ * @returns {void}
772
+ */
773
+SmallVideo.prototype._onPopoverHover = function (popoverIsHovered) {
774
+    this._popoverIsHovered = popoverIsHovered;
775
+    this.updateView();
776
+};
777
+
703
 export default SmallVideo;
778
 export default SmallVideo;

+ 4
- 18
modules/UI/videolayout/VideoLayout.js Ver arquivo

570
                 ? ParticipantConnectionStatus.INTERRUPTED
570
                 ? ParticipantConnectionStatus.INTERRUPTED
571
                 : ParticipantConnectionStatus.ACTIVE;
571
                 : ParticipantConnectionStatus.ACTIVE;
572
 
572
 
573
-        localVideoThumbnail
574
-            .connectionIndicator.updateConnectionStatusIndicator(status);
573
+        localVideoThumbnail.updateConnectionStatus(status);
575
     },
574
     },
576
 
575
 
577
     /**
576
     /**
779
         // Why library internal objects are passed as event's args ?
778
         // Why library internal objects are passed as event's args ?
780
         object.resolution = resolution[APP.conference.getMyUserId()];
779
         object.resolution = resolution[APP.conference.getMyUserId()];
781
         object.framerate = framerate[APP.conference.getMyUserId()];
780
         object.framerate = framerate[APP.conference.getMyUserId()];
782
-        localVideoThumbnail.updateStatsIndicator(percent, object);
781
+        localVideoThumbnail.updateConnectionStats(percent, object);
783
 
782
 
784
         Object.keys(resolution).forEach(function (id) {
783
         Object.keys(resolution).forEach(function (id) {
785
             if (APP.conference.isLocalId(id)) {
784
             if (APP.conference.isLocalId(id)) {
817
     updateConnectionStats (id, percent, object) {
816
     updateConnectionStats (id, percent, object) {
818
         let remoteVideo = remoteVideos[id];
817
         let remoteVideo = remoteVideos[id];
819
         if (remoteVideo) {
818
         if (remoteVideo) {
820
-            remoteVideo.updateStatsIndicator(percent, object);
819
+            remoteVideo.updateConnectionStats(percent, object);
821
         }
820
         }
822
     },
821
     },
823
 
822
 
828
     hideConnectionIndicator (id) {
827
     hideConnectionIndicator (id) {
829
         let remoteVideo = remoteVideos[id];
828
         let remoteVideo = remoteVideos[id];
830
         if (remoteVideo)
829
         if (remoteVideo)
831
-            remoteVideo.hideConnectionIndicator();
830
+            remoteVideo.removeConnectionIndicator();
832
     },
831
     },
833
 
832
 
834
     /**
833
     /**
894
         }
893
         }
895
     },
894
     },
896
 
895
 
897
-    showMore (id) {
898
-        if (id === 'local') {
899
-            localVideoThumbnail.connectionIndicator.showMore();
900
-        } else {
901
-            let remoteVideo = remoteVideos[id];
902
-            if (remoteVideo) {
903
-                remoteVideo.connectionIndicator.showMore();
904
-            } else {
905
-                logger.info("Error - no remote video for id: " + id);
906
-            }
907
-        }
908
-    },
909
-
910
     /**
896
     /**
911
      * Resizes the video area.
897
      * Resizes the video area.
912
      *
898
      *

+ 284
- 0
react/features/connection-indicator/components/ConnectionIndicator.js Ver arquivo

1
+import React, { Component } from 'react';
2
+
3
+import JitsiPopover from '../../../../modules/UI/util/JitsiPopover';
4
+
5
+import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
6
+import { ConnectionStatsTable } from '../../connection-stats';
7
+
8
+declare var $: Object;
9
+declare var interfaceConfig: Object;
10
+
11
+// Converts the percent for connection quality into a string recognized for CSS.
12
+const QUALITY_TO_WIDTH = [
13
+
14
+    // Full (5 bars)
15
+    {
16
+        percent: 80,
17
+        width: '100%'
18
+    },
19
+
20
+    // 4 bars
21
+    {
22
+        percent: 60,
23
+        width: '80%'
24
+    },
25
+
26
+    // 3 bars
27
+    {
28
+        percent: 40,
29
+        width: '55%'
30
+    },
31
+
32
+    // 2 bars
33
+    {
34
+        percent: 20,
35
+        width: '40%'
36
+    },
37
+
38
+    // 1 bar
39
+    {
40
+        percent: 0,
41
+        width: '20%'
42
+    }
43
+
44
+    // Note: we never show 0 bars.
45
+];
46
+
47
+/**
48
+ * Implements a React {@link Component} which displays the current connection
49
+ * quality percentage and has a popover to show more detailed connection stats.
50
+ *
51
+ * @extends {Component}
52
+ */
53
+class ConnectionIndicator extends Component {
54
+    /**
55
+     * {@code ConnectionIndicator} component's property types.
56
+     *
57
+     * @static
58
+     */
59
+    static propTypes = {
60
+        /**
61
+         * Whether or not the displays stats are for local video.
62
+         */
63
+        isLocalVideo: React.PropTypes.bool,
64
+
65
+        /**
66
+         * The callback to invoke when the hover state over the popover changes.
67
+         */
68
+        onHover: React.PropTypes.func,
69
+
70
+        /**
71
+         * Whether or not the popover should display a link that can toggle
72
+         * a more detailed view of the stats.
73
+         */
74
+        showMoreLink: React.PropTypes.bool,
75
+
76
+        /**
77
+         * An object that contains statistics related to connection quality.
78
+         *
79
+         * {
80
+         *     bandwidth: Object,
81
+         *     bitrate: Object,
82
+         *     connectionStatus: String,
83
+         *     framerate: Object,
84
+         *     packetLoss: Object,
85
+         *     percent: Number,
86
+         *     resolution: Object,
87
+         *     transport:  Array
88
+         * }
89
+         */
90
+        stats: React.PropTypes.object,
91
+
92
+        /**
93
+         * Invoked to obtain translated strings.
94
+         */
95
+        t: React.PropTypes.func
96
+    };
97
+
98
+    /**
99
+     * Initializes a new {@code ConnectionIndicator} instance.
100
+     *
101
+     * @param {Object} props - The read-only properties with which the new
102
+     * instance is to be initialized.
103
+     */
104
+    constructor(props) {
105
+        super(props);
106
+
107
+        /**
108
+         * The internal reference to topmost DOM/HTML element backing the React
109
+         * {@code Component}. Accessed directly for associating an element as
110
+         * the trigger for a popover.
111
+         *
112
+         * @private
113
+         * @type {HTMLDivElement}
114
+         */
115
+        this._rootElement = null;
116
+
117
+        this.state = {
118
+            /**
119
+             * Whether or not the popover content should display additional
120
+             * statistics.
121
+             *
122
+             * @type {boolean}
123
+             */
124
+            showMoreStats: false
125
+        };
126
+
127
+        // Bind event handlers so they are only bound once for every instance.
128
+        this._onToggleShowMore = this._onToggleShowMore.bind(this);
129
+        this._setRootElement = this._setRootElement.bind(this);
130
+    }
131
+
132
+    /**
133
+     * Creates a popover instance to display when the component is hovered.
134
+     *
135
+     * @inheritdoc
136
+     * returns {void}
137
+     */
138
+    componentDidMount() {
139
+        this.popover = new JitsiPopover($(this._rootElement), {
140
+            content: this._renderStatisticsTable(),
141
+            skin: 'black',
142
+            position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
143
+        });
144
+
145
+        this.popover.addOnHoverPopover(this.props.onHover);
146
+    }
147
+
148
+    /**
149
+     * Updates the contents of the popover. This is done manually because the
150
+     * popover is not a React Component yet and so is not automatiucally aware
151
+     * of changed data.
152
+     *
153
+     * @inheritdoc
154
+     * returns {void}
155
+     */
156
+    componentDidUpdate() {
157
+        this.popover.updateContent(this._renderStatisticsTable());
158
+    }
159
+
160
+    /**
161
+     * Cleans up any popover instance that is linked to the component.
162
+     *
163
+     * @inheritdoc
164
+     * returns {void}
165
+     */
166
+    componentWillUnmount() {
167
+        this.popover.forceHide();
168
+        this.popover.remove();
169
+    }
170
+
171
+    /**
172
+     * Implements React's {@link Component#render()}.
173
+     *
174
+     * @inheritdoc
175
+     * @returns {ReactElement}
176
+     */
177
+    render() {
178
+        return (
179
+            <div
180
+                className = 'connection-indicator indicator'
181
+                ref = { this._setRootElement }>
182
+                <div className = 'connection indicatoricon'>
183
+                    { this._renderIcon() }
184
+                </div>
185
+            </div>
186
+        );
187
+    }
188
+
189
+    /**
190
+     * Callback to invoke when the show more link in the popover content is
191
+     * clicked. Sets the state which will determine if the popover should show
192
+     * additional statistics about the connection.
193
+     *
194
+     * @returns {void}
195
+     */
196
+    _onToggleShowMore() {
197
+        this.setState({ showMoreStats: !this.state.showMoreStats });
198
+    }
199
+
200
+    /**
201
+     * Creates a ReactElement for displaying an icon that represents the current
202
+     * connection quality.
203
+     *
204
+     * @returns {ReactElement}
205
+     */
206
+    _renderIcon() {
207
+        switch (this.props.stats.connectionStatus) {
208
+        case JitsiParticipantConnectionStatus.INTERRUPTED:
209
+            return (
210
+                <span className = 'connection_lost'>
211
+                    <i className = 'icon-connection-lost' />
212
+                </span>
213
+            );
214
+        case JitsiParticipantConnectionStatus.INACTIVE:
215
+            return (
216
+                <span className = 'connection_ninja'>
217
+                    <i className = 'icon-ninja' />
218
+                </span>
219
+            );
220
+        default: {
221
+            const { percent } = this.props.stats;
222
+            const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
223
+            const iconWidth = width && width.width
224
+                ? { width: width && width.width } : {};
225
+
226
+            return [
227
+                <span
228
+                    className = 'connection_empty'
229
+                    key = 'icon-empty'>
230
+                    <i className = 'icon-connection' />
231
+                </span>,
232
+                <span
233
+                    className = 'connection_full'
234
+                    key = 'icon-full'
235
+                    style = { iconWidth }>
236
+                    <i className = 'icon-connection' />
237
+                </span>
238
+            ];
239
+        }
240
+        }
241
+    }
242
+
243
+    /**
244
+     * Creates a {@code ConnectionStatisticsTable} instance.
245
+     *
246
+     * @returns {ReactElement}
247
+     */
248
+    _renderStatisticsTable() {
249
+        const {
250
+            bandwidth,
251
+            bitrate,
252
+            framerate,
253
+            packetLoss,
254
+            resolution,
255
+            transport
256
+        } = this.props.stats;
257
+
258
+        return (
259
+            <ConnectionStatsTable
260
+                bandwidth = { bandwidth }
261
+                bitrate = { bitrate }
262
+                framerate = { framerate }
263
+                isLocalVideo = { this.props.isLocalVideo }
264
+                onShowMore = { this._onToggleShowMore }
265
+                packetLoss = { packetLoss }
266
+                resolution = { resolution }
267
+                shouldShowMore = { this.state.showMoreStats }
268
+                transport = { transport } />
269
+        );
270
+    }
271
+
272
+    /**
273
+     * Sets an internal reference to the component's root element.
274
+     *
275
+     * @param {Object} element - The highest DOM element in the component.
276
+     * @private
277
+     * @returns {void}
278
+     */
279
+    _setRootElement(element) {
280
+        this._rootElement = element;
281
+    }
282
+}
283
+
284
+export default ConnectionIndicator;

+ 1
- 0
react/features/connection-indicator/components/index.js Ver arquivo

1
+export { default as ConnectionIndicator } from './ConnectionIndicator';

+ 1
- 0
react/features/connection-indicator/index.js Ver arquivo

1
+export * from './components';

+ 5
- 1
react/features/filmstrip/components/Filmstrip.web.js Ver arquivo

34
                                 id = 'localAudio'
34
                                 id = 'localAudio'
35
                                 muted = { true } />
35
                                 muted = { true } />
36
                             <div className = 'videocontainer__toolbar' />
36
                             <div className = 'videocontainer__toolbar' />
37
-                            <div className = 'videocontainer__toptoolbar' />
37
+                            <div className = 'videocontainer__toptoolbar'>
38
+                                <span
39
+                                    className
40
+                                        = 'connection-indicator-container' />
41
+                            </div>
38
                             <div className = 'videocontainer__hoverOverlay' />
42
                             <div className = 'videocontainer__hoverOverlay' />
39
                         </span>
43
                         </span>
40
                     </div>
44
                     </div>

Carregando…
Cancelar
Salvar