Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ParticipantView.native.tsx 9.2KB

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