2019-04-15 16:23:28 +00:00
|
|
|
// @flow
|
|
|
|
|
|
|
|
import { Component } from 'react';
|
|
|
|
|
2022-10-31 21:17:05 +00:00
|
|
|
import { getVirtualScreenshareParticipantOwnerId } from '../../base/participants/functions';
|
2019-04-15 16:23:28 +00:00
|
|
|
import statsEmitter from '../statsEmitter';
|
|
|
|
|
2021-09-09 12:50:22 +00:00
|
|
|
const defaultAutoHideTimeout = 5000;
|
|
|
|
|
2019-04-15 16:23:28 +00:00
|
|
|
/**
|
|
|
|
* The connection quality percentage that must be reached to be considered of
|
|
|
|
* good quality and can result in the connection indicator being hidden.
|
|
|
|
*
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
export const INDICATOR_DISPLAY_THRESHOLD = 30;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the React {@code Component} props of {@link ConnectionIndicator}.
|
|
|
|
*/
|
|
|
|
export type Props = {
|
|
|
|
|
2021-09-09 12:50:22 +00:00
|
|
|
/**
|
|
|
|
* How long the connection indicator should remain displayed before hiding.
|
|
|
|
*/
|
|
|
|
_autoHideTimeout: number,
|
|
|
|
|
2022-10-31 21:17:05 +00:00
|
|
|
/**
|
|
|
|
* Whether or not the statistics are for screen share.
|
|
|
|
*/
|
|
|
|
_isVirtualScreenshareParticipant: boolean,
|
|
|
|
|
2019-04-15 16:23:28 +00:00
|
|
|
/**
|
|
|
|
* The ID of the participant associated with the displayed connection indication and
|
|
|
|
* stats.
|
|
|
|
*/
|
2022-05-09 13:42:45 +00:00
|
|
|
participantId: string,
|
|
|
|
|
2022-06-17 10:08:21 +00:00
|
|
|
/**
|
|
|
|
* Custom icon style.
|
|
|
|
*/
|
2022-10-28 20:24:44 +00:00
|
|
|
iconStyle?: Object
|
2019-04-15 16:23:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the React {@code Component} state of {@link ConnectionIndicator}.
|
|
|
|
*/
|
|
|
|
export type State = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not a CSS class should be applied to the root for hiding the
|
|
|
|
* connection indicator. By default the indicator should start out hidden
|
|
|
|
* because the current connection status is not known at mount.
|
|
|
|
*/
|
|
|
|
showIndicator: boolean,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cache of the stats received from subscribing to stats emitting. The keys
|
|
|
|
* should be the name of the stat. With each stat update, updates stats are
|
|
|
|
* mixed in with cached stats and a new stats object is set in state.
|
|
|
|
*/
|
|
|
|
stats: Object
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a React {@link Component} which displays the current connection
|
|
|
|
* quality.
|
|
|
|
*
|
2021-11-04 21:10:43 +00:00
|
|
|
* @augments {Component}
|
2019-04-15 16:23:28 +00:00
|
|
|
*/
|
2021-09-09 12:50:22 +00:00
|
|
|
class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
2019-04-15 16:23:28 +00:00
|
|
|
/**
|
|
|
|
* The timeout for automatically hiding the indicator.
|
|
|
|
*/
|
2021-11-04 21:10:43 +00:00
|
|
|
autoHideTimeout: ?TimeoutID;
|
2019-04-15 16:23:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes a new {@code ConnectionIndicator} instance.
|
|
|
|
*
|
|
|
|
* @param {P} props - The read-only properties with which the new
|
|
|
|
* instance is to be initialized.
|
|
|
|
*/
|
|
|
|
constructor(props: P) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
// Bind event handlers so they are only bound once for every instance.
|
|
|
|
this._onStatsUpdated = this._onStatsUpdated.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts listening for stat updates.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* returns {void}
|
|
|
|
*/
|
|
|
|
componentDidMount() {
|
2022-10-31 21:17:05 +00:00
|
|
|
statsEmitter.subscribeToClientStats(this._getRealParticipantId(this.props), this._onStatsUpdated);
|
2019-04-15 16:23:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates which user's stats are being listened to.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* returns {void}
|
|
|
|
*/
|
|
|
|
componentDidUpdate(prevProps: Props) {
|
2022-10-31 21:17:05 +00:00
|
|
|
const prevParticipantId = this._getRealParticipantId(prevProps);
|
|
|
|
const participantId = this._getRealParticipantId(this.props);
|
|
|
|
|
|
|
|
if (prevParticipantId !== participantId) {
|
|
|
|
statsEmitter.unsubscribeToClientStats(prevParticipantId, this._onStatsUpdated);
|
|
|
|
statsEmitter.subscribeToClientStats(participantId, this._onStatsUpdated);
|
2019-04-15 16:23:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cleans up any queued processes, which includes listening for new stats
|
|
|
|
* and clearing any timeout to hide the indicator.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
componentWillUnmount() {
|
2022-10-31 21:17:05 +00:00
|
|
|
statsEmitter.unsubscribeToClientStats(this._getRealParticipantId(this.props), this._onStatsUpdated);
|
2022-05-09 13:42:45 +00:00
|
|
|
|
2019-04-15 16:23:28 +00:00
|
|
|
clearTimeout(this.autoHideTimeout);
|
|
|
|
}
|
|
|
|
|
2022-10-31 21:17:05 +00:00
|
|
|
/**
|
|
|
|
* Gets the "real" participant ID. FOr a virtual screenshare participant, that is its "owner".
|
|
|
|
*
|
|
|
|
* @param {Props} props - The props where to extract the data from.
|
|
|
|
* @returns {string | undefined } The resolved participant ID.
|
|
|
|
*/
|
|
|
|
_getRealParticipantId(props: Props) {
|
|
|
|
if (props._isVirtualScreenshareParticipant) {
|
|
|
|
return getVirtualScreenshareParticipantOwnerId(props.participantId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return props.participantId;
|
|
|
|
}
|
|
|
|
|
2019-04-15 16:23:28 +00:00
|
|
|
_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
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @param {Object} stats - Connection stats from the library.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onStatsUpdated(stats = {}) {
|
|
|
|
// Rely on React to batch setState actions.
|
|
|
|
const { connectionQuality } = stats;
|
|
|
|
const newPercentageState = typeof connectionQuality === 'undefined'
|
|
|
|
? {} : { percent: connectionQuality };
|
|
|
|
const newStats = Object.assign(
|
|
|
|
{},
|
|
|
|
this.state.stats,
|
|
|
|
stats,
|
|
|
|
newPercentageState);
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
stats: newStats
|
|
|
|
});
|
|
|
|
|
|
|
|
this._updateIndicatorAutoHide(newStats.percent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the internal state for automatically hiding the indicator.
|
|
|
|
*
|
|
|
|
* @param {number} percent - The current connection quality percentage
|
|
|
|
* between the values 0 and 100.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_updateIndicatorAutoHide(percent) {
|
|
|
|
if (percent < INDICATOR_DISPLAY_THRESHOLD) {
|
|
|
|
clearTimeout(this.autoHideTimeout);
|
|
|
|
this.autoHideTimeout = undefined;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
showIndicator: true
|
|
|
|
});
|
|
|
|
} else if (this.autoHideTimeout) {
|
|
|
|
// This clause is intentionally left blank because no further action
|
|
|
|
// is needed if the percent is below the threshold and there is an
|
|
|
|
// autoHideTimeout set.
|
|
|
|
} else {
|
|
|
|
this.autoHideTimeout = setTimeout(() => {
|
|
|
|
this.setState({
|
|
|
|
showIndicator: false
|
|
|
|
});
|
2021-09-09 12:50:22 +00:00
|
|
|
}, this.props._autoHideTimeout);
|
2019-04-15 16:23:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-09 12:50:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Maps (parts of) the Redux state to the associated props for the
|
|
|
|
* {@code ConnectorIndicator} component.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The Redux state.
|
|
|
|
* @private
|
|
|
|
* @returns {Props}
|
|
|
|
*/
|
|
|
|
export function mapStateToProps(state: Object) {
|
|
|
|
return {
|
2022-11-11 11:11:30 +00:00
|
|
|
_autoHideTimeout: state['features/base/config'].connectionIndicators?.autoHideTimeout ?? defaultAutoHideTimeout
|
2021-09-09 12:50:22 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export default AbstractConnectionIndicator;
|