import React, { Component } from 'react';
import { translate } from '../../base/i18n';
/**
* React {@code Component} for displaying connection statistics.
*
* @extends Component
*/
class ConnectionStatsTable extends Component {
/**
* {@code ConnectionStatsTable} component's property types.
*
* @static
*/
static propTypes = {
/**
* Statistics related to bandwidth.
* {{
* download: Number,
* upload: Number
* }}
*/
bandwidth: React.PropTypes.object,
/**
* Statistics related to bitrate.
* {{
* download: Number,
* upload: Number
* }}
*/
bitrate: React.PropTypes.object,
/**
* Statistics related to framerates for each ssrc.
* {{
* [ ssrc ]: Number
* }}
*/
framerate: React.PropTypes.object,
/**
* Whether or not the statitics are for local video.
*/
isLocalVideo: React.PropTypes.bool,
/**
* Callback to invoke when the show additional stats link is clicked.
*/
onShowMore: React.PropTypes.func,
/**
* Statistics related to packet loss.
* {{
* download: Number,
* upload: Number
* }}
*/
packetLoss: React.PropTypes.object,
/**
* Statistics related to display resolutions for each ssrc.
* {{
* [ ssrc ]: {
* height: Number,
* width: Number
* }
* }}
*/
resolution: React.PropTypes.object,
/**
* Whether or not additional stats about bandwidth and transport should
* be displayed. Will not display even if true for remote participants.
*/
shouldShowMore: React.PropTypes.bool,
/**
* Invoked to obtain translated strings.
*/
t: React.PropTypes.func,
/**
* Statistics related to transports.
*/
transport: React.PropTypes.array
};
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { isLocalVideo } = this.props;
return (
{ this._renderStatistics() }
{ isLocalVideo ? this._renderShowMoreLink() : null }
{ isLocalVideo && this.props.shouldShowMore
? this._renderAdditionalStats() : null }
);
}
/**
* Creates a table as ReactElement that will display additional statistics
* related to bandwidth and transport.
*
* @private
* @returns {ReactElement}
*/
_renderAdditionalStats() {
return (
{ this._renderBandwidth() }
{ this._renderTransport() }
);
}
/**
* Creates a table row as a ReactElement for displaying bandwidth related
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderBandwidth() {
const { download, upload } = this.props.bandwidth || {};
return (
{ this.props.t('connectionindicator.bandwidth') }
|
↓
{ download ? `${download} Kbps` : 'N/A' }
↑
{ upload ? `${upload} Kbps` : 'N/A' }
|
);
}
/**
* Creates a a table row as a ReactElement for displaying bitrate related
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderBitrate() {
const { download, upload } = this.props.bitrate || {};
return (
{ this.props.t('connectionindicator.bitrate') }
|
↓
{ download ? `${download} Kbps` : 'N/A' }
↑
{ upload ? `${upload} Kbps` : 'N/A' }
|
);
}
/**
* Creates a table row as a ReactElement for displaying frame rate related
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderFrameRate() {
const { framerate, t } = this.props;
const frameRateString = Object.keys(framerate || {})
.map(ssrc => framerate[ssrc])
.join(', ') || 'N/A';
return (
{ t('connectionindicator.framerate') }
|
{ frameRateString } |
);
}
/**
* Creates a tables row as a ReactElement for displaying packet loss related
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderPacketLoss() {
const { packetLoss, t } = this.props;
let packetLossTableData;
if (packetLoss) {
const { download, upload } = packetLoss;
// eslint-disable-next-line no-extra-parens
packetLossTableData = (
↓
{ download === null ? 'N/A' : `${download}%` }
↑
{ upload === null ? 'N/A' : `${upload}%` }
|
);
} else {
packetLossTableData = N/A | ;
}
return (
{ t('connectionindicator.packetloss') }
|
{ packetLossTableData }
);
}
/**
* Creates a table row as a ReactElement for displaying resolution related
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderResolution() {
const { resolution, t } = this.props;
const resolutionString = Object.keys(resolution || {})
.map(ssrc => {
const { width, height } = resolution[ssrc];
return `${width}x${height}`;
})
.join(', ') || 'N/A';
return (
{ t('connectionindicator.resolution') }
|
{ resolutionString } |
);
}
/**
* Creates a ReactElement for display a link to toggle showing additional
* statistics.
*
* @private
* @returns {ReactElement}
*/
_renderShowMoreLink() {
const translationKey
= this.props.shouldShowMore
? 'connectionindicator.less'
: 'connectionindicator.more';
return (
{ this.props.t(translationKey) }
);
}
/**
* Creates a table as a ReactElement for displaying connection statistics.
*
* @private
* @returns {ReactElement}
*/
_renderStatistics() {
return (
{ this._renderBitrate() }
{ this._renderPacketLoss() }
{ this._renderResolution() }
{ this._renderFrameRate() }
);
}
/**
* Creates table rows as ReactElements for displaying transport related
* statistics.
*
* @private
* @returns {ReactElement[]}
*/
_renderTransport() {
const { t, transport } = this.props;
if (!transport || transport.length === 0) {
// eslint-disable-next-line no-extra-parens
const NA = (
{ t('connectionindicator.address') }
|
N/A
|
);
return [ NA ];
}
const data = {
localIP: [],
localPort: [],
remoteIP: [],
remotePort: [],
transportType: []
};
for (let i = 0; i < transport.length; i++) {
const ip = getIP(transport[i].ip);
const localIP = getIP(transport[i].localip);
const localPort = getPort(transport[i].localip);
const port = getPort(transport[i].ip);
if (!data.remoteIP.includes(ip)) {
data.remoteIP.push(ip);
}
if (!data.localIP.includes(localIP)) {
data.localIP.push(localIP);
}
if (!data.localPort.includes(localPort)) {
data.localPort.push(localPort);
}
if (!data.remotePort.includes(port)) {
data.remotePort.push(port);
}
if (!data.transportType.includes(transport[i].type)) {
data.transportType.push(transport[i].type);
}
}
// All of the transports should be either P2P or JVB
let isP2P = false, isTURN = false;
if (transport.length) {
isP2P = transport[0].p2p;
isTURN = transport[0].localCandidateType === 'relay'
|| transport[0].remoteCandidateType === 'relay';
}
let additionalData = null;
if (isP2P) {
additionalData = isTURN
? { t('connectionindicator.turn') }
: { t('connectionindicator.peer_to_peer') };
}
// First show remote statistics, then local, and then transport type.
const tableRowConfigurations = [
{
additionalData,
data: data.remoteIP,
key: 'remoteaddress',
label: t('connectionindicator.remoteaddress',
{ count: data.remoteIP.length })
},
{
data: data.remotePort,
key: 'remoteport',
label: t('connectionindicator.remoteport',
{ count: transport.length })
},
{
data: data.localIP,
key: 'localaddress',
label: t('connectionindicator.localaddress',
{ count: data.localIP.length })
},
{
data: data.localPort,
key: 'localport',
label: t('connectionindicator.localport',
{ count: transport.length })
},
{
data: data.transportType,
key: 'transport',
label: t('connectionindicator.transport',
{ count: data.transportType.length })
}
];
return tableRowConfigurations.map(this._renderTransportTableRow);
}
/**
* Creates a table row as a ReactElement for displaying a transport related
* statistic.
*
* @param {Object} config - Describes the contents of the row.
* @param {ReactElement} config.additionalData - Extra data to display next
* to the passed in config.data.
* @param {Array} config.data - The transport statistics to display.
* @param {string} config.key - The ReactElement's key. Must be unique for
* iterating over multiple child rows.
* @param {string} config.label - The text to display describing the data.
* @private
* @returns {ReactElement}
*/
_renderTransportTableRow(config) {
const { additionalData, data, key, label } = config;
return (
{ label }
|
{ getStringFromArray(data) }
{ additionalData || null }
|
);
}
}
/**
* Utility for getting the IP from a transport statistics object's
* representation of an IP.
*
* @param {string} value - The transport's IP to parse.
* @private
* @returns {string}
*/
function getIP(value) {
return value.substring(0, value.lastIndexOf(':'));
}
/**
* Utility for getting the port from a transport statistics object's
* representation of an IP.
*
* @param {string} value - The transport's IP to parse.
* @private
* @returns {string}
*/
function getPort(value) {
return value.substring(value.lastIndexOf(':') + 1, value.length);
}
/**
* Utility for concatenating values in an array into a comma separated string.
*
* @param {Array} array - Transport statistics to concatenate.
* @private
* @returns {string}
*/
function getStringFromArray(array) {
let res = '';
for (let i = 0; i < array.length; i++) {
res += (i === 0 ? '' : ', ') + array[i];
}
return res;
}
export default translate(ConnectionStatsTable);