diff --git a/react/features/base/styles/components/styles/ColorPalette.js b/react/features/base/styles/components/styles/ColorPalette.ts similarity index 53% rename from react/features/base/styles/components/styles/ColorPalette.js rename to react/features/base/styles/components/styles/ColorPalette.ts index a60aff472..45b880ee7 100644 --- a/react/features/base/styles/components/styles/ColorPalette.js +++ b/react/features/base/styles/components/styles/ColorPalette.ts @@ -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' }; diff --git a/react/features/base/testing/components/TestConnectionInfo.js b/react/features/base/testing/components/TestConnectionInfo.js index 263be090f..987a6ad5d 100644 --- a/react/features/base/testing/components/TestConnectionInfo.js +++ b/react/features/base/testing/components/TestConnectionInfo.js @@ -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'; diff --git a/react/features/conference/components/native/carmode/TitleBar.tsx b/react/features/conference/components/native/carmode/TitleBar.tsx index 90405d1f0..d265f6a33 100644 --- a/react/features/conference/components/native/carmode/TitleBar.tsx +++ b/react/features/conference/components/native/carmode/TitleBar.tsx @@ -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 => { }> diff --git a/react/features/connection-indicator/components/AbstractConnectionIndicator.js b/react/features/connection-indicator/components/AbstractConnectionIndicator.js index 3008f5896..aadc29370 100644 --- a/react/features/connection-indicator/components/AbstractConnectionIndicator.js +++ b/react/features/connection-indicator/components/AbstractConnectionIndicator.js @@ -209,7 +209,7 @@ class AbstractConnectionIndicator extends Component { */ export function mapStateToProps(state: Object) { return { - _autoHideTimeout: state['features/base/config'].connectionIndicators.autoHideTimeout ?? defaultAutoHideTimeout + _autoHideTimeout: state['features/base/config'].connectionIndicators?.autoHideTimeout ?? defaultAutoHideTimeout }; } diff --git a/react/features/connection-indicator/components/index.native.js b/react/features/connection-indicator/components/index.native.js deleted file mode 100644 index a32ec6061..000000000 --- a/react/features/connection-indicator/components/index.native.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export * from './native'; diff --git a/react/features/connection-indicator/components/index.web.js b/react/features/connection-indicator/components/index.web.js deleted file mode 100644 index 40d5f4652..000000000 --- a/react/features/connection-indicator/components/index.web.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export * from './web'; diff --git a/react/features/connection-indicator/components/native/ConnectionIndicator.js b/react/features/connection-indicator/components/native/ConnectionIndicator.js deleted file mode 100644 index 5da0b5d17..000000000 --- a/react/features/connection-indicator/components/native/ConnectionIndicator.js +++ /dev/null @@ -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 { - /** - * 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 ( - - - - ); - } - -} - -export default connect()(ConnectionIndicator); diff --git a/react/features/connection-indicator/components/native/ConnectionIndicator.tsx b/react/features/connection-indicator/components/native/ConnectionIndicator.tsx new file mode 100644 index 000000000..512b3bc43 --- /dev/null +++ b/react/features/connection-indicator/components/native/ConnectionIndicator.tsx @@ -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 { + /** + * 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 ( + + + + ); + } + +} + +/** + * 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); diff --git a/react/features/connection-indicator/components/native/index.js b/react/features/connection-indicator/components/native/index.js deleted file mode 100644 index 8038d8459..000000000 --- a/react/features/connection-indicator/components/native/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export { default as ConnectionIndicator } from './ConnectionIndicator'; diff --git a/react/features/connection-indicator/components/native/styles.js b/react/features/connection-indicator/components/native/styles.js deleted file mode 100644 index 4f00065e5..000000000 --- a/react/features/connection-indicator/components/native/styles.js +++ /dev/null @@ -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 -}; diff --git a/react/features/connection-indicator/components/native/styles.ts b/react/features/connection-indicator/components/native/styles.ts new file mode 100644 index 000000000..fe561f4b4 --- /dev/null +++ b/react/features/connection-indicator/components/native/styles.ts @@ -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 +}; diff --git a/react/features/connection-indicator/components/web/ConnectionIndicator.tsx b/react/features/connection-indicator/components/web/ConnectionIndicator.tsx index 890441ed5..83709c835 100644 --- a/react/features/connection-indicator/components/web/ConnectionIndicator.tsx +++ b/react/features/connection-indicator/components/web/ConnectionIndicator.tsx @@ -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), diff --git a/react/features/connection-indicator/index.js b/react/features/connection-indicator/index.js deleted file mode 100644 index d309bedc1..000000000 --- a/react/features/connection-indicator/index.js +++ /dev/null @@ -1,5 +0,0 @@ -// @flow - -export * from './components'; - -export { default as statsEmitter } from './statsEmitter'; diff --git a/react/features/filmstrip/components/native/Thumbnail.js b/react/features/filmstrip/components/native/Thumbnail.js index a68e5f84b..c6a930418 100644 --- a/react/features/filmstrip/components/native/Thumbnail.js +++ b/react/features/filmstrip/components/native/Thumbnail.js @@ -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 { dispatch(showSharedVideoMenu(_participantId)); } + // TODO: add support for getting info about the virtual screen shares. + if (!_fakeParticipant) { dispatch(showContextMenuDetails(_participantId, _local)); } diff --git a/react/features/video-menu/components/native/ConnectionStatusComponent.js b/react/features/video-menu/components/native/ConnectionStatusComponent.tsx similarity index 71% rename from react/features/video-menu/components/native/ConnectionStatusComponent.js rename to react/features/video-menu/components/native/ConnectionStatusComponent.tsx index d40079176..8b0f355e1 100644 --- a/react/features/video-menu/components/native/ConnectionStatusComponent.js +++ b/react/features/video-menu/components/native/ConnectionStatusComponent.tsx @@ -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 { +class ConnectionStatusComponent extends PureComponent { /** * Constructor of the component. @@ -85,7 +127,7 @@ class ConnectionStatusComponent extends Component { * * @inheritdoc */ - constructor(props: Props) { + constructor(props: IProps) { super(props); this._onStatsUpdated = this._onStatsUpdated.bind(this); @@ -121,15 +163,15 @@ class ConnectionStatusComponent extends Component { - { `${t('connectionindicator.status')} ` } + { t('connectionindicator.status') } - { this.state.connectionString } + { t(this.state.connectionString) } - { `${t('connectionindicator.bitrate')}` } + { t('connectionindicator.bitrate') } { - { `${t('connectionindicator.packetloss')}` } + { t('connectionindicator.packetloss') } { - { `${t('connectionindicator.resolution')} ` } + { t('connectionindicator.resolution') } { this.state.resolutionString } @@ -179,7 +221,7 @@ class ConnectionStatusComponent extends Component { - { `${t('connectionindicator.codecs')}` } + { t('connectionindicator.codecs') } { this.state.codecString } @@ -206,7 +248,7 @@ class ConnectionStatusComponent extends Component { * @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 { } } - _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 { * @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 { ? 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 { * @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 { * @private * @returns {{ download, upload }} */ - _extractBitrate(stats) { + _extractBitrate(stats: any) { return stats.bitrate; } @@ -301,7 +340,7 @@ class ConnectionStatusComponent extends Component { * @private * @returns {{ download, upload }} */ - _extractPacketLost(stats) { + _extractPacketLost(stats: any) { return stats.packetLoss; } @@ -312,7 +351,7 @@ class ConnectionStatusComponent extends Component { * @private * @returns {string} */ - _extractServer(stats) { + _extractServer(stats: any) { return stats.serverRegion; } @@ -323,17 +362,17 @@ class ConnectionStatusComponent extends Component { * @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 { * @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 { * @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)));