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.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import PropTypes from 'prop-types';
  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 {
  13. AudioMutedIndicator,
  14. DominantSpeakerIndicator,
  15. ModeratorIndicator,
  16. styles,
  17. VideoMutedIndicator
  18. } from './_';
  19. import { AVATAR_SIZE } from './styles';
  20. /**
  21. * React component for video thumbnail.
  22. *
  23. * @extends Component
  24. */
  25. class Thumbnail extends Component {
  26. /**
  27. * Thumbnail component's property types.
  28. *
  29. * @static
  30. */
  31. static propTypes = {
  32. _audioTrack: PropTypes.object,
  33. _largeVideo: PropTypes.object,
  34. _videoTrack: PropTypes.object,
  35. dispatch: PropTypes.func,
  36. participant: PropTypes.object
  37. };
  38. /**
  39. * Initializes new Video Thumbnail component.
  40. *
  41. * @param {Object} props - Component props.
  42. */
  43. constructor(props) {
  44. super(props);
  45. // Bind event handlers so they are only bound once for every instance.
  46. this._onClick = this._onClick.bind(this);
  47. }
  48. /**
  49. * Implements React's {@link Component#render()}.
  50. *
  51. * @inheritdoc
  52. * @returns {ReactElement}
  53. */
  54. render() {
  55. const audioTrack = this.props._audioTrack;
  56. const largeVideo = this.props._largeVideo;
  57. const participant = this.props.participant;
  58. const videoTrack = this.props._videoTrack;
  59. let style = styles.thumbnail;
  60. if (participant.pinned) {
  61. style = {
  62. ...style,
  63. ...styles.thumbnailPinned
  64. };
  65. }
  66. // We don't render audio in any of the following:
  67. // 1. The audio (source) is muted. There's no practical reason (that we
  68. // know of, anyway) why we'd want to render it given that it's
  69. // silence (& not even comfort noise).
  70. // 2. The audio is local. If we were to render local audio, the local
  71. // participants would be hearing themselves.
  72. const audioMuted = !audioTrack || audioTrack.muted;
  73. const renderAudio = !audioMuted && !audioTrack.local;
  74. const participantId = participant.id;
  75. const participantNotInLargeVideo
  76. = participantId !== largeVideo.participantId;
  77. const videoMuted = !videoTrack || videoTrack.muted;
  78. return (
  79. <Container
  80. onClick = { this._onClick }
  81. style = { style }>
  82. { renderAudio
  83. && <Audio
  84. stream
  85. = { audioTrack.jitsiTrack.getOriginalStream() } /> }
  86. <ParticipantView
  87. avatarSize = { AVATAR_SIZE }
  88. participantId = { participantId }
  89. showAvatar = { participantNotInLargeVideo }
  90. showVideo = { participantNotInLargeVideo }
  91. zOrder = { 1 } />
  92. { participant.role === PARTICIPANT_ROLE.MODERATOR
  93. && <ModeratorIndicator /> }
  94. { participant.dominantSpeaker
  95. && <DominantSpeakerIndicator /> }
  96. <Container style = { styles.thumbnailIndicatorContainer }>
  97. { audioMuted
  98. && <AudioMutedIndicator /> }
  99. { videoMuted
  100. && <VideoMutedIndicator /> }
  101. </Container>
  102. </Container>
  103. );
  104. }
  105. /**
  106. * Handles click/tap event on the thumbnail.
  107. *
  108. * @returns {void}
  109. */
  110. _onClick() {
  111. const { dispatch, participant } = this.props;
  112. // TODO The following currently ignores interfaceConfig.filmStripOnly.
  113. dispatch(pinParticipant(participant.pinned ? null : participant.id));
  114. }
  115. }
  116. /**
  117. * Function that maps parts of Redux state tree into component props.
  118. *
  119. * @param {Object} state - Redux state.
  120. * @param {Object} ownProps - Properties of component.
  121. * @private
  122. * @returns {{
  123. * _audioTrack: Track,
  124. * _largeVideo: Object,
  125. * _videoTrack: Track
  126. * }}
  127. */
  128. function _mapStateToProps(state, ownProps) {
  129. // We need read-only access to the state of features/large-video so that the
  130. // filmstrip doesn't render the video of the participant who is rendered on
  131. // the stage i.e. as a large video.
  132. const largeVideo = state['features/large-video'];
  133. const tracks = state['features/base/tracks'];
  134. const id = ownProps.participant.id;
  135. const audioTrack
  136. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
  137. const videoTrack
  138. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
  139. return {
  140. _audioTrack: audioTrack,
  141. _largeVideo: largeVideo,
  142. _videoTrack: videoTrack
  143. };
  144. }
  145. export default connect(_mapStateToProps)(Thumbnail);