您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ConnectionIndicator.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import React, { Component } from 'react';
  2. import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
  3. import { Popover } from '../../base/popover';
  4. import { ConnectionStatsTable } from '../../connection-stats';
  5. import statsEmitter from '../statsEmitter';
  6. declare var $: Object;
  7. declare var interfaceConfig: Object;
  8. // Converts the percent for connection quality into a string recognized for CSS.
  9. const QUALITY_TO_WIDTH = [
  10. // Full (5 bars)
  11. {
  12. percent: 80,
  13. width: '100%'
  14. },
  15. // 4 bars
  16. {
  17. percent: 60,
  18. width: '80%'
  19. },
  20. // 3 bars
  21. {
  22. percent: 40,
  23. width: '55%'
  24. },
  25. // 2 bars
  26. {
  27. percent: 20,
  28. width: '40%'
  29. },
  30. // 1 bar
  31. {
  32. percent: 0,
  33. width: '20%'
  34. }
  35. // Note: we never show 0 bars.
  36. ];
  37. /**
  38. * Implements a React {@link Component} which displays the current connection
  39. * quality percentage and has a popover to show more detailed connection stats.
  40. *
  41. * @extends {Component}
  42. */
  43. class ConnectionIndicator extends Component {
  44. /**
  45. * {@code ConnectionIndicator} component's property types.
  46. *
  47. * @static
  48. */
  49. static propTypes = {
  50. /**
  51. * The current condition of the user's connection, matching one of the
  52. * enumerated values in the library.
  53. *
  54. * @type {JitsiParticipantConnectionStatus}
  55. */
  56. connectionStatus: React.PropTypes.string,
  57. /**
  58. * Whether or not clicking the indicator should display a popover for
  59. * more details.
  60. */
  61. enableStatsDisplay: React.PropTypes.bool,
  62. /**
  63. * Whether or not the displays stats are for local video.
  64. */
  65. isLocalVideo: React.PropTypes.bool,
  66. /**
  67. * Relative to the icon from where the popover for more connection
  68. * details should display.
  69. */
  70. statsPopoverPosition: React.PropTypes.string,
  71. /**
  72. * Invoked to obtain translated strings.
  73. */
  74. t: React.PropTypes.func,
  75. /**
  76. * The user ID associated with the displayed connection indication and
  77. * stats.
  78. */
  79. userID: React.PropTypes.string
  80. };
  81. /**
  82. * Initializes a new {@code ConnectionIndicator} instance.
  83. *
  84. * @param {Object} props - The read-only properties with which the new
  85. * instance is to be initialized.
  86. */
  87. constructor(props) {
  88. super(props);
  89. this.state = {
  90. /**
  91. * Whether or not the popover content should display additional
  92. * statistics.
  93. *
  94. * @type {boolean}
  95. */
  96. showMoreStats: false,
  97. /**
  98. * Cache of the stats received from subscribing to stats emitting.
  99. * The keys should be the name of the stat. With each stat update,
  100. * updates stats are mixed in with cached stats and a new stats
  101. * object is set in state.
  102. */
  103. stats: {}
  104. };
  105. // Bind event handlers so they are only bound once for every instance.
  106. this._onStatsUpdated = this._onStatsUpdated.bind(this);
  107. this._onToggleShowMore = this._onToggleShowMore.bind(this);
  108. }
  109. /**
  110. * Starts listening for stat updates.
  111. *
  112. * @inheritdoc
  113. * returns {void}
  114. */
  115. componentDidMount() {
  116. statsEmitter.subscribeToClientStats(
  117. this.props.userID, this._onStatsUpdated);
  118. }
  119. /**
  120. * Updates which user's stats are being listened to.
  121. *
  122. * @inheritdoc
  123. * returns {void}
  124. */
  125. componentDidUpdate(prevProps) {
  126. if (prevProps.userID !== this.props.userID) {
  127. statsEmitter.unsubscribeToClientStats(
  128. prevProps.userID, this._onStatsUpdated);
  129. statsEmitter.subscribeToClientStats(
  130. this.props.userID, this._onStatsUpdated);
  131. }
  132. }
  133. /**
  134. * Sets the state to hide the Statistics Table popover.
  135. *
  136. * @private
  137. * @returns {void}
  138. */
  139. componentWillUnmount() {
  140. statsEmitter.unsubscribeToClientStats(
  141. this.props.userID, this._onStatsUpdated);
  142. }
  143. /**
  144. * Implements React's {@link Component#render()}.
  145. *
  146. * @inheritdoc
  147. * @returns {ReactElement}
  148. */
  149. render() {
  150. return (
  151. <Popover
  152. className = 'indicator-container'
  153. content = { this._renderStatisticsTable() }
  154. position = { this.props.statsPopoverPosition }>
  155. <div className = 'popover-trigger'>
  156. <div className = 'connection-indicator indicator'>
  157. <div className = 'connection indicatoricon'>
  158. { this._renderIcon() }
  159. </div>
  160. </div>
  161. </div>
  162. </Popover>
  163. );
  164. }
  165. /**
  166. * Callback invoked when new connection stats associated with the passed in
  167. * user ID are available. Will update the component's display of current
  168. * statistics.
  169. *
  170. * @param {Object} stats - Connection stats from the library.
  171. * @private
  172. * @returns {void}
  173. */
  174. _onStatsUpdated(stats = {}) {
  175. const { connectionQuality } = stats;
  176. const newPercentageState = typeof connectionQuality === 'undefined'
  177. ? {} : { percent: connectionQuality };
  178. const newStats = Object.assign(
  179. {},
  180. this.state.stats,
  181. stats,
  182. newPercentageState);
  183. this.setState({
  184. stats: newStats
  185. });
  186. }
  187. /**
  188. * Callback to invoke when the show more link in the popover content is
  189. * clicked. Sets the state which will determine if the popover should show
  190. * additional statistics about the connection.
  191. *
  192. * @returns {void}
  193. */
  194. _onToggleShowMore() {
  195. this.setState({ showMoreStats: !this.state.showMoreStats });
  196. }
  197. /**
  198. * Creates a ReactElement for displaying an icon that represents the current
  199. * connection quality.
  200. *
  201. * @returns {ReactElement}
  202. */
  203. _renderIcon() {
  204. switch (this.props.connectionStatus) {
  205. case JitsiParticipantConnectionStatus.INTERRUPTED:
  206. return (
  207. <span className = 'connection_lost'>
  208. <i className = 'icon-connection-lost' />
  209. </span>
  210. );
  211. case JitsiParticipantConnectionStatus.INACTIVE:
  212. return (
  213. <span className = 'connection_ninja'>
  214. <i className = 'icon-ninja' />
  215. </span>
  216. );
  217. default: {
  218. const { percent } = this.state.stats;
  219. const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
  220. const iconWidth = width && width.width
  221. ? { width: width && width.width } : {};
  222. return [
  223. <span
  224. className = 'connection_empty'
  225. key = 'icon-empty'>
  226. <i className = 'icon-connection' />
  227. </span>,
  228. <span
  229. className = 'connection_full'
  230. key = 'icon-full'
  231. style = { iconWidth }>
  232. <i className = 'icon-connection' />
  233. </span>
  234. ];
  235. }
  236. }
  237. }
  238. /**
  239. * Creates a {@code ConnectionStatisticsTable} instance.
  240. *
  241. * @returns {ReactElement}
  242. */
  243. _renderStatisticsTable() {
  244. const {
  245. bandwidth,
  246. bitrate,
  247. framerate,
  248. packetLoss,
  249. resolution,
  250. transport
  251. } = this.state.stats;
  252. return (
  253. <ConnectionStatsTable
  254. bandwidth = { bandwidth }
  255. bitrate = { bitrate }
  256. framerate = { framerate }
  257. isLocalVideo = { this.props.isLocalVideo }
  258. onShowMore = { this._onToggleShowMore }
  259. packetLoss = { packetLoss }
  260. resolution = { resolution }
  261. shouldShowMore = { this.state.showMoreStats }
  262. transport = { transport } />
  263. );
  264. }
  265. }
  266. export default ConnectionIndicator;