fix(rn,connection-indicator) align rendering with web
Use the same way for calculating que perceived quality and display it.
This commit is contained in:
parent
48efe36cdf
commit
14fcd153e5
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* IMPORTANT: this file is deprecated. All of these colors should be moved to
|
||||
* the theme instead.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The application's definition of the default color black.
|
||||
*/
|
||||
|
@ -18,29 +23,12 @@ export const ColorPalette = {
|
|||
* the sake of consistency.
|
||||
*/
|
||||
black: BLACK,
|
||||
blackBlue: 'rgb(0, 3, 6)',
|
||||
blue: '#17A0DB',
|
||||
blueHighlight: '#1081b2',
|
||||
buttonUnderlay: '#495258',
|
||||
darkGrey: '#555555',
|
||||
darkBackground: 'rgb(19,21,25)',
|
||||
green: '#40b183',
|
||||
lightGrey: '#AAAAAA',
|
||||
overflowMenuItemUnderlay: '#EEEEEE',
|
||||
red: '#D00000',
|
||||
transparent: 'rgba(0, 0, 0, 0)',
|
||||
toggled: 'rgba(255,255,255,.15)',
|
||||
warning: 'rgb(215, 121, 118)',
|
||||
white: '#FFFFFF',
|
||||
|
||||
/**
|
||||
* These are colors from the atlaskit to be used on mobile, when needed.
|
||||
*
|
||||
* FIXME: Maybe a better solution would be good, or a native packaging of
|
||||
* the respective atlaskit components.
|
||||
*/
|
||||
G400: '#00875A', // Slime
|
||||
N500: '#42526E', // McFanning
|
||||
R400: '#DE350B', // Red dirt
|
||||
Y200: '#FFC400' // Pub mix
|
||||
white: '#FFFFFF'
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
import { statsEmitter } from '../../../connection-indicator';
|
||||
import statsEmitter from '../../../connection-indicator/statsEmitter';
|
||||
import { getLocalParticipant } from '../../participants';
|
||||
import { connect } from '../../redux';
|
||||
import { isTestModeEnabled } from '../functions';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { StyleProp, Text, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
@ -57,7 +58,9 @@ const TitleBar = (props: IProps): JSX.Element => {
|
|||
<VideoQualityLabel />
|
||||
</View>
|
||||
<ConnectionIndicator
|
||||
// @ts-ignore
|
||||
iconStyle = { styles.connectionIndicatorIcon }
|
||||
// @ts-ignore
|
||||
participantId = { localParticipantId } />
|
||||
<View style = { styles.headerLabels as StyleProp<ViewStyle> }>
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
|
|
|
@ -209,7 +209,7 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
|||
*/
|
||||
export function mapStateToProps(state: Object) {
|
||||
return {
|
||||
_autoHideTimeout: state['features/base/config'].connectionIndicators.autoHideTimeout ?? defaultAutoHideTimeout
|
||||
_autoHideTimeout: state['features/base/config'].connectionIndicators?.autoHideTimeout ?? defaultAutoHideTimeout
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export * from './native';
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export * from './web';
|
|
@ -1,68 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { IconConnection } from '../../../base/icons';
|
||||
import { BaseIndicator } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import indicatorStyles from '../../../filmstrip/components/native/styles';
|
||||
import AbstractConnectionIndicator, {
|
||||
type Props,
|
||||
type State
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
import { CONNECTOR_INDICATOR_COLORS, iconStyle } from './styles';
|
||||
|
||||
/**
|
||||
* Implements an indicator to show the quality of the connection of a participant.
|
||||
*/
|
||||
class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
||||
/**
|
||||
* Initializes a new {@code ConnectionIndicator} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
autoHideTimeout: undefined,
|
||||
showIndicator: false,
|
||||
stats: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { showIndicator, stats } = this.state;
|
||||
const { percent } = stats;
|
||||
|
||||
if (!showIndicator || typeof percent === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Signal level on a scale 0..2
|
||||
const signalLevel = Math.floor(percent / 33.4);
|
||||
|
||||
return (
|
||||
<View
|
||||
style = {{
|
||||
...indicatorStyles.indicatorContainer,
|
||||
backgroundColor: CONNECTOR_INDICATOR_COLORS[signalLevel]
|
||||
}}>
|
||||
<BaseIndicator
|
||||
icon = { IconConnection }
|
||||
iconStyle = { this.props.iconStyle || iconStyle } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect()(ConnectionIndicator);
|
|
@ -0,0 +1,216 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { IconConnection } from '../../../base/icons/svg';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantById,
|
||||
isScreenShareParticipant
|
||||
} from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant
|
||||
} from '../../../base/tracks/functions.native';
|
||||
// @ts-ignore
|
||||
import indicatorStyles from '../../../filmstrip/components/native/styles';
|
||||
import {
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../functions';
|
||||
import AbstractConnectionIndicator, {
|
||||
type Props as AbstractProps,
|
||||
mapStateToProps as _abstractMapStateToProps
|
||||
// @ts-ignore
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
import {
|
||||
CONNECTOR_INDICATOR_COLORS,
|
||||
CONNECTOR_INDICATOR_LOST,
|
||||
CONNECTOR_INDICATOR_OTHER,
|
||||
iconStyle
|
||||
} from './styles';
|
||||
|
||||
type IProps = AbstractProps & {
|
||||
|
||||
/**
|
||||
* Whether connection indicators are disabled or not.
|
||||
*/
|
||||
_connectionIndicatorDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the inactive connection indicator is disabled or not.
|
||||
*/
|
||||
_connectionIndicatorInactiveDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection is inactive or not.
|
||||
*/
|
||||
_isConnectionStatusInactive: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection is interrupted or not.
|
||||
*/
|
||||
_isConnectionStatusInterrupted: boolean;
|
||||
|
||||
/**
|
||||
* Whether the current participant is a virtual screenshare.
|
||||
*/
|
||||
_isVirtualScreenshareParticipant: boolean;
|
||||
|
||||
/**
|
||||
* Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* Icon style override.
|
||||
*/
|
||||
iconStyle: any;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
autoHideTimeout: number | undefined;
|
||||
showIndicator: boolean;
|
||||
stats: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements an indicator to show the quality of the connection of a participant.
|
||||
*/
|
||||
class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new {@code ConnectionIndicator} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// @ts-ignore
|
||||
this.state = {
|
||||
autoHideTimeout: undefined,
|
||||
showIndicator: false,
|
||||
stats: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon configuration from CONNECTOR_INDICATOR_COLORS which has a percentage
|
||||
* that matches or exceeds the passed in percentage. The implementation
|
||||
* assumes CONNECTOR_INDICATOR_COLORS is already sorted by highest to lowest
|
||||
* percentage.
|
||||
*
|
||||
* @param {number} percent - The connection percentage, out of 100, to find
|
||||
* the closest matching configuration for.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDisplayConfiguration(percent: number): any {
|
||||
return CONNECTOR_INDICATOR_COLORS.find(x => percent >= x.percent) || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_connectionIndicatorInactiveDisabled,
|
||||
_connectionIndicatorDisabled,
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
// @ts-ignore
|
||||
} = this.props;
|
||||
const {
|
||||
showIndicator,
|
||||
stats
|
||||
// @ts-ignore
|
||||
} = this.state;
|
||||
const { percent } = stats;
|
||||
|
||||
if (!showIndicator || typeof percent === 'undefined'
|
||||
|| _connectionIndicatorDisabled || _isVirtualScreenshareParticipant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let indicatorColor;
|
||||
|
||||
if (_isConnectionStatusInactive) {
|
||||
if (_connectionIndicatorInactiveDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
indicatorColor = CONNECTOR_INDICATOR_OTHER;
|
||||
} else if (_isConnectionStatusInterrupted) {
|
||||
indicatorColor = CONNECTOR_INDICATOR_LOST;
|
||||
} else {
|
||||
const displayConfig = this._getDisplayConfiguration(percent);
|
||||
|
||||
if (!displayConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
indicatorColor = displayConfig.color;
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style = {{
|
||||
...indicatorStyles.indicatorContainer,
|
||||
backgroundColor: indicatorColor
|
||||
}}>
|
||||
<BaseIndicator
|
||||
icon = { IconConnection }
|
||||
// @ts-ignore
|
||||
iconStyle = { this.props.iconStyle || iconStyle } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {IProps} ownProps - The own props of the component.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const { participantId } = ownProps;
|
||||
const tracks = state['features/base/tracks'];
|
||||
const participant = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
const _isVirtualScreenshareParticipant = isScreenShareParticipant(participant);
|
||||
let _isConnectionStatusInactive;
|
||||
let _isConnectionStatusInterrupted;
|
||||
|
||||
if (!_isVirtualScreenshareParticipant) {
|
||||
const _videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
|
||||
_isConnectionStatusInactive = isTrackStreamingStatusInactive(_videoTrack);
|
||||
_isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
}
|
||||
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_connectionIndicatorDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.disabled),
|
||||
_isVirtualScreenshareParticipant,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default connect(_mapStateToProps)(ConnectionIndicator);
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export { default as ConnectionIndicator } from './ConnectionIndicator';
|
|
@ -1,13 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../../base/styles';
|
||||
|
||||
export const CONNECTOR_INDICATOR_COLORS = [
|
||||
ColorPalette.red,
|
||||
ColorPalette.Y200,
|
||||
ColorPalette.green
|
||||
];
|
||||
|
||||
export const iconStyle = {
|
||||
fontSize: 14
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
// @ts-ignore
|
||||
import { INDICATOR_DISPLAY_THRESHOLD } from '../AbstractConnectionIndicator';
|
||||
|
||||
export const CONNECTOR_INDICATOR_LOST = BaseTheme.palette.ui05;
|
||||
export const CONNECTOR_INDICATOR_OTHER = BaseTheme.palette.action01;
|
||||
export const CONNECTOR_INDICATOR_COLORS = [
|
||||
|
||||
// Full (3 bars)
|
||||
{
|
||||
color: BaseTheme.palette.success01,
|
||||
percent: INDICATOR_DISPLAY_THRESHOLD
|
||||
},
|
||||
|
||||
// 2 bars.
|
||||
{
|
||||
color: BaseTheme.palette.warning01,
|
||||
percent: 10
|
||||
},
|
||||
|
||||
// 1 bar.
|
||||
{
|
||||
color: BaseTheme.palette.iconError,
|
||||
percent: 0
|
||||
}
|
||||
];
|
||||
|
||||
export const iconStyle = {
|
||||
fontSize: 14
|
||||
};
|
|
@ -27,7 +27,8 @@ import {
|
|||
import AbstractConnectionIndicator, {
|
||||
type Props as AbstractProps,
|
||||
type State as AbstractState,
|
||||
INDICATOR_DISPLAY_THRESHOLD
|
||||
INDICATOR_DISPLAY_THRESHOLD,
|
||||
mapStateToProps as _abstractMapStateToProps
|
||||
// @ts-ignore
|
||||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
|
@ -386,6 +387,7 @@ export function _mapStateToProps(state: IReduxState, ownProps: Props) {
|
|||
const _isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_isVirtualScreenshareParticipant: isScreenShareParticipant(participant),
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export * from './components';
|
||||
|
||||
export { default as statsEmitter } from './statsEmitter';
|
|
@ -25,7 +25,7 @@ import {
|
|||
getVideoTrackByParticipant,
|
||||
trackStreamingStatusChanged
|
||||
} from '../../../base/tracks';
|
||||
import { ConnectionIndicator } from '../../../connection-indicator';
|
||||
import ConnectionIndicator from '../../../connection-indicator/components/native/ConnectionIndicator';
|
||||
import { DisplayNameLabel } from '../../../display-name';
|
||||
import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions';
|
||||
import {
|
||||
|
@ -191,6 +191,8 @@ class Thumbnail extends PureComponent<Props> {
|
|||
dispatch(showSharedVideoMenu(_participantId));
|
||||
}
|
||||
|
||||
// TODO: add support for getting info about the virtual screen shares.
|
||||
|
||||
if (!_fakeParticipant) {
|
||||
dispatch(showContextMenuDetails(_participantId, _local));
|
||||
}
|
||||
|
|
|
@ -1,17 +1,34 @@
|
|||
import React, { Component } from 'react';
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { withTheme } from 'react-native-paper';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { BottomSheet, hideSheet } from '../../../base/dialog';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
// @ts-ignore
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
// @ts-ignore
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
// @ts-ignore
|
||||
import { bottomSheetStyles } from '../../../base/dialog/components/native/styles';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons';
|
||||
import { getParticipantDisplayName } from '../../../base/participants';
|
||||
import { BaseIndicator } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons/svg';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import { getParticipantDisplayName } from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant
|
||||
} from '../../../base/tracks/functions.native';
|
||||
import {
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../../connection-indicator/functions';
|
||||
import statsEmitter from '../../../connection-indicator/statsEmitter';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
|
@ -20,62 +37,87 @@ import styles from './styles';
|
|||
const AVATAR_SIZE = 25;
|
||||
|
||||
const CONNECTION_QUALITY = [
|
||||
'Low',
|
||||
'Medium',
|
||||
'Good'
|
||||
|
||||
// Full (3 bars)
|
||||
{
|
||||
msg: 'connectionindicator.quality.good',
|
||||
percent: 30 // INDICATOR_DISPLAY_THRESHOLD
|
||||
},
|
||||
|
||||
// 2 bars.
|
||||
{
|
||||
msg: 'connectionindicator.quality.nonoptimal',
|
||||
percent: 10
|
||||
},
|
||||
|
||||
// 1 bar.
|
||||
{
|
||||
msg: 'connectionindicator.quality.poor',
|
||||
percent: 0
|
||||
}
|
||||
];
|
||||
|
||||
export type Props = {
|
||||
type IProps = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
* Whether this participant's connection is inactive.
|
||||
*/
|
||||
dispatch: Function,
|
||||
_isConnectionStatusInactive: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
* Whether this participant's connection is interrupted.
|
||||
*/
|
||||
participantID: string,
|
||||
_isConnectionStatusInterrupted: boolean;
|
||||
|
||||
/**
|
||||
* True if the menu is currently open, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
_isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
_participantDisplayName: string;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
*/
|
||||
participantID: string;
|
||||
|
||||
/**
|
||||
* The function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function,
|
||||
t: Function;
|
||||
|
||||
/**
|
||||
* Theme used for styles.
|
||||
*/
|
||||
theme: Object
|
||||
}
|
||||
theme: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link ConnectionStatusComponent}.
|
||||
*/
|
||||
type State = {
|
||||
resolutionString: string,
|
||||
downloadString: string,
|
||||
uploadString: string,
|
||||
packetLostDownloadString: string,
|
||||
packetLostUploadString: string,
|
||||
serverRegionString: string,
|
||||
codecString: string,
|
||||
connectionString: string
|
||||
type IState = {
|
||||
codecString: string;
|
||||
connectionString: string;
|
||||
downloadString: string;
|
||||
packetLostDownloadString: string;
|
||||
packetLostUploadString: string;
|
||||
resolutionString: string;
|
||||
serverRegionString: string;
|
||||
uploadString: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that show the connection statistics.
|
||||
*/
|
||||
class ConnectionStatusComponent extends Component<Props, State> {
|
||||
class ConnectionStatusComponent extends PureComponent<IProps, IState> {
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
|
@ -85,7 +127,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onStatsUpdated = this._onStatsUpdated.bind(this);
|
||||
|
@ -121,15 +163,15 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
<View style = { styles.statsWrapper }>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.status')} ` }
|
||||
{ t('connectionindicator.status') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.connectionString }
|
||||
{ t(this.state.connectionString) }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.bitrate')}` }
|
||||
{ t('connectionindicator.bitrate') }
|
||||
</Text>
|
||||
<BaseIndicator
|
||||
icon = { IconArrowDownLarge }
|
||||
|
@ -150,7 +192,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.packetloss')}` }
|
||||
{ t('connectionindicator.packetloss') }
|
||||
</Text>
|
||||
<BaseIndicator
|
||||
icon = { IconArrowDownLarge }
|
||||
|
@ -171,7 +213,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.resolution')} ` }
|
||||
{ t('connectionindicator.resolution') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.resolutionString }
|
||||
|
@ -179,7 +221,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
</View>
|
||||
<View style = { styles.statsInfoCell }>
|
||||
<Text style = { styles.statsTitleText }>
|
||||
{ `${t('connectionindicator.codecs')}` }
|
||||
{ t('connectionindicator.codecs') }
|
||||
</Text>
|
||||
<Text style = { styles.statsInfoText }>
|
||||
{ this.state.codecString }
|
||||
|
@ -206,7 +248,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @inheritdoc
|
||||
* returns {void}
|
||||
*/
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
if (prevProps.participantID !== this.props.participantID) {
|
||||
statsEmitter.unsubscribeToClientStats(
|
||||
prevProps.participantID, this._onStatsUpdated);
|
||||
|
@ -215,8 +257,6 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_onStatsUpdated: Object => void;
|
||||
|
||||
/**
|
||||
* Callback invoked when new connection stats associated with the passed in
|
||||
* user ID are available. Will update the component's display of current
|
||||
|
@ -239,9 +279,8 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {State}
|
||||
*/
|
||||
_buildState(stats) {
|
||||
_buildState(stats: any) {
|
||||
const { download: downloadBitrate, upload: uploadBitrate } = this._extractBitrate(stats) ?? {};
|
||||
|
||||
const { download: downloadPacketLost, upload: uploadPacketLost } = this._extractPacketLost(stats) ?? {};
|
||||
|
||||
return {
|
||||
|
@ -254,7 +293,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
? this.state.packetLostUploadString : `${uploadPacketLost}%`,
|
||||
serverRegionString: this._extractServer(stats) ?? this.state.serverRegionString,
|
||||
codecString: this._extractCodecs(stats) ?? this.state.codecString,
|
||||
connectionString: this._extractConnection(stats) ?? this.state.connectionString
|
||||
connectionString: this._extractConnection(stats)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -265,7 +304,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractResolutionString(stats) {
|
||||
_extractResolutionString(stats: any) {
|
||||
const { framerate, resolution } = stats;
|
||||
|
||||
const resolutionString = Object.keys(resolution || {})
|
||||
|
@ -290,7 +329,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {{ download, upload }}
|
||||
*/
|
||||
_extractBitrate(stats) {
|
||||
_extractBitrate(stats: any) {
|
||||
return stats.bitrate;
|
||||
}
|
||||
|
||||
|
@ -301,7 +340,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {{ download, upload }}
|
||||
*/
|
||||
_extractPacketLost(stats) {
|
||||
_extractPacketLost(stats: any) {
|
||||
return stats.packetLoss;
|
||||
}
|
||||
|
||||
|
@ -312,7 +351,7 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractServer(stats) {
|
||||
_extractServer(stats: any) {
|
||||
return stats.serverRegion;
|
||||
}
|
||||
|
||||
|
@ -323,17 +362,17 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractCodecs(stats) {
|
||||
_extractCodecs(stats: any) {
|
||||
const { codec } = stats;
|
||||
|
||||
let codecString;
|
||||
|
||||
if (codec) {
|
||||
const audioCodecs = Object.values(codec)
|
||||
.map(c => c.audio)
|
||||
.map((c: any) => c.audio)
|
||||
.filter(Boolean);
|
||||
const videoCodecs = Object.values(codec)
|
||||
.map(c => c.video)
|
||||
.map((c: any) => c.video)
|
||||
.filter(Boolean);
|
||||
|
||||
if (audioCodecs.length || videoCodecs.length) {
|
||||
|
@ -352,14 +391,39 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_extractConnection(stats) {
|
||||
_extractConnection(stats: any) {
|
||||
const { connectionQuality } = stats;
|
||||
const {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
} = this.props;
|
||||
|
||||
if (connectionQuality) {
|
||||
const signalLevel = Math.floor(connectionQuality / 33.4);
|
||||
|
||||
return CONNECTION_QUALITY[signalLevel];
|
||||
if (_isConnectionStatusInactive) {
|
||||
return 'connectionindicator.quality.inactive';
|
||||
} else if (_isConnectionStatusInterrupted) {
|
||||
return 'connectionindicator.quality.lost';
|
||||
} else if (typeof connectionQuality === 'undefined') {
|
||||
return 'connectionindicator.quality.good';
|
||||
}
|
||||
|
||||
const qualityConfig = this._getQualityConfig(connectionQuality);
|
||||
|
||||
return qualityConfig.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quality configuration from CONNECTION_QUALITY which has a percentage
|
||||
* that matches or exceeds the passed in percentage. The implementation
|
||||
* assumes CONNECTION_QUALITY is already sorted by highest to lowest
|
||||
* percentage.
|
||||
*
|
||||
* @param {number} percent - The connection percentage, out of 100, to find
|
||||
* the closest matching configuration for.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getQualityConfig(percent: number): any {
|
||||
return CONNECTION_QUALITY.find(x => percent >= x.percent) || {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,12 +470,19 @@ class ConnectionStatusComponent extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const { participantID } = ownProps;
|
||||
const tracks = state['features/base/tracks'];
|
||||
const _videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantID);
|
||||
const _isConnectionStatusInactive = isTrackStreamingStatusInactive(_videoTrack);
|
||||
const _isConnectionStatusInterrupted = isTrackStreamingStatusInterrupted(_videoTrack);
|
||||
|
||||
return {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_participantDisplayName: getParticipantDisplayName(state, participantID)
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default translate(connect(_mapStateToProps)(withTheme(ConnectionStatusComponent)));
|
Loading…
Reference in New Issue