Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

Thumbnail.js 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { View } from 'react-native';
  4. import type { Dispatch } from 'redux';
  5. import { ColorSchemeRegistry } from '../../../base/color-scheme';
  6. import { openDialog } from '../../../base/dialog';
  7. import { Audio, MEDIA_TYPE } from '../../../base/media';
  8. import {
  9. PARTICIPANT_ROLE,
  10. ParticipantView,
  11. isLocalParticipantModerator,
  12. pinParticipant
  13. } from '../../../base/participants';
  14. import { Container } from '../../../base/react';
  15. import { connect } from '../../../base/redux';
  16. import { StyleType } from '../../../base/styles';
  17. import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
  18. import { DisplayNameLabel } from '../../../display-name';
  19. import { RemoteVideoMenu } from '../../../remote-video-menu';
  20. import AudioMutedIndicator from './AudioMutedIndicator';
  21. import DominantSpeakerIndicator from './DominantSpeakerIndicator';
  22. import ModeratorIndicator from './ModeratorIndicator';
  23. import RaisedHandIndicator from './RaisedHandIndicator';
  24. import styles, { AVATAR_SIZE } from './styles';
  25. import VideoMutedIndicator from './VideoMutedIndicator';
  26. /**
  27. * Thumbnail component's property types.
  28. */
  29. type Props = {
  30. /**
  31. * The Redux representation of the participant's audio track.
  32. */
  33. _audioTrack: Object,
  34. /**
  35. * True if the local participant is a moderator.
  36. */
  37. _isModerator: boolean,
  38. /**
  39. * The Redux representation of the state "features/large-video".
  40. */
  41. _largeVideo: Object,
  42. /**
  43. * Handles click/tap event on the thumbnail.
  44. */
  45. _onClick: ?Function,
  46. /**
  47. * Handles long press on the thumbnail.
  48. */
  49. _onShowRemoteVideoMenu: ?Function,
  50. /**
  51. * The color-schemed stylesheet of the feature.
  52. */
  53. _styles: StyleType,
  54. /**
  55. * The Redux representation of the participant's video track.
  56. */
  57. _videoTrack: Object,
  58. /**
  59. * If true, tapping on the thumbnail will not pin the participant to large
  60. * video. By default tapping does pin the participant.
  61. */
  62. disablePin?: boolean,
  63. /**
  64. * If true, there will be no color overlay (tint) on the thumbnail
  65. * indicating the participant associated with the thumbnail is displayed on
  66. * large video. By default there will be a tint.
  67. */
  68. disableTint?: boolean,
  69. /**
  70. * Invoked to trigger state changes in Redux.
  71. */
  72. dispatch: Dispatch<any>,
  73. /**
  74. * The Redux representation of the participant to display.
  75. */
  76. participant: Object,
  77. /**
  78. * Whether to display or hide the display name of the participant in the thumbnail.
  79. */
  80. renderDisplayName: ?boolean,
  81. /**
  82. * Optional styling to add or override on the Thumbnail component root.
  83. */
  84. styleOverrides?: Object
  85. };
  86. /**
  87. * React component for video thumbnail.
  88. *
  89. * @extends Component
  90. */
  91. class Thumbnail extends Component<Props> {
  92. /**
  93. * Implements React's {@link Component#render()}.
  94. *
  95. * @inheritdoc
  96. * @returns {ReactElement}
  97. */
  98. render() {
  99. const {
  100. _audioTrack: audioTrack,
  101. _isModerator,
  102. _largeVideo: largeVideo,
  103. _onClick,
  104. _onShowRemoteVideoMenu,
  105. _styles,
  106. _videoTrack: videoTrack,
  107. disablePin,
  108. disableTint,
  109. participant,
  110. renderDisplayName
  111. } = this.props;
  112. // We don't render audio in any of the following:
  113. // 1. The audio (source) is muted. There's no practical reason (that we
  114. // know of, anyway) why we'd want to render it given that it's
  115. // silence (& not even comfort noise).
  116. // 2. The audio is local. If we were to render local audio, the local
  117. // participants would be hearing themselves.
  118. const audioMuted = !audioTrack || audioTrack.muted;
  119. const renderAudio = !audioMuted && !audioTrack.local;
  120. const participantId = participant.id;
  121. const participantInLargeVideo
  122. = participantId === largeVideo.participantId;
  123. const videoMuted = !videoTrack || videoTrack.muted;
  124. const showRemoteVideoMenu = _isModerator && !participant.local;
  125. return (
  126. <Container
  127. onClick = { disablePin ? undefined : _onClick }
  128. onLongPress = {
  129. showRemoteVideoMenu
  130. ? _onShowRemoteVideoMenu : undefined }
  131. style = { [
  132. styles.thumbnail,
  133. participant.pinned && !disablePin
  134. ? _styles.thumbnailPinned : null,
  135. this.props.styleOverrides || null
  136. ] }
  137. touchFeedback = { false }>
  138. { renderAudio
  139. && <Audio
  140. stream
  141. = { audioTrack.jitsiTrack.getOriginalStream() } /> }
  142. <ParticipantView
  143. avatarSize = { AVATAR_SIZE }
  144. participantId = { participantId }
  145. style = { _styles.participantViewStyle }
  146. tintEnabled = { participantInLargeVideo && !disableTint }
  147. tintStyle = { _styles.activeThumbnailTint }
  148. zOrder = { 1 } />
  149. { renderDisplayName && <DisplayNameLabel participantId = { participantId } /> }
  150. { participant.role === PARTICIPANT_ROLE.MODERATOR
  151. && <View style = { styles.moderatorIndicatorContainer }>
  152. <ModeratorIndicator />
  153. </View> }
  154. <View style = { styles.thumbnailTopIndicatorContainer }>
  155. <RaisedHandIndicator participantId = { participant.id } />
  156. { participant.dominantSpeaker
  157. && <DominantSpeakerIndicator /> }
  158. </View>
  159. <Container style = { styles.thumbnailIndicatorContainer }>
  160. { audioMuted
  161. && <AudioMutedIndicator /> }
  162. { videoMuted
  163. && <VideoMutedIndicator /> }
  164. </Container>
  165. </Container>
  166. );
  167. }
  168. }
  169. /**
  170. * Maps part of redux actions to component's props.
  171. *
  172. * @param {Function} dispatch - Redux's {@code dispatch} function.
  173. * @param {Props} ownProps - The own props of the component.
  174. * @returns {{
  175. * _onClick: Function,
  176. * _onShowRemoteVideoMenu: Function
  177. * }}
  178. */
  179. function _mapDispatchToProps(dispatch: Function, ownProps): Object {
  180. return {
  181. /**
  182. * Handles click/tap event on the thumbnail.
  183. *
  184. * @protected
  185. * @returns {void}
  186. */
  187. _onClick() {
  188. const { participant } = ownProps;
  189. dispatch(
  190. pinParticipant(participant.pinned ? null : participant.id));
  191. },
  192. /**
  193. * Handles long press on the thumbnail.
  194. *
  195. * @returns {void}
  196. */
  197. _onShowRemoteVideoMenu() {
  198. const { participant } = ownProps;
  199. dispatch(openDialog(RemoteVideoMenu, {
  200. participant
  201. }));
  202. }
  203. };
  204. }
  205. /**
  206. * Function that maps parts of Redux state tree into component props.
  207. *
  208. * @param {Object} state - Redux state.
  209. * @param {Props} ownProps - Properties of component.
  210. * @returns {{
  211. * _audioTrack: Track,
  212. * _isModerator: boolean,
  213. * _largeVideo: Object,
  214. * _styles: StyleType,
  215. * _videoTrack: Track
  216. * }}
  217. */
  218. function _mapStateToProps(state, ownProps) {
  219. // We need read-only access to the state of features/large-video so that the
  220. // filmstrip doesn't render the video of the participant who is rendered on
  221. // the stage i.e. as a large video.
  222. const largeVideo = state['features/large-video'];
  223. const tracks = state['features/base/tracks'];
  224. const id = ownProps.participant.id;
  225. const audioTrack
  226. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
  227. const videoTrack
  228. = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
  229. return {
  230. _audioTrack: audioTrack,
  231. _isModerator: isLocalParticipantModerator(state),
  232. _largeVideo: largeVideo,
  233. _styles: ColorSchemeRegistry.get(state, 'Thumbnail'),
  234. _videoTrack: videoTrack
  235. };
  236. }
  237. export default connect(_mapStateToProps, _mapDispatchToProps)(Thumbnail);