You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractConnectionIndicator.js 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // @flow
  2. import { Component } from 'react';
  3. import statsEmitter from '../statsEmitter';
  4. declare var interfaceConfig: Object;
  5. /**
  6. * The connection quality percentage that must be reached to be considered of
  7. * good quality and can result in the connection indicator being hidden.
  8. *
  9. * @type {number}
  10. */
  11. export const INDICATOR_DISPLAY_THRESHOLD = 30;
  12. /**
  13. * The type of the React {@code Component} props of {@link ConnectionIndicator}.
  14. */
  15. export type Props = {
  16. /**
  17. * The ID of the participant associated with the displayed connection indication and
  18. * stats.
  19. */
  20. participantId: string
  21. };
  22. /**
  23. * The type of the React {@code Component} state of {@link ConnectionIndicator}.
  24. */
  25. export type State = {
  26. /**
  27. * Whether or not a CSS class should be applied to the root for hiding the
  28. * connection indicator. By default the indicator should start out hidden
  29. * because the current connection status is not known at mount.
  30. */
  31. showIndicator: boolean,
  32. /**
  33. * Cache of the stats received from subscribing to stats emitting. The keys
  34. * should be the name of the stat. With each stat update, updates stats are
  35. * mixed in with cached stats and a new stats object is set in state.
  36. */
  37. stats: Object
  38. };
  39. /**
  40. * Implements a React {@link Component} which displays the current connection
  41. * quality.
  42. *
  43. * @extends {Component}
  44. */
  45. export default class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
  46. /**
  47. * The timeout for automatically hiding the indicator.
  48. */
  49. autoHideTimeout: ?TimeoutID
  50. /**
  51. * Initializes a new {@code ConnectionIndicator} instance.
  52. *
  53. * @param {P} props - The read-only properties with which the new
  54. * instance is to be initialized.
  55. */
  56. constructor(props: P) {
  57. super(props);
  58. // Bind event handlers so they are only bound once for every instance.
  59. this._onStatsUpdated = this._onStatsUpdated.bind(this);
  60. }
  61. /**
  62. * Starts listening for stat updates.
  63. *
  64. * @inheritdoc
  65. * returns {void}
  66. */
  67. componentDidMount() {
  68. statsEmitter.subscribeToClientStats(
  69. this.props.participantId, this._onStatsUpdated);
  70. }
  71. /**
  72. * Updates which user's stats are being listened to.
  73. *
  74. * @inheritdoc
  75. * returns {void}
  76. */
  77. componentDidUpdate(prevProps: Props) {
  78. if (prevProps.participantId !== this.props.participantId) {
  79. statsEmitter.unsubscribeToClientStats(
  80. prevProps.participantId, this._onStatsUpdated);
  81. statsEmitter.subscribeToClientStats(
  82. this.props.participantId, this._onStatsUpdated);
  83. }
  84. }
  85. /**
  86. * Cleans up any queued processes, which includes listening for new stats
  87. * and clearing any timeout to hide the indicator.
  88. *
  89. * @private
  90. * @returns {void}
  91. */
  92. componentWillUnmount() {
  93. statsEmitter.unsubscribeToClientStats(
  94. this.props.participantId, this._onStatsUpdated);
  95. clearTimeout(this.autoHideTimeout);
  96. }
  97. _onStatsUpdated: (Object) => void;
  98. /**
  99. * Callback invoked when new connection stats associated with the passed in
  100. * user ID are available. Will update the component's display of current
  101. * statistics.
  102. *
  103. * @param {Object} stats - Connection stats from the library.
  104. * @private
  105. * @returns {void}
  106. */
  107. _onStatsUpdated(stats = {}) {
  108. // Rely on React to batch setState actions.
  109. const { connectionQuality } = stats;
  110. const newPercentageState = typeof connectionQuality === 'undefined'
  111. ? {} : { percent: connectionQuality };
  112. const newStats = Object.assign(
  113. {},
  114. this.state.stats,
  115. stats,
  116. newPercentageState);
  117. this.setState({
  118. stats: newStats
  119. });
  120. this._updateIndicatorAutoHide(newStats.percent);
  121. }
  122. /**
  123. * Updates the internal state for automatically hiding the indicator.
  124. *
  125. * @param {number} percent - The current connection quality percentage
  126. * between the values 0 and 100.
  127. * @private
  128. * @returns {void}
  129. */
  130. _updateIndicatorAutoHide(percent) {
  131. if (percent < INDICATOR_DISPLAY_THRESHOLD) {
  132. clearTimeout(this.autoHideTimeout);
  133. this.autoHideTimeout = undefined;
  134. this.setState({
  135. showIndicator: true
  136. });
  137. } else if (this.autoHideTimeout) {
  138. // This clause is intentionally left blank because no further action
  139. // is needed if the percent is below the threshold and there is an
  140. // autoHideTimeout set.
  141. } else {
  142. this.autoHideTimeout = setTimeout(() => {
  143. this.setState({
  144. showIndicator: false
  145. });
  146. }, typeof interfaceConfig === 'undefined'
  147. ? 5000
  148. : interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT);
  149. }
  150. }
  151. }