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.
This commit is contained in:
parent
35f79dd2b4
commit
4ce5888b4c
|
@ -58,14 +58,27 @@
|
|||
padding: $toolbarPadding;
|
||||
padding-bottom: 0;
|
||||
|
||||
.connection-indicator-container,
|
||||
.connection-indicator,
|
||||
span.indicator {
|
||||
margin-right: em(5, 8);
|
||||
}
|
||||
|
||||
span.indicator {
|
||||
display: none;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.connection-indicator,
|
||||
span.indicator {
|
||||
position: relative;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
line-height: $thumbnailIndicatorSize;
|
||||
display: none;
|
||||
padding: 0;
|
||||
margin-right: em(5, 8);
|
||||
float: left;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
|
@ -74,10 +87,6 @@
|
|||
color: $thumbnailPictogramColor;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.indicatoricon {
|
||||
@include absoluteAligning();
|
||||
}
|
||||
|
@ -297,7 +306,7 @@
|
|||
background: $raiseHandBg;
|
||||
}
|
||||
|
||||
#connectionindicator {
|
||||
.connection-indicator {
|
||||
background: $connectionIndicatorBg;
|
||||
}
|
||||
|
||||
|
|
|
@ -665,11 +665,6 @@ UI.getRemoteVideoType = function (jid) {
|
|||
return VideoLayout.getRemoteVideoType(jid);
|
||||
};
|
||||
|
||||
UI.connectionIndicatorShowMore = function(id) {
|
||||
VideoLayout.showMore(id);
|
||||
return false;
|
||||
};
|
||||
|
||||
// FIXME check if someone user this
|
||||
UI.showLoginPopup = function(callback) {
|
||||
logger.log('password is required');
|
||||
|
|
|
@ -1,273 +0,0 @@
|
|||
/* global $, interfaceConfig, JitsiMeetJS */
|
||||
/* jshint -W101 */
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { ConnectionStatsTable } from '../../../react/features/connection-stats';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
import JitsiPopover from "../util/JitsiPopover";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
const ParticipantConnectionStatus
|
||||
= JitsiMeetJS.constants.participantConnectionStatus;
|
||||
|
||||
/**
|
||||
* Maps a connection quality value (in percent) to the width of the "full" icon.
|
||||
*/
|
||||
const qualityToWidth = [
|
||||
// Full (5 bars)
|
||||
{percent: 80, width: "100%"},
|
||||
// 4 bars
|
||||
{percent: 60, width: "80%"},
|
||||
// 3 bars
|
||||
{percent: 40, width: "55%"},
|
||||
// 2 bars
|
||||
{percent: 20, width: "40%"},
|
||||
// 1 bar
|
||||
{percent: 0, width: "20%"}
|
||||
// Note: we never show 0 bars.
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructs new connection indicator.
|
||||
* @param videoContainer the video container associated with the indicator.
|
||||
* @param videoId the identifier of the video
|
||||
* @constructor
|
||||
*/
|
||||
function ConnectionIndicator(videoContainer, videoId) {
|
||||
this.videoContainer = videoContainer;
|
||||
this.bandwidth = null;
|
||||
this.packetLoss = null;
|
||||
this.bitrate = null;
|
||||
this.showMoreValue = false;
|
||||
this.resolution = null;
|
||||
this.transport = [];
|
||||
this.framerate = null;
|
||||
this.popover = null;
|
||||
this.id = videoId;
|
||||
this.create();
|
||||
|
||||
this.isLocalVideo
|
||||
= this.videoContainer.videoSpanId === 'localVideoContainer';
|
||||
this.showMore = this.showMore.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the html content.
|
||||
* @returns {string} the html content.
|
||||
*/
|
||||
ConnectionIndicator.prototype.generateText = function () {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
<ConnectionStatsTable
|
||||
bandwidth = { this.bandwidth }
|
||||
bitrate = { this.bitrate }
|
||||
isLocalVideo = { this.isLocalVideo }
|
||||
framerate = { this.framerate }
|
||||
onShowMore = { this.showMore }
|
||||
packetLoss = { this.packetLoss}
|
||||
resolution = { this.resolution }
|
||||
shouldShowMore = { this.showMoreValue }
|
||||
transport = { this.transport } />
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows or hide the additional information.
|
||||
*/
|
||||
ConnectionIndicator.prototype.showMore = function () {
|
||||
this.showMoreValue = !this.showMoreValue;
|
||||
this.updatePopoverData();
|
||||
};
|
||||
|
||||
|
||||
function createIcon(classes, iconClass) {
|
||||
var icon = document.createElement("span");
|
||||
for(var i in classes) {
|
||||
icon.classList.add(classes[i]);
|
||||
}
|
||||
icon.appendChild(
|
||||
document.createElement("i")).classList.add(iconClass);
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indicator
|
||||
*/
|
||||
ConnectionIndicator.prototype.create = function () {
|
||||
let indicatorId = 'connectionindicator';
|
||||
let element = UIUtil.getVideoThumbnailIndicatorSpan({
|
||||
videoSpanId: this.videoContainer.videoSpanId,
|
||||
indicatorId
|
||||
});
|
||||
element.classList.add('show');
|
||||
this.connectionIndicatorContainer = element;
|
||||
|
||||
let popoverContent = (
|
||||
`<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
|
||||
);
|
||||
this.popover = new JitsiPopover($(element), {
|
||||
content: popoverContent,
|
||||
skin: "black",
|
||||
position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
|
||||
});
|
||||
|
||||
// override popover show method to make sure we will update the content
|
||||
// before showing the popover
|
||||
var origShowFunc = this.popover.show;
|
||||
this.popover.show = function () {
|
||||
// update content by forcing it, to finish even if popover
|
||||
// is not visible
|
||||
this.updatePopoverData(true);
|
||||
// call the original show, passing its actual this
|
||||
origShowFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
|
||||
let connectionIconContainer = document.createElement('div');
|
||||
connectionIconContainer.className = 'connection indicatoricon';
|
||||
|
||||
|
||||
this.emptyIcon = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_empty"], "icon-connection"));
|
||||
this.fullIcon = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_full"], "icon-connection"));
|
||||
this.interruptedIndicator = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_lost"],"icon-connection-lost"));
|
||||
this.ninjaIndicator = connectionIconContainer.appendChild(
|
||||
createIcon(["connection_ninja"],"icon-ninja"));
|
||||
|
||||
$(this.interruptedIndicator).hide();
|
||||
$(this.ninjaIndicator).hide();
|
||||
this.connectionIndicatorContainer.appendChild(connectionIconContainer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the indicator
|
||||
*/
|
||||
ConnectionIndicator.prototype.remove = function() {
|
||||
if (this.connectionIndicatorContainer.parentNode) {
|
||||
this.connectionIndicatorContainer.parentNode.removeChild(
|
||||
this.connectionIndicatorContainer);
|
||||
}
|
||||
this.popover.forceHide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the UI which displays or not a warning about user's connectivity
|
||||
* problems.
|
||||
*
|
||||
* @param {ParticipantConnectionStatus} connectionStatus
|
||||
*/
|
||||
ConnectionIndicator.prototype.updateConnectionStatusIndicator
|
||||
= function (connectionStatus) {
|
||||
this.connectionStatus = connectionStatus;
|
||||
if (connectionStatus === ParticipantConnectionStatus.INTERRUPTED) {
|
||||
$(this.interruptedIndicator).show();
|
||||
$(this.emptyIcon).hide();
|
||||
$(this.fullIcon).hide();
|
||||
$(this.ninjaIndicator).hide();
|
||||
} else if (connectionStatus === ParticipantConnectionStatus.INACTIVE) {
|
||||
$(this.interruptedIndicator).hide();
|
||||
$(this.emptyIcon).hide();
|
||||
$(this.fullIcon).hide();
|
||||
$(this.ninjaIndicator).show();
|
||||
} else {
|
||||
$(this.interruptedIndicator).hide();
|
||||
$(this.emptyIcon).show();
|
||||
$(this.fullIcon).show();
|
||||
$(this.ninjaIndicator).hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the data of the indicator
|
||||
* @param percent the percent of connection quality
|
||||
* @param object the statistics data.
|
||||
*/
|
||||
ConnectionIndicator.prototype.updateConnectionQuality =
|
||||
function (percent, object) {
|
||||
if (!percent) {
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
this.popover.forceHide();
|
||||
return;
|
||||
} else {
|
||||
if(this.connectionIndicatorContainer.style.display == "none") {
|
||||
this.connectionIndicatorContainer.style.display = "block";
|
||||
}
|
||||
}
|
||||
if (object) {
|
||||
this.bandwidth = object.bandwidth;
|
||||
this.bitrate = object.bitrate;
|
||||
this.packetLoss = object.packetLoss;
|
||||
this.transport = object.transport;
|
||||
if (object.resolution) {
|
||||
this.resolution = object.resolution;
|
||||
}
|
||||
if (object.framerate)
|
||||
this.framerate = object.framerate;
|
||||
}
|
||||
|
||||
let width = qualityToWidth.find(x => percent >= x.percent);
|
||||
this.fullIcon.style.width = width.width;
|
||||
|
||||
this.updatePopoverData();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the resolution
|
||||
* @param resolution the new resolution
|
||||
*/
|
||||
ConnectionIndicator.prototype.updateResolution = function (resolution) {
|
||||
this.resolution = resolution;
|
||||
this.updatePopoverData();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the framerate
|
||||
* @param framerate the new resolution
|
||||
*/
|
||||
ConnectionIndicator.prototype.updateFramerate = function (framerate) {
|
||||
this.framerate = framerate;
|
||||
this.updatePopoverData();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the content of the popover if its visible
|
||||
* @param force to work even if popover is not visible
|
||||
*/
|
||||
ConnectionIndicator.prototype.updatePopoverData = function (force) {
|
||||
// generate content, translate it and add it to document only if
|
||||
// popover is visible or we force to do so.
|
||||
if(this.popover.popoverShown || force) {
|
||||
this.popover.updateContent(this.generateText());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the popover
|
||||
*/
|
||||
ConnectionIndicator.prototype.hide = function () {
|
||||
this.popover.forceHide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the indicator
|
||||
*/
|
||||
ConnectionIndicator.prototype.hideIndicator = function () {
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
if(this.popover)
|
||||
this.popover.forceHide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a hover listener to the popover.
|
||||
*/
|
||||
ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) {
|
||||
this.popover.addOnHoverPopover(listener);
|
||||
};
|
||||
|
||||
export default ConnectionIndicator;
|
|
@ -1,7 +1,6 @@
|
|||
/* global $, config, interfaceConfig, APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import ConnectionIndicator from "./ConnectionIndicator";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import SmallVideo from "./SmallVideo";
|
||||
|
@ -13,7 +12,6 @@ function LocalVideo(VideoLayout, emitter) {
|
|||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.localVideoId = null;
|
||||
this.createConnectionIndicator();
|
||||
this.bindHoverHandler();
|
||||
if(config.enableLocalVideoFlip)
|
||||
this._buildContextMenu();
|
||||
|
@ -32,6 +30,7 @@ function LocalVideo(VideoLayout, emitter) {
|
|||
this.setDisplayName();
|
||||
|
||||
this.addAudioLevelIndicator();
|
||||
this.updateConnectionIndicator();
|
||||
}
|
||||
|
||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
|
@ -148,13 +147,6 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
|
|||
}
|
||||
};
|
||||
|
||||
LocalVideo.prototype.createConnectionIndicator = function() {
|
||||
if(this.connectionIndicator)
|
||||
return;
|
||||
|
||||
this.connectionIndicator = new ConnectionIndicator(this, null);
|
||||
};
|
||||
|
||||
LocalVideo.prototype.changeVideo = function (stream) {
|
||||
this.videoStream = stream;
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import ConnectionIndicator from './ConnectionIndicator';
|
||||
|
||||
import SmallVideo from "./SmallVideo";
|
||||
import UIUtils from "../util/UIUtil";
|
||||
|
@ -48,7 +47,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
|||
this.hasRemoteVideoMenu = false;
|
||||
this._supportsRemoteControl = false;
|
||||
this.addRemoteVideoContainer();
|
||||
this.connectionIndicator = new ConnectionIndicator(this, this.id);
|
||||
this.updateConnectionIndicator();
|
||||
this.setDisplayName();
|
||||
this.bindHoverHandler();
|
||||
this.flipX = false;
|
||||
|
@ -497,10 +496,7 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function () {
|
|||
// FIXME rename 'mutedWhileDisconnected' to 'mutedWhileNotRendering'
|
||||
// Update 'mutedWhileDisconnected' flag
|
||||
this._figureOutMutedWhileDisconnected();
|
||||
if(this.connectionIndicator) {
|
||||
this.connectionIndicator.updateConnectionStatusIndicator(
|
||||
connectionStatus);
|
||||
}
|
||||
this.updateConnectionStatus(connectionStatus);
|
||||
|
||||
const isInterrupted
|
||||
= connectionStatus === ParticipantConnectionStatus.INTERRUPTED;
|
||||
|
@ -621,9 +617,7 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
|
|||
};
|
||||
|
||||
RemoteVideo.prototype.updateResolution = function (resolution) {
|
||||
if (this.connectionIndicator) {
|
||||
this.connectionIndicator.updateResolution(resolution);
|
||||
}
|
||||
this.updateConnectionIndicator({ resolution });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -631,19 +625,7 @@ RemoteVideo.prototype.updateResolution = function (resolution) {
|
|||
* @param framerate the value to update
|
||||
*/
|
||||
RemoteVideo.prototype.updateFramerate = function (framerate) {
|
||||
if (this.connectionIndicator) {
|
||||
this.connectionIndicator.updateFramerate(framerate);
|
||||
}
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.removeConnectionIndicator = function () {
|
||||
if (this.connectionIndicator)
|
||||
this.connectionIndicator.remove();
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||
if (this.connectionIndicator)
|
||||
this.connectionIndicator.hide();
|
||||
this.updateConnectionIndicator({ framerate });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -713,6 +695,10 @@ RemoteVideo.createContainer = function (spanId) {
|
|||
indicatorBar.className = "videocontainer__toptoolbar";
|
||||
container.appendChild(indicatorBar);
|
||||
|
||||
const connectionIndicatorContainer = document.createElement('span');
|
||||
connectionIndicatorContainer.className = 'connection-indicator-container';
|
||||
indicatorBar.appendChild(connectionIndicatorContainer);
|
||||
|
||||
let toolbar = document.createElement('div');
|
||||
toolbar.className = "videocontainer__toolbar";
|
||||
container.appendChild(toolbar);
|
||||
|
|
|
@ -6,6 +6,9 @@ import ReactDOM from 'react-dom';
|
|||
|
||||
import { AudioLevelIndicator }
|
||||
from '../../../react/features/audio-level-indicator';
|
||||
import {
|
||||
ConnectionIndicator
|
||||
} from '../../../react/features/connection-indicator';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
@ -64,6 +67,28 @@ function SmallVideo(VideoLayout) {
|
|||
this.hideDisplayName = false;
|
||||
// we can stop updating the thumbnail
|
||||
this.disableUpdateView = false;
|
||||
|
||||
/**
|
||||
* Statistics to display within the connection indicator. With new updates,
|
||||
* only changed values are updated through assignment to a new reference.
|
||||
*
|
||||
* @private
|
||||
* @type {object}
|
||||
*/
|
||||
this._cachedConnectionStats = {};
|
||||
|
||||
/**
|
||||
* Whether or not the ConnectionIndicator's popover is hovered. Modifies
|
||||
* how the video overlays display based on hover state.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._popoverIsHovered = false;
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onPopoverHover = this._onPopoverHover.bind(this);
|
||||
this.updateView = this.updateView.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,12 +219,6 @@ SmallVideo.prototype.bindHoverHandler = function () {
|
|||
this.updateView();
|
||||
}
|
||||
);
|
||||
if (this.connectionIndicator) {
|
||||
this.connectionIndicator.addPopoverHoverListener(
|
||||
() => {
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -208,16 +227,34 @@ SmallVideo.prototype.bindHoverHandler = function () {
|
|||
* @param percent the percent for connection quality
|
||||
* @param object the data
|
||||
*/
|
||||
SmallVideo.prototype.updateStatsIndicator = function (percent, object) {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.updateConnectionQuality(percent, object);
|
||||
SmallVideo.prototype.updateConnectionStats = function (percent, object) {
|
||||
const newStats = Object.assign({}, object, { percent });
|
||||
|
||||
this.updateConnectionIndicator(newStats);
|
||||
};
|
||||
|
||||
SmallVideo.prototype.hideIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.hideIndicator();
|
||||
/**
|
||||
* Unmounts the ConnectionIndicator component.
|
||||
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype.removeConnectionIndicator = function () {
|
||||
const connectionIndicatorContainer
|
||||
= this.container.querySelector('.connection-indicator-container');
|
||||
|
||||
if (connectionIndicatorContainer) {
|
||||
ReactDOM.unmountComponentAtNode(connectionIndicatorContainer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the connectionStatus stat which displays in the ConnectionIndicator.
|
||||
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype.updateConnectionStatus = function (connectionStatus) {
|
||||
this.updateConnectionIndicator({ connectionStatus });
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows / hides the audio muted indicator over small videos.
|
||||
|
@ -524,9 +561,7 @@ SmallVideo.prototype.selectDisplayMode = function() {
|
|||
* @private
|
||||
*/
|
||||
SmallVideo.prototype._isHovered = function () {
|
||||
return this.videoIsHovered
|
||||
|| (this.connectionIndicator
|
||||
&& this.connectionIndicator.popover.popoverIsHovered);
|
||||
return this.videoIsHovered || this._popoverIsHovered;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -700,4 +735,44 @@ SmallVideo.prototype.initBrowserSpecificProperties = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates or updates the connection indicator. Updates the previously known
|
||||
* statistics about the participant's connection.
|
||||
*
|
||||
* @param {Object} newStats - New statistics to merge with previously known
|
||||
* statistics about the participant's connection.
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype.updateConnectionIndicator = function (newStats = {}) {
|
||||
this._cachedConnectionStats
|
||||
= Object.assign({}, this._cachedConnectionStats, newStats);
|
||||
|
||||
const connectionIndicatorContainer
|
||||
= this.container.querySelector('.connection-indicator-container');
|
||||
|
||||
/* jshint ignore:start */
|
||||
ReactDOM.render(
|
||||
<ConnectionIndicator
|
||||
isLocalVideo = { this.isLocal }
|
||||
onHover = { this._onPopoverHover }
|
||||
showMoreLink = { this.isLocal }
|
||||
stats = { this._cachedConnectionStats } />,
|
||||
connectionIndicatorContainer
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current state of the connection indicator popover being hovered.
|
||||
* If hovered, display the small video as if it is hovered.
|
||||
*
|
||||
* @param {boolean} popoverIsHovered - Whether or not the mouse cursor is
|
||||
* currently over the connection indicator popover.
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype._onPopoverHover = function (popoverIsHovered) {
|
||||
this._popoverIsHovered = popoverIsHovered;
|
||||
this.updateView();
|
||||
};
|
||||
|
||||
export default SmallVideo;
|
||||
|
|
|
@ -570,8 +570,7 @@ var VideoLayout = {
|
|||
? ParticipantConnectionStatus.INTERRUPTED
|
||||
: ParticipantConnectionStatus.ACTIVE;
|
||||
|
||||
localVideoThumbnail
|
||||
.connectionIndicator.updateConnectionStatusIndicator(status);
|
||||
localVideoThumbnail.updateConnectionStatus(status);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -779,7 +778,7 @@ var VideoLayout = {
|
|||
// Why library internal objects are passed as event's args ?
|
||||
object.resolution = resolution[APP.conference.getMyUserId()];
|
||||
object.framerate = framerate[APP.conference.getMyUserId()];
|
||||
localVideoThumbnail.updateStatsIndicator(percent, object);
|
||||
localVideoThumbnail.updateConnectionStats(percent, object);
|
||||
|
||||
Object.keys(resolution).forEach(function (id) {
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
|
@ -817,7 +816,7 @@ var VideoLayout = {
|
|||
updateConnectionStats (id, percent, object) {
|
||||
let remoteVideo = remoteVideos[id];
|
||||
if (remoteVideo) {
|
||||
remoteVideo.updateStatsIndicator(percent, object);
|
||||
remoteVideo.updateConnectionStats(percent, object);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -828,7 +827,7 @@ var VideoLayout = {
|
|||
hideConnectionIndicator (id) {
|
||||
let remoteVideo = remoteVideos[id];
|
||||
if (remoteVideo)
|
||||
remoteVideo.hideConnectionIndicator();
|
||||
remoteVideo.removeConnectionIndicator();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -894,19 +893,6 @@ var VideoLayout = {
|
|||
}
|
||||
},
|
||||
|
||||
showMore (id) {
|
||||
if (id === 'local') {
|
||||
localVideoThumbnail.connectionIndicator.showMore();
|
||||
} else {
|
||||
let remoteVideo = remoteVideos[id];
|
||||
if (remoteVideo) {
|
||||
remoteVideo.connectionIndicator.showMore();
|
||||
} else {
|
||||
logger.info("Error - no remote video for id: " + id);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resizes the video area.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import JitsiPopover from '../../../../modules/UI/util/JitsiPopover';
|
||||
|
||||
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
|
||||
import { ConnectionStatsTable } from '../../connection-stats';
|
||||
|
||||
declare var $: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
// Converts the percent for connection quality into a string recognized for CSS.
|
||||
const QUALITY_TO_WIDTH = [
|
||||
|
||||
// Full (5 bars)
|
||||
{
|
||||
percent: 80,
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
// 4 bars
|
||||
{
|
||||
percent: 60,
|
||||
width: '80%'
|
||||
},
|
||||
|
||||
// 3 bars
|
||||
{
|
||||
percent: 40,
|
||||
width: '55%'
|
||||
},
|
||||
|
||||
// 2 bars
|
||||
{
|
||||
percent: 20,
|
||||
width: '40%'
|
||||
},
|
||||
|
||||
// 1 bar
|
||||
{
|
||||
percent: 0,
|
||||
width: '20%'
|
||||
}
|
||||
|
||||
// Note: we never show 0 bars.
|
||||
];
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which displays the current connection
|
||||
* quality percentage and has a popover to show more detailed connection stats.
|
||||
*
|
||||
* @extends {Component}
|
||||
*/
|
||||
class ConnectionIndicator extends Component {
|
||||
/**
|
||||
* {@code ConnectionIndicator} component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* Whether or not the displays stats are for local video.
|
||||
*/
|
||||
isLocalVideo: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The callback to invoke when the hover state over the popover changes.
|
||||
*/
|
||||
onHover: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Whether or not the popover should display a link that can toggle
|
||||
* a more detailed view of the stats.
|
||||
*/
|
||||
showMoreLink: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* An object that contains statistics related to connection quality.
|
||||
*
|
||||
* {
|
||||
* bandwidth: Object,
|
||||
* bitrate: Object,
|
||||
* connectionStatus: String,
|
||||
* framerate: Object,
|
||||
* packetLoss: Object,
|
||||
* percent: Number,
|
||||
* resolution: Object,
|
||||
* transport: Array
|
||||
* }
|
||||
*/
|
||||
stats: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: React.PropTypes.func
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new {@code ConnectionIndicator} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
/**
|
||||
* The internal reference to topmost DOM/HTML element backing the React
|
||||
* {@code Component}. Accessed directly for associating an element as
|
||||
* the trigger for a popover.
|
||||
*
|
||||
* @private
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
this._rootElement = null;
|
||||
|
||||
this.state = {
|
||||
/**
|
||||
* Whether or not the popover content should display additional
|
||||
* statistics.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
showMoreStats: false
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onToggleShowMore = this._onToggleShowMore.bind(this);
|
||||
this._setRootElement = this._setRootElement.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a popover instance to display when the component is hovered.
|
||||
*
|
||||
* @inheritdoc
|
||||
* returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.popover = new JitsiPopover($(this._rootElement), {
|
||||
content: this._renderStatisticsTable(),
|
||||
skin: 'black',
|
||||
position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
|
||||
});
|
||||
|
||||
this.popover.addOnHoverPopover(this.props.onHover);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the contents of the popover. This is done manually because the
|
||||
* popover is not a React Component yet and so is not automatiucally aware
|
||||
* of changed data.
|
||||
*
|
||||
* @inheritdoc
|
||||
* returns {void}
|
||||
*/
|
||||
componentDidUpdate() {
|
||||
this.popover.updateContent(this._renderStatisticsTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up any popover instance that is linked to the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
* returns {void}
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
this.popover.forceHide();
|
||||
this.popover.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className = 'connection-indicator indicator'
|
||||
ref = { this._setRootElement }>
|
||||
<div className = 'connection indicatoricon'>
|
||||
{ this._renderIcon() }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to invoke when the show more link in the popover content is
|
||||
* clicked. Sets the state which will determine if the popover should show
|
||||
* additional statistics about the connection.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleShowMore() {
|
||||
this.setState({ showMoreStats: !this.state.showMoreStats });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ReactElement for displaying an icon that represents the current
|
||||
* connection quality.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderIcon() {
|
||||
switch (this.props.stats.connectionStatus) {
|
||||
case JitsiParticipantConnectionStatus.INTERRUPTED:
|
||||
return (
|
||||
<span className = 'connection_lost'>
|
||||
<i className = 'icon-connection-lost' />
|
||||
</span>
|
||||
);
|
||||
case JitsiParticipantConnectionStatus.INACTIVE:
|
||||
return (
|
||||
<span className = 'connection_ninja'>
|
||||
<i className = 'icon-ninja' />
|
||||
</span>
|
||||
);
|
||||
default: {
|
||||
const { percent } = this.props.stats;
|
||||
const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
|
||||
const iconWidth = width && width.width
|
||||
? { width: width && width.width } : {};
|
||||
|
||||
return [
|
||||
<span
|
||||
className = 'connection_empty'
|
||||
key = 'icon-empty'>
|
||||
<i className = 'icon-connection' />
|
||||
</span>,
|
||||
<span
|
||||
className = 'connection_full'
|
||||
key = 'icon-full'
|
||||
style = { iconWidth }>
|
||||
<i className = 'icon-connection' />
|
||||
</span>
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ConnectionStatisticsTable} instance.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderStatisticsTable() {
|
||||
const {
|
||||
bandwidth,
|
||||
bitrate,
|
||||
framerate,
|
||||
packetLoss,
|
||||
resolution,
|
||||
transport
|
||||
} = this.props.stats;
|
||||
|
||||
return (
|
||||
<ConnectionStatsTable
|
||||
bandwidth = { bandwidth }
|
||||
bitrate = { bitrate }
|
||||
framerate = { framerate }
|
||||
isLocalVideo = { this.props.isLocalVideo }
|
||||
onShowMore = { this._onToggleShowMore }
|
||||
packetLoss = { packetLoss }
|
||||
resolution = { resolution }
|
||||
shouldShowMore = { this.state.showMoreStats }
|
||||
transport = { transport } />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an internal reference to the component's root element.
|
||||
*
|
||||
* @param {Object} element - The highest DOM element in the component.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_setRootElement(element) {
|
||||
this._rootElement = element;
|
||||
}
|
||||
}
|
||||
|
||||
export default ConnectionIndicator;
|
|
@ -0,0 +1 @@
|
|||
export { default as ConnectionIndicator } from './ConnectionIndicator';
|
|
@ -0,0 +1 @@
|
|||
export * from './components';
|
|
@ -34,7 +34,11 @@ export default class Filmstrip extends Component {
|
|||
id = 'localAudio'
|
||||
muted = { true } />
|
||||
<div className = 'videocontainer__toolbar' />
|
||||
<div className = 'videocontainer__toptoolbar' />
|
||||
<div className = 'videocontainer__toptoolbar'>
|
||||
<span
|
||||
className
|
||||
= 'connection-indicator-container' />
|
||||
</div>
|
||||
<div className = 'videocontainer__hoverOverlay' />
|
||||
</span>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue