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.

ParticipantView.native.js 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import React, { Component } from 'react';
  2. import { Text, View } from 'react-native';
  3. import {
  4. isParticipantConnectionStatusActive,
  5. isParticipantConnectionStatusInactive,
  6. isTrackStreamingStatusActive,
  7. isTrackStreamingStatusInactive
  8. } from '../../../connection-indicator/functions';
  9. import { SharedVideo } from '../../../shared-video/components/native';
  10. import Avatar from '../../avatar/components/Avatar';
  11. import { getSourceNameSignalingFeatureFlag } from '../../config/functions.any';
  12. import { translate } from '../../i18n/functions';
  13. import VideoTrack from '../../media/components/native/VideoTrack';
  14. import { shouldRenderVideoTrack } from '../../media/functions';
  15. import { Container } from '../../react';
  16. import { connect, toState } from '../../redux/functions';
  17. import { TestHint } from '../../testing/components';
  18. import { getVideoTrackByParticipant } from '../../tracks/functions';
  19. import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions';
  20. import styles from './styles';
  21. /**
  22. * The type of the React {@link Component} props of {@link ParticipantView}.
  23. */
  24. type Props = {
  25. /**
  26. * Whether the connection is inactive or not.
  27. *
  28. * @private
  29. */
  30. _isConnectionInactive: boolean,
  31. /**
  32. * Whether the participant is a shared video participant.
  33. */
  34. _isSharedVideoParticipant: boolean,
  35. /**
  36. * The name of the participant which this component represents.
  37. *
  38. * @private
  39. */
  40. _participantName: string,
  41. /**
  42. * True if the video should be rendered, false otherwise.
  43. */
  44. _renderVideo: boolean,
  45. /**
  46. * The video Track of the participant with {@link #participantId}.
  47. */
  48. _videoTrack: Object,
  49. /**
  50. * The avatar size.
  51. */
  52. avatarSize: number,
  53. /**
  54. * Whether video should be disabled for his view.
  55. */
  56. disableVideo: ?boolean,
  57. /**
  58. * Callback to invoke when the {@code ParticipantView} is clicked/pressed.
  59. */
  60. onPress: Function,
  61. /**
  62. * The ID of the participant (to be) depicted by {@link ParticipantView}.
  63. *
  64. * @public
  65. */
  66. participantId: string,
  67. /**
  68. * The style, if any, to apply to {@link ParticipantView} in addition to its
  69. * default style.
  70. */
  71. style: Object,
  72. /**
  73. * The function to translate human-readable text.
  74. */
  75. t: Function,
  76. /**
  77. * The test hint id which can be used to locate the {@code ParticipantView}
  78. * on the jitsi-meet-torture side. If not provided, the
  79. * {@code participantId} with the following format will be used:
  80. * {@code `org.jitsi.meet.Participant#${participantId}`}.
  81. */
  82. testHintId: ?string,
  83. /**
  84. * Indicates if the connectivity info label should be shown, if appropriate.
  85. * It will be shown in case the connection is interrupted.
  86. */
  87. useConnectivityInfoLabel: boolean,
  88. /**
  89. * The z-order of the {@link Video} of {@link ParticipantView} in the
  90. * stacking space of all {@code Video}s. For more details, refer to the
  91. * {@code zOrder} property of the {@code Video} class for React Native.
  92. */
  93. zOrder: number,
  94. /**
  95. * Indicates whether zooming (pinch to zoom and/or drag) is enabled.
  96. */
  97. zoomEnabled: boolean
  98. };
  99. /**
  100. * Implements a React Component which depicts a specific participant's avatar
  101. * and video.
  102. *
  103. * @augments Component
  104. */
  105. class ParticipantView extends Component<Props> {
  106. /**
  107. * Renders the inactive connection status label.
  108. *
  109. * @private
  110. * @returns {ReactElement}
  111. */
  112. _renderInactiveConnectionInfo() {
  113. const {
  114. avatarSize,
  115. _participantName: displayName,
  116. t
  117. } = this.props;
  118. // XXX Consider splitting this component into 2: one for the large view
  119. // and one for the thumbnail. Some of these don't apply to both.
  120. const containerStyle = {
  121. ...styles.connectionInfoContainer,
  122. width: avatarSize * 1.5
  123. };
  124. return (
  125. <View
  126. pointerEvents = 'box-none'
  127. style = { containerStyle }>
  128. <Text style = { styles.connectionInfoText }>
  129. { t('connection.LOW_BANDWIDTH', { displayName }) }
  130. </Text>
  131. </View>
  132. );
  133. }
  134. /**
  135. * Implements React's {@link Component#render()}.
  136. *
  137. * @inheritdoc
  138. * @returns {ReactElement}
  139. */
  140. render() {
  141. const {
  142. _isConnectionInactive,
  143. _isSharedVideoParticipant,
  144. _renderVideo: renderVideo,
  145. _videoTrack: videoTrack,
  146. disableVideo,
  147. onPress
  148. } = this.props;
  149. const testHintId
  150. = this.props.testHintId
  151. ? this.props.testHintId
  152. : `org.jitsi.meet.Participant#${this.props.participantId}`;
  153. const renderSharedVideo = _isSharedVideoParticipant && !disableVideo;
  154. return (
  155. <Container
  156. onClick = { renderVideo || renderSharedVideo ? undefined : onPress }
  157. style = {{
  158. ...styles.participantView,
  159. ...this.props.style
  160. }}
  161. touchFeedback = { false }>
  162. <TestHint
  163. id = { testHintId }
  164. onPress = { renderSharedVideo ? undefined : onPress }
  165. value = '' />
  166. { renderSharedVideo && <SharedVideo /> }
  167. { renderVideo
  168. && <VideoTrack
  169. onPress = { onPress }
  170. videoTrack = { videoTrack }
  171. waitForVideoStarted = { false }
  172. zOrder = { this.props.zOrder }
  173. zoomEnabled = { this.props.zoomEnabled } /> }
  174. { !renderSharedVideo && !renderVideo
  175. && <View style = { styles.avatarContainer }>
  176. <Avatar
  177. participantId = { this.props.participantId }
  178. size = { this.props.avatarSize } />
  179. </View> }
  180. { _isConnectionInactive && this.props.useConnectivityInfoLabel
  181. && this._renderInactiveConnectionInfo() }
  182. </Container>
  183. );
  184. }
  185. }
  186. /**
  187. * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
  188. * props.
  189. *
  190. * @param {Object} state - The redux state.
  191. * @param {Object} ownProps - The React {@code Component} props passed to the
  192. * associated (instance of) {@code ParticipantView}.
  193. * @private
  194. * @returns {Props}
  195. */
  196. function _mapStateToProps(state, ownProps) {
  197. const { disableVideo, participantId } = ownProps;
  198. const participant = getParticipantById(state, participantId);
  199. const videoTrack = getVideoTrackByParticipant(state, participant);
  200. return {
  201. _isConnectionInactive: getSourceNameSignalingFeatureFlag(state)
  202. ? isTrackStreamingStatusInactive(videoTrack) : isParticipantConnectionStatusInactive(participant),
  203. _isSharedVideoParticipant: isSharedVideoParticipant(participant),
  204. _participantName: getParticipantDisplayName(state, participantId),
  205. _renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
  206. _videoTrack: videoTrack
  207. };
  208. }
  209. /**
  210. * Returns true if the video of the participant should be rendered.
  211. *
  212. * @param {Object|Function} stateful - Object or function that can be resolved
  213. * to the Redux state.
  214. * @param {string} id - The ID of the participant.
  215. * @returns {boolean}
  216. */
  217. function shouldRenderParticipantVideo(stateful, id) {
  218. const state = toState(stateful);
  219. const participant = getParticipantById(state, id);
  220. if (!participant) {
  221. return false;
  222. }
  223. /* First check if we have an unmuted video track. */
  224. const videoTrack = getVideoTrackByParticipant(state, participant);
  225. if (!videoTrack) {
  226. return false;
  227. }
  228. if (!shouldRenderVideoTrack(videoTrack, /* waitForVideoStarted */ false)) {
  229. return false;
  230. }
  231. /* Then check if the participant connection or track streaming status is active. */
  232. if (getSourceNameSignalingFeatureFlag(state)) {
  233. // Note that this will work only if a listener is registered for the track's TrackStreamingStatus.
  234. // The associated TrackStreamingStatusImpl instance is not created or disposed when there are zero listeners.
  235. if (!videoTrack.local && !isTrackStreamingStatusActive(videoTrack)) {
  236. return false;
  237. }
  238. } else if (!isParticipantConnectionStatusActive(participant)) {
  239. return false;
  240. }
  241. /* Then check if audio-only mode is not active. */
  242. const audioOnly = state['features/base/audio-only'].enabled;
  243. if (!audioOnly) {
  244. return true;
  245. }
  246. /* Last, check if the participant is sharing their screen and they are on stage. */
  247. const remoteScreenShares = state['features/video-layout'].remoteScreenShares || [];
  248. const largeVideoParticipantId = state['features/large-video'].participantId;
  249. const participantIsInLargeVideoWithScreen
  250. = participant.id === largeVideoParticipantId && remoteScreenShares.includes(participant.id);
  251. return participantIsInLargeVideoWithScreen;
  252. }
  253. export default translate(connect(_mapStateToProps)(ParticipantView));