mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
There was a fundamental change in how e2e pings work in lib-jitsi-meet where it does few pings at the beginning, pushes metrics and shuts the whole thing down, so there are no more e2ertt updates after that. Plus it's disabled by default. @paweldomas have decided with @bgrozev to remove that instead of trying to adjust. Even if e2e pings are enabled the data is not feed correctly to the indicators, what's wrong there is as soon as the pings stop going the new data does not arrive and then all you get is N/A if the UI component is recreated.
966 lines
25 KiB
TypeScript
966 lines
25 KiB
TypeScript
import { withStyles } from '@material-ui/styles';
|
|
import clsx from 'clsx';
|
|
import React, { Component } from 'react';
|
|
import { WithTranslation } from 'react-i18next';
|
|
|
|
import ContextMenu from '../../base/components/context-menu/ContextMenu';
|
|
import { isMobileBrowser } from '../../base/environment/utils';
|
|
import { translate } from '../../base/i18n/functions';
|
|
|
|
type DownloadUpload = {
|
|
download: number;
|
|
upload: number;
|
|
};
|
|
|
|
/**
|
|
* The type of the React {@code Component} props of
|
|
* {@link ConnectionStatsTable}.
|
|
*/
|
|
interface Props extends WithTranslation {
|
|
|
|
/**
|
|
* The audio SSRC of this client.
|
|
*/
|
|
audioSsrc: number;
|
|
|
|
/**
|
|
* Statistics related to bandwidth.
|
|
* {{
|
|
* download: Number,
|
|
* upload: Number
|
|
* }}.
|
|
*/
|
|
bandwidth: DownloadUpload;
|
|
|
|
/**
|
|
* Statistics related to bitrate.
|
|
* {{
|
|
* download: Number,
|
|
* upload: Number
|
|
* }}.
|
|
*/
|
|
bitrate: DownloadUpload;
|
|
|
|
/**
|
|
* The number of bridges (aka media servers) currently used in the
|
|
* conference.
|
|
*/
|
|
bridgeCount: number;
|
|
|
|
/**
|
|
* An object containing the CSS classes.
|
|
*/
|
|
classes: any;
|
|
|
|
/**
|
|
* Audio/video codecs in use for the connection.
|
|
*/
|
|
codec: {
|
|
[key: string]: {
|
|
audio: string;
|
|
video: string;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* A message describing the connection quality.
|
|
*/
|
|
connectionSummary: string;
|
|
|
|
/**
|
|
* Whether or not should display the "Show More" link.
|
|
*/
|
|
disableShowMoreStats: boolean;
|
|
|
|
/**
|
|
* Whether or not should display the "Save Logs" link.
|
|
*/
|
|
enableSaveLogs: boolean;
|
|
|
|
/**
|
|
* Statistics related to frame rates for each ssrc.
|
|
* {{
|
|
* [ ssrc ]: Number
|
|
* }}.
|
|
*/
|
|
framerate: Object;
|
|
|
|
/**
|
|
* Whether or not the statistics are for local video.
|
|
*/
|
|
isLocalVideo: boolean;
|
|
|
|
/**
|
|
* Whether or not the statistics are for screen share.
|
|
*/
|
|
isVirtualScreenshareParticipant: boolean;
|
|
|
|
/**
|
|
* The send-side max enabled resolution (aka the highest layer that is not
|
|
* suspended on the send-side).
|
|
*/
|
|
maxEnabledResolution: number;
|
|
|
|
/**
|
|
* Callback to invoke when the user clicks on the download logs link.
|
|
*/
|
|
onSaveLogs: () => void;
|
|
|
|
/**
|
|
* Callback to invoke when the show additional stats link is clicked.
|
|
*/
|
|
onShowMore: (e?: React.MouseEvent) => void;
|
|
|
|
/**
|
|
* Statistics related to packet loss.
|
|
* {{
|
|
* download: Number,
|
|
* upload: Number
|
|
* }}.
|
|
*/
|
|
packetLoss: DownloadUpload;
|
|
|
|
/**
|
|
* The endpoint id of this client.
|
|
*/
|
|
participantId: string;
|
|
|
|
/**
|
|
* The region that we think the client is in.
|
|
*/
|
|
region: string;
|
|
|
|
/**
|
|
* Statistics related to display resolutions for each ssrc.
|
|
* {{
|
|
* [ ssrc ]: {
|
|
* height: Number,
|
|
* width: Number
|
|
* }
|
|
* }}.
|
|
*/
|
|
resolution: {
|
|
[ssrc: string]: {
|
|
height: number;
|
|
width: number;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* The region of the media server that we are connected to.
|
|
*/
|
|
serverRegion: string;
|
|
|
|
/**
|
|
* Whether or not additional stats about bandwidth and transport should be
|
|
* displayed. Will not display even if true for remote participants.
|
|
*/
|
|
shouldShowMore: boolean;
|
|
|
|
/**
|
|
* Whether source name signaling is enabled.
|
|
*/
|
|
sourceNameSignalingEnabled: boolean;
|
|
|
|
/**
|
|
* Statistics related to transports.
|
|
*/
|
|
transport: Array<{
|
|
ip: string;
|
|
localCandidateType: string;
|
|
localip: string;
|
|
p2p: boolean;
|
|
remoteCandidateType: string;
|
|
transportType: string;
|
|
type: string;
|
|
}>;
|
|
|
|
/**
|
|
* The video SSRC of this client.
|
|
*/
|
|
videoSsrc: number;
|
|
}
|
|
|
|
/**
|
|
* Click handler.
|
|
*
|
|
* @param {SyntheticEvent} event - The click event.
|
|
* @returns {void}
|
|
*/
|
|
function onClick(event: React.MouseEvent) {
|
|
// If the event is propagated to the thumbnail container the participant will be pinned. That's why the propagation
|
|
// needs to be stopped.
|
|
event.stopPropagation();
|
|
}
|
|
|
|
const styles = (theme: any) => {
|
|
return {
|
|
actions: {
|
|
margin: '10px auto',
|
|
textAlign: 'center'
|
|
},
|
|
connectionStatsTable: {
|
|
'&, & > table': {
|
|
fontSize: '12px',
|
|
fontWeight: '400',
|
|
|
|
'& td': {
|
|
padding: '2px 0'
|
|
}
|
|
},
|
|
'& > table': {
|
|
whiteSpace: 'nowrap'
|
|
},
|
|
|
|
'& td:nth-child(n-1)': {
|
|
paddingLeft: '5px'
|
|
},
|
|
|
|
'& $upload, & $download': {
|
|
marginRight: '2px'
|
|
}
|
|
},
|
|
contextMenu: {
|
|
position: 'relative',
|
|
marginTop: 0,
|
|
right: 'auto',
|
|
padding: `${theme.spacing(2)}px ${theme.spacing(1)}px`,
|
|
marginLeft: `${theme.spacing(1)}px`,
|
|
marginRight: `${theme.spacing(1)}px`,
|
|
marginBottom: `${theme.spacing(1)}px`
|
|
},
|
|
download: {},
|
|
mobile: {
|
|
margin: `${theme.spacing(3)}px`
|
|
},
|
|
status: {
|
|
fontWeight: 'bold'
|
|
},
|
|
upload: {}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* React {@code Component} for displaying connection statistics.
|
|
*
|
|
* @augments Component
|
|
*/
|
|
class ConnectionStatsTable extends Component<Props> {
|
|
/**
|
|
* Implements React's {@link Component#render()}.
|
|
*
|
|
* @inheritdoc
|
|
* @returns {ReactElement}
|
|
*/
|
|
render() {
|
|
const {
|
|
classes,
|
|
disableShowMoreStats,
|
|
enableSaveLogs,
|
|
isVirtualScreenshareParticipant,
|
|
isLocalVideo
|
|
} = this.props;
|
|
const className = clsx(classes.connectionStatsTable, { [classes.mobile]: isMobileBrowser() });
|
|
|
|
if (isVirtualScreenshareParticipant) {
|
|
return this._renderScreenShareStatus();
|
|
}
|
|
|
|
return (
|
|
<ContextMenu
|
|
className = { classes.contextMenu }
|
|
hidden = { false }
|
|
inDrawer = { true }>
|
|
<div
|
|
className = { className }
|
|
onClick = { onClick }>
|
|
{ this._renderStatistics() }
|
|
<div className = { classes.actions }>
|
|
{ isLocalVideo && enableSaveLogs ? this._renderSaveLogs() : null}
|
|
{ !disableShowMoreStats && this._renderShowMoreLink() }
|
|
</div>
|
|
{ this.props.shouldShowMore ? this._renderAdditionalStats() : null }
|
|
</div>
|
|
</ContextMenu>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a ReactElement that will display connection statistics for a screen share thumbnail.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderScreenShareStatus() {
|
|
const { classes } = this.props;
|
|
const className = clsx(classes.connectionStatsTable, { [classes.mobile]: isMobileBrowser() });
|
|
|
|
return (<ContextMenu
|
|
className = { classes.contextMenu }
|
|
hidden = { false }
|
|
inDrawer = { true }>
|
|
<div
|
|
className = { className }
|
|
onClick = { onClick }>
|
|
<tbody>
|
|
{ this._renderResolution() }
|
|
{ this._renderFrameRate() }
|
|
</tbody>
|
|
</div>
|
|
</ContextMenu>);
|
|
}
|
|
|
|
/**
|
|
* Creates a table as ReactElement that will display additional statistics
|
|
* related to bandwidth and transport for the local user.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderAdditionalStats() {
|
|
const { isLocalVideo } = this.props;
|
|
|
|
return (
|
|
<table>
|
|
<tbody>
|
|
{ isLocalVideo ? this._renderBandwidth() : null }
|
|
{ isLocalVideo ? this._renderTransport() : null }
|
|
{ isLocalVideo ? this._renderRegion() : null }
|
|
{ this._renderAudioSsrc() }
|
|
{ this._renderVideoSsrc() }
|
|
{ this._renderParticipantId() }
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying bandwidth related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderBandwidth() {
|
|
const { classes } = this.props;
|
|
const { download, upload } = this.props.bandwidth || {};
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
{ this.props.t('connectionindicator.bandwidth') }
|
|
</td>
|
|
<td>
|
|
<span className = { classes.download }>
|
|
↓
|
|
</span>
|
|
{ download ? `${download} Kbps` : 'N/A' }
|
|
<span className = { classes.upload }>
|
|
↑
|
|
</span>
|
|
{ upload ? `${upload} Kbps` : 'N/A' }
|
|
</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a a table row as a ReactElement for displaying bitrate related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderBitrate() {
|
|
const { classes } = this.props;
|
|
const { download, upload } = this.props.bitrate || {};
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>
|
|
{ this.props.t('connectionindicator.bitrate') }
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span className = { classes.download }>
|
|
↓
|
|
</span>
|
|
{ download ? `${download} Kbps` : 'N/A' }
|
|
<span className = { classes.upload }>
|
|
↑
|
|
</span>
|
|
{ upload ? `${upload} Kbps` : 'N/A' }
|
|
</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying the audio ssrc.
|
|
* This will typically be something like "Audio SSRC: 12345".
|
|
*
|
|
* @returns {JSX.Element}
|
|
* @private
|
|
*/
|
|
_renderAudioSsrc() {
|
|
const { audioSsrc, t } = this.props;
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.audio_ssrc') }</span>
|
|
</td>
|
|
<td>{ audioSsrc || 'N/A' }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying the video ssrc.
|
|
* This will typically be something like "Video SSRC: 12345".
|
|
*
|
|
* @returns {JSX.Element}
|
|
* @private
|
|
*/
|
|
_renderVideoSsrc() {
|
|
const { videoSsrc, t } = this.props;
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.video_ssrc') }</span>
|
|
</td>
|
|
<td>{ videoSsrc || 'N/A' }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying the endpoint id.
|
|
* This will typically be something like "Endpoint id: 1e8fbg".
|
|
*
|
|
* @returns {JSX.Element}
|
|
* @private
|
|
*/
|
|
_renderParticipantId() {
|
|
const { participantId, t } = this.props;
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.participant_id') }</span>
|
|
</td>
|
|
<td>{ participantId || 'N/A' }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a a table row as a ReactElement for displaying codec, if present.
|
|
* This will typically be something like "Codecs (A/V): Opus, vp8".
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderCodecs() {
|
|
const { codec, t, sourceNameSignalingEnabled } = this.props;
|
|
|
|
if (!codec) {
|
|
return;
|
|
}
|
|
|
|
let codecString;
|
|
|
|
if (sourceNameSignalingEnabled) {
|
|
codecString = `${codec.audio}, ${codec.video}`;
|
|
} else {
|
|
// Only report one codec, in case there are multiple for a user.
|
|
Object.keys(codec || {})
|
|
.forEach(ssrc => {
|
|
const { audio, video } = codec[ssrc];
|
|
|
|
codecString = `${audio}, ${video}`;
|
|
});
|
|
}
|
|
|
|
if (!codecString) {
|
|
codecString = 'N/A';
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.codecs') }</span>
|
|
</td>
|
|
<td>{ codecString }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying a summary message
|
|
* about the current connection status.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderConnectionSummary() {
|
|
const { classes } = this.props;
|
|
|
|
return (
|
|
<tr className = { classes.status }>
|
|
<td>
|
|
<span>{ this.props.t('connectionindicator.status') }</span>
|
|
</td>
|
|
<td>{ this.props.connectionSummary }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying the "connected to"
|
|
* information.
|
|
*
|
|
* @returns {ReactElement}
|
|
* @private
|
|
*/
|
|
_renderRegion() {
|
|
const { region, serverRegion, t } = this.props;
|
|
let str = serverRegion;
|
|
|
|
if (!serverRegion) {
|
|
return;
|
|
}
|
|
|
|
|
|
if (region && serverRegion && region !== serverRegion) {
|
|
str += ` from ${region}`;
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.connectedTo') }</span>
|
|
</td>
|
|
<td>{ str }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying the "bridge count"
|
|
* information.
|
|
*
|
|
* @returns {*}
|
|
* @private
|
|
*/
|
|
_renderBridgeCount() {
|
|
const { bridgeCount, t } = this.props;
|
|
|
|
// 0 is valid, but undefined/null/NaN aren't.
|
|
if (!bridgeCount && bridgeCount !== 0) {
|
|
return;
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.bridgeCount') }</span>
|
|
</td>
|
|
<td>{ bridgeCount }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying frame rate related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderFrameRate() {
|
|
const { framerate, t, sourceNameSignalingEnabled } = this.props;
|
|
|
|
let frameRateString;
|
|
|
|
if (sourceNameSignalingEnabled) {
|
|
frameRateString = framerate || 'N/A';
|
|
} else {
|
|
frameRateString = Object.keys(framerate || {})
|
|
.map(ssrc => framerate[ssrc as keyof typeof framerate])
|
|
.join(', ') || 'N/A';
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.framerate') }</span>
|
|
</td>
|
|
<td>{ frameRateString }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a tables row as a ReactElement for displaying packet loss related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderPacketLoss() {
|
|
const { classes, packetLoss, t } = this.props;
|
|
let packetLossTableData;
|
|
|
|
if (packetLoss) {
|
|
const { download, upload } = packetLoss;
|
|
|
|
packetLossTableData = (
|
|
<td>
|
|
<span className = { classes.download }>
|
|
↓
|
|
</span>
|
|
{ download === null ? 'N/A' : `${download}%` }
|
|
<span className = { classes.upload }>
|
|
↑
|
|
</span>
|
|
{ upload === null ? 'N/A' : `${upload}%` }
|
|
</td>
|
|
);
|
|
} else {
|
|
packetLossTableData = <td>N/A</td>;
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>
|
|
{ t('connectionindicator.packetloss') }
|
|
</span>
|
|
</td>
|
|
{ packetLossTableData }
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table row as a ReactElement for displaying resolution related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderResolution() {
|
|
const { resolution, maxEnabledResolution, t, sourceNameSignalingEnabled } = this.props;
|
|
let resolutionString;
|
|
|
|
if (sourceNameSignalingEnabled) {
|
|
resolutionString = resolution ? `${resolution.width}x${resolution.height}` : 'N/A';
|
|
} else {
|
|
resolutionString = Object.keys(resolution || {})
|
|
.map(ssrc => {
|
|
const { width, height } = resolution[ssrc];
|
|
|
|
return `${width}x${height}`;
|
|
})
|
|
.join(', ') || 'N/A';
|
|
}
|
|
|
|
if (maxEnabledResolution && maxEnabledResolution < 720) {
|
|
const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');
|
|
|
|
resolutionString += ` (${maxEnabledResolutionTitle} ${maxEnabledResolution}p)`;
|
|
}
|
|
|
|
return (
|
|
<tr>
|
|
<td>
|
|
<span>{ t('connectionindicator.resolution') }</span>
|
|
</td>
|
|
<td>{ resolutionString }</td>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a ReactElement for display a link to save the logs.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderSaveLogs() {
|
|
return (
|
|
<span>
|
|
<a
|
|
className = 'savelogs link'
|
|
onClick = { this.props.onSaveLogs }
|
|
role = 'button'
|
|
tabIndex = { 0 }>
|
|
{ this.props.t('connectionindicator.savelogs') }
|
|
</a>
|
|
<span> | </span>
|
|
</span>
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 (
|
|
<a
|
|
className = 'showmore link'
|
|
onClick = { this.props.onShowMore }
|
|
role = 'button'
|
|
tabIndex = { 0 }>
|
|
{ this.props.t(translationKey) }
|
|
</a>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a table as a ReactElement for displaying connection statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement}
|
|
*/
|
|
_renderStatistics() {
|
|
const isRemoteVideo = !this.props.isLocalVideo;
|
|
|
|
return (
|
|
<table>
|
|
<tbody>
|
|
{ this._renderConnectionSummary() }
|
|
{ this._renderBitrate() }
|
|
{ this._renderPacketLoss() }
|
|
{ isRemoteVideo ? this._renderRegion() : null }
|
|
{ this._renderResolution() }
|
|
{ this._renderFrameRate() }
|
|
{ this._renderCodecs() }
|
|
{ isRemoteVideo ? null : this._renderBridgeCount() }
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates table rows as ReactElements for displaying transport related
|
|
* statistics.
|
|
*
|
|
* @private
|
|
* @returns {ReactElement[]}
|
|
*/
|
|
_renderTransport() {
|
|
const { t, transport } = this.props;
|
|
|
|
if (!transport || transport.length === 0) {
|
|
const NA = (
|
|
<tr key = 'address'>
|
|
<td>
|
|
<span>{ t('connectionindicator.address') }</span>
|
|
</td>
|
|
<td>
|
|
N/A
|
|
</td>
|
|
</tr>
|
|
);
|
|
|
|
return [ NA ];
|
|
}
|
|
|
|
const data: {
|
|
localIP: string[];
|
|
localPort: string[];
|
|
remoteIP: string[];
|
|
remotePort: string[];
|
|
transportType: string[];
|
|
} = {
|
|
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';
|
|
}
|
|
|
|
const additionalData = [];
|
|
|
|
if (isP2P) {
|
|
additionalData.push(
|
|
<span> (p2p)</span>);
|
|
}
|
|
if (isTURN) {
|
|
additionalData.push(<span> (turn)</span>);
|
|
}
|
|
|
|
// 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: any) {
|
|
const { additionalData, data, key, label } = config;
|
|
|
|
return (
|
|
<tr key = { key }>
|
|
<td>
|
|
<span>
|
|
{ label }
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{ getStringFromArray(data) }
|
|
{ additionalData || null }
|
|
</td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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: string) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
|
|
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: string) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
|
|
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: string[]) {
|
|
let res = '';
|
|
|
|
for (let i = 0; i < array.length; i++) {
|
|
res += (i === 0 ? '' : ', ') + array[i];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// @ts-ignore
|
|
export default translate(withStyles(styles)(ConnectionStatsTable));
|