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.

SpeakerStats.js 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { Dialog } from '../../base/dialog';
  5. import { translate } from '../../base/i18n';
  6. import { getLocalParticipant } from '../../base/participants';
  7. import SpeakerStatsItem from './SpeakerStatsItem';
  8. import SpeakerStatsLabels from './SpeakerStatsLabels';
  9. declare var interfaceConfig: Object;
  10. /**
  11. * The type of the React {@code Component} props of {@link SpeakerStats}
  12. */
  13. type Props = {
  14. /**
  15. * The display name for the local participant obtained from the redux store.
  16. */
  17. _localDisplayName: string,
  18. /**
  19. * The JitsiConference from which stats will be pulled.
  20. */
  21. conference: Object,
  22. /**
  23. * The function to translate human-readable text.
  24. */
  25. t: Function
  26. };
  27. /**
  28. * The type of the React {@code Component} state of {@link SpeakerStats}
  29. */
  30. type State = {
  31. /**
  32. * The stats summary provided by the JitsiConference.
  33. */
  34. stats: Object
  35. };
  36. /**
  37. * React component for displaying a list of speaker stats.
  38. *
  39. * @extends Component
  40. */
  41. class SpeakerStats extends Component<Props, State> {
  42. state = {
  43. stats: {}
  44. };
  45. _updateInterval: IntervalID;
  46. /**
  47. * Initializes a new SpeakerStats instance.
  48. *
  49. * @param {Object} props - The read-only React Component props with which
  50. * the new instance is to be initialized.
  51. */
  52. constructor(props) {
  53. super(props);
  54. // Bind event handlers so they are only bound once per instance.
  55. this._updateStats = this._updateStats.bind(this);
  56. }
  57. /**
  58. * Immediately request for updated speaker stats and begin
  59. * polling for speaker stats updates.
  60. *
  61. * @inheritdoc
  62. * @returns {void}
  63. */
  64. componentWillMount() {
  65. this._updateStats();
  66. this._updateInterval = setInterval(this._updateStats, 1000);
  67. }
  68. /**
  69. * Stop polling for speaker stats updates.
  70. *
  71. * @inheritdoc
  72. * @returns {void}
  73. */
  74. componentWillUnmount() {
  75. clearInterval(this._updateInterval);
  76. }
  77. /**
  78. * Implements React's {@link Component#render()}.
  79. *
  80. * @inheritdoc
  81. * @returns {ReactElement}
  82. */
  83. render() {
  84. const userIds = Object.keys(this.state.stats);
  85. const items = userIds.map(userId => this._createStatsItem(userId));
  86. return (
  87. <Dialog
  88. cancelTitleKey = { 'dialog.close' }
  89. submitDisabled = { true }
  90. titleKey = 'speakerStats.speakerStats'>
  91. <div className = 'speaker-stats'>
  92. <SpeakerStatsLabels />
  93. { items }
  94. </div>
  95. </Dialog>
  96. );
  97. }
  98. /**
  99. * Create a SpeakerStatsItem instance for the passed in user id.
  100. *
  101. * @param {string} userId - User id used to look up the associated
  102. * speaker stats from the jitsi library.
  103. * @returns {SpeakerStatsItem|null}
  104. * @private
  105. */
  106. _createStatsItem(userId) {
  107. const statsModel = this.state.stats[userId];
  108. if (!statsModel) {
  109. return null;
  110. }
  111. const isDominantSpeaker = statsModel.isDominantSpeaker();
  112. const dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime();
  113. const hasLeft = statsModel.hasLeft();
  114. let displayName;
  115. if (statsModel.isLocalStats()) {
  116. const { t } = this.props;
  117. const meString = t('me');
  118. displayName = this.props._localDisplayName;
  119. displayName
  120. = displayName ? `${displayName} (${meString})` : meString;
  121. } else {
  122. displayName
  123. = this.state.stats[userId].getDisplayName()
  124. || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
  125. }
  126. return (
  127. <SpeakerStatsItem
  128. displayName = { displayName }
  129. dominantSpeakerTime = { dominantSpeakerTime }
  130. hasLeft = { hasLeft }
  131. isDominantSpeaker = { isDominantSpeaker }
  132. key = { userId } />
  133. );
  134. }
  135. _updateStats: () => void;
  136. /**
  137. * Update the internal state with the latest speaker stats.
  138. *
  139. * @returns {void}
  140. * @private
  141. */
  142. _updateStats() {
  143. const stats = this.props.conference.getSpeakerStats();
  144. this.setState({ stats });
  145. }
  146. }
  147. /**
  148. * Maps (parts of) the redux state to the associated SpeakerStats's props.
  149. *
  150. * @param {Object} state - The redux state.
  151. * @private
  152. * @returns {{
  153. * _localDisplayName: ?string
  154. * }}
  155. */
  156. function _mapStateToProps(state) {
  157. const localParticipant = getLocalParticipant(state);
  158. return {
  159. /**
  160. * The local display name.
  161. *
  162. * @private
  163. * @type {string|undefined}
  164. */
  165. _localDisplayName: localParticipant && localParticipant.name
  166. };
  167. }
  168. export default translate(connect(_mapStateToProps)(SpeakerStats));