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 4.9KB

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