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.

Thumbnail.js 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { Audio, MEDIA_TYPE } from '../../../base/media';
  5. import {
  6. PARTICIPANT_ROLE,
  7. ParticipantView,
  8. pinParticipant
  9. } from '../../../base/participants';
  10. import { Container } from '../../../base/react';
  11. import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
  12. import AudioMutedIndicator from './AudioMutedIndicator';
  13. import DominantSpeakerIndicator from './DominantSpeakerIndicator';
  14. import ModeratorIndicator from './ModeratorIndicator';
  15. import { AVATAR_SIZE } from '../styles';
  16. import styles from './styles';
  17. import VideoMutedIndicator from './VideoMutedIndicator';
  18. /**
  19. * Thumbnail component's property types.
  20. */
  21. type Props = {
  22. /**
  23. * The Redux representation of the participant's audio track.
  24. */
  25. _audioTrack: Object,
  26. /**
  27. * The Redux representation of the state "features/large-video".
  28. */
  29. _largeVideo: Object,
  30. /**
  31. * The Redux representation of the participant's video track.
  32. */
  33. _videoTrack: Object,
  34. /**
  35. * If true, tapping on the thumbnail will not pin the participant to large
  36. * video. By default tapping does pin the participant.
  37. */
  38. disablePin?: boolean,
  39. /**
  40. * If true, there will be no color overlay (tint) on the thumbnail
  41. * indicating the participant associated with the thumbnail is displayed on
  42. * large video. By default there will be a tint.
  43. */
  44. disableTint?: boolean,
  45. /**
  46. * Invoked to trigger state changes in Redux.
  47. */
  48. dispatch: Dispatch<*>,
  49. /**
  50. * The Redux representation of the participant to display.
  51. */
  52. participant: Object,
  53. /**
  54. * Optional styling to add or override on the Thumbnail component root.
  55. */
  56. styleOverrides?: Object
  57. };
  58. /**
  59. * React component for video thumbnail.
  60. *
  61. * @extends Component
  62. */
  63. class Thumbnail extends Component<Props> {
  64. /**
  65. * Initializes new Video Thumbnail component.
  66. *
  67. * @param {Object} props - Component props.
  68. */
  69. constructor(props: Props) {
  70. super(props);
  71. // Bind event handlers so they are only bound once for every instance.
  72. this._onClick = this._onClick.bind(this);
  73. }
  74. /**
  75. * Implements React's {@link Component#render()}.
  76. *
  77. * @inheritdoc
  78. * @returns {ReactElement}
  79. */
  80. render() {
  81. const {
  82. _audioTrack: audioTrack,
  83. _largeVideo: largeVideo,
  84. _videoTrack: videoTrack,
  85. disablePin,
  86. disableTint,
  87. participant
  88. } = this.props;
  89. // We don't render audio in any of the following:
  90. // 1. The audio (source) is muted. There's no practical reason (that we
  91. // know of, anyway) why we'd want to render it given that it's
  92. // silence (& not even comfort noise).
  93. // 2. The audio is local. If we were to render local audio, the local
  94. // participants would be hearing themselves.
  95. const audioMuted = !audioTrack || audioTrack.muted;
  96. const renderAudio = !audioMuted && !audioTrack.local;
  97. const participantId = participant.id;
  98. const participantInLargeVideo
  99. = participantId === largeVideo.participantId;
  100. const videoMuted = !videoTrack || videoTrack.muted;
  101. return (
  102. <Container
  103. onClick = { disablePin ? undefined : this._onClick }
  104. style = { [
  105. styles.thumbnail,
  106. participant.pinned && !disablePin
  107. ? styles.thumbnailPinned : null,
  108. this.props.styleOverrides || null
  109. ] }>
  110. { renderAudio
  111. && <Audio
  112. stream
  113. = { audioTrack.jitsiTrack.getOriginalStream() } /> }
  114. <ParticipantView
  115. avatarSize = { AVATAR_SIZE }
  116. participantId = { participantId }
  117. tintEnabled = { participantInLargeVideo && !disableTint }
  118. zOrder = { 1 } />
  119. { participant.role === PARTICIPANT_ROLE.MODERATOR
  120. && <ModeratorIndicator /> }
  121. { participant.dominantSpeaker
  122. && <DominantSpeakerIndicator /> }
  123. <Container style = { styles.thumbnailIndicatorContainer }>
  124. { audioMuted
  125. && <AudioMutedIndicator /> }
  126. { videoMuted
  127. && <VideoMutedIndicator /> }
  128. </Container>
  129. </Container>
  130. );
  131. }
  132. _onClick: () => void;
  133. /**
  134. * Handles click/tap event on the thumbnail.
  135. *
  136. * @returns {void}
  137. */
  138. _onClick() {
  139. const { dispatch, participant } = this.props;
  140. // TODO The following currently ignores interfaceConfig.filmStripOnly.
  141. dispatch(pinParticipant(participant.pinned ? null : participant.id));
  142. }
  143. }
  144. /**
  145. * Function that maps parts of Redux state tree into component props.
  146. *
  147. * @param {Object} state - Redux state.
  148. * @param {Object} ownProps - Properties of component.
  149. * @private
  150. * @returns {{
  151. * _audioTrack: Track,
  152. * _largeVideo: Object,
  153. * _videoTrack: Track
  154. * }}
  155. */
  156. function _mapStateToProps(state, ownProps) {
  157. // We need read-only access to the state of features/large-video so that the
  158. // filmstrip doesn't render the video of the participant who is rendered on
  159. // the stage i.e. as a large video.
  160. const largeVideo = state['features/large-video'];
  161. const tracks = state['features/base/tracks'];
  162. const id = ownProps.participant.id;
  163. const audioTrack
  164. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
  165. const videoTrack
  166. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
  167. return {
  168. _audioTrack: audioTrack,
  169. _largeVideo: largeVideo,
  170. _videoTrack: videoTrack
  171. };
  172. }
  173. export default connect(_mapStateToProps)(Thumbnail);