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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { Text, View } from 'react-native';
  4. import FastImage from 'react-native-fast-image';
  5. import { connect } from 'react-redux';
  6. import { translate } from '../../i18n';
  7. import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
  8. import {
  9. MEDIA_TYPE,
  10. VideoTrack
  11. } from '../../media';
  12. import { Container, TintedView } from '../../react';
  13. import { TestHint } from '../../testing/components';
  14. import { getTrackByMediaTypeAndParticipant } from '../../tracks';
  15. import Avatar from './Avatar';
  16. import {
  17. getAvatarURL,
  18. getParticipantById,
  19. getParticipantDisplayName,
  20. shouldRenderParticipantVideo
  21. } from '../functions';
  22. import styles from './styles';
  23. /**
  24. * The type of the React {@link Component} props of {@link ParticipantView}.
  25. */
  26. type Props = {
  27. /**
  28. * The source (e.g. URI, URL) of the avatar image of the participant with
  29. * {@link #participantId}.
  30. *
  31. * @private
  32. */
  33. _avatar: string,
  34. /**
  35. * The connection status of the participant. Her video will only be rendered
  36. * if the connection status is 'active'; otherwise, the avatar will be
  37. * rendered. If undefined, 'active' is presumed.
  38. *
  39. * @private
  40. */
  41. _connectionStatus: string,
  42. /**
  43. * The name of the participant which this component represents.
  44. *
  45. * @private
  46. */
  47. _participantName: string,
  48. /**
  49. * True if the video should be rendered, false otherwise.
  50. */
  51. _renderVideo: boolean,
  52. /**
  53. * The video Track of the participant with {@link #participantId}.
  54. */
  55. _videoTrack: Object,
  56. /**
  57. * The avatar size.
  58. */
  59. avatarSize: number,
  60. /**
  61. * Callback to invoke when the {@code ParticipantView} is clicked/pressed.
  62. */
  63. onPress: Function,
  64. /**
  65. * The ID of the participant (to be) depicted by {@link ParticipantView}.
  66. *
  67. * @public
  68. */
  69. participantId: string,
  70. /**
  71. * The style, if any, to apply to {@link ParticipantView} in addition to its
  72. * default style.
  73. */
  74. style: Object,
  75. /**
  76. * The function to translate human-readable text.
  77. */
  78. t: Function,
  79. /**
  80. * If true, a tinting will be applied to the view, regardless of video or
  81. * avatar is rendered.
  82. */
  83. tintEnabled: boolean,
  84. /**
  85. * The test hint id which can be used to locate the {@code ParticipantView}
  86. * on the jitsi-meet-torture side. If not provided, the
  87. * {@code participantId} with the following format will be used:
  88. * {@code `org.jitsi.meet.Participant#${participantId}`}
  89. */
  90. testHintId: ?string,
  91. /**
  92. * Indicates if the connectivity info label should be shown, if appropriate.
  93. * It will be shown in case the connection is interrupted.
  94. */
  95. useConnectivityInfoLabel: boolean,
  96. /**
  97. * The z-order of the {@link Video} of {@link ParticipantView} in the
  98. * stacking space of all {@code Video}s. For more details, refer to the
  99. * {@code zOrder} property of the {@code Video} class for React Native.
  100. */
  101. zOrder: number,
  102. /**
  103. * Indicates whether zooming (pinch to zoom and/or drag) is enabled.
  104. */
  105. zoomEnabled: boolean
  106. };
  107. /**
  108. * Implements a React Component which depicts a specific participant's avatar
  109. * and video.
  110. *
  111. * @extends Component
  112. */
  113. class ParticipantView extends Component<Props> {
  114. /**
  115. * Renders the connection status label, if appropriate.
  116. *
  117. * @param {string} connectionStatus - The status of the participant's
  118. * connection.
  119. * @private
  120. * @returns {ReactElement|null}
  121. */
  122. _renderConnectionInfo(connectionStatus) {
  123. let messageKey;
  124. switch (connectionStatus) {
  125. case JitsiParticipantConnectionStatus.INACTIVE:
  126. messageKey = 'connection.LOW_BANDWIDTH';
  127. break;
  128. case JitsiParticipantConnectionStatus.INTERRUPTED:
  129. messageKey = 'connection.USER_CONNECTION_INTERRUPTED';
  130. break;
  131. default:
  132. return null;
  133. }
  134. const {
  135. avatarSize,
  136. _participantName: displayName,
  137. t
  138. } = this.props;
  139. // XXX Consider splitting this component into 2: one for the large view
  140. // and one for the thumbnail. Some of these don't apply to both.
  141. const containerStyle = {
  142. ...styles.connectionInfoContainer,
  143. width: avatarSize * 1.5
  144. };
  145. return (
  146. <View
  147. pointerEvents = 'box-none'
  148. style = { containerStyle }>
  149. <Text style = { styles.connectionInfoText }>
  150. { t(messageKey, { displayName }) }
  151. </Text>
  152. </View>
  153. );
  154. }
  155. /**
  156. * Implements React's {@link Component#render()}.
  157. *
  158. * @inheritdoc
  159. * @returns {ReactElement}
  160. */
  161. render() {
  162. const {
  163. onPress,
  164. _avatar: avatar,
  165. _connectionStatus: connectionStatus,
  166. _renderVideo: renderVideo,
  167. _videoTrack: videoTrack
  168. } = this.props;
  169. const waitForVideoStarted = false;
  170. // Is the avatar to be rendered?
  171. const renderAvatar = Boolean(!renderVideo && avatar);
  172. // If the connection has problems, we will "tint" the video / avatar.
  173. const useTint
  174. = connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE
  175. || this.props.tintEnabled;
  176. const testHintId
  177. = this.props.testHintId
  178. ? this.props.testHintId
  179. : `org.jitsi.meet.Participant#${this.props.participantId}`;
  180. return (
  181. <Container
  182. onClick = { renderVideo ? undefined : onPress }
  183. style = {{
  184. ...styles.participantView,
  185. ...this.props.style
  186. }}
  187. touchFeedback = { false }>
  188. <TestHint
  189. id = { testHintId }
  190. onPress = { onPress }
  191. value = '' />
  192. { renderVideo
  193. && <VideoTrack
  194. onPress = { onPress }
  195. videoTrack = { videoTrack }
  196. waitForVideoStarted = { waitForVideoStarted }
  197. zOrder = { this.props.zOrder }
  198. zoomEnabled = { this.props.zoomEnabled } /> }
  199. { renderAvatar
  200. && <Avatar
  201. size = { this.props.avatarSize }
  202. uri = { avatar } /> }
  203. { useTint
  204. // If the connection has problems, tint the video / avatar.
  205. && <TintedView /> }
  206. { this.props.useConnectivityInfoLabel
  207. && this._renderConnectionInfo(connectionStatus) }
  208. </Container>
  209. );
  210. }
  211. }
  212. /**
  213. * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
  214. * props.
  215. *
  216. * @param {Object} state - The redux state.
  217. * @param {Object} ownProps - The React {@code Component} props passed to the
  218. * associated (instance of) {@code ParticipantView}.
  219. * @private
  220. * @returns {{
  221. * _avatar: string,
  222. * _connectionStatus: string,
  223. * _participantName: string,
  224. * _renderVideo: boolean,
  225. * _videoTrack: Track
  226. * }}
  227. */
  228. function _mapStateToProps(state, ownProps) {
  229. const { participantId } = ownProps;
  230. const participant = getParticipantById(state, participantId);
  231. let avatar;
  232. let connectionStatus;
  233. let participantName;
  234. if (participant) {
  235. avatar = getAvatarURL(participant);
  236. connectionStatus = participant.connectionStatus;
  237. participantName = getParticipantDisplayName(state, participant.id);
  238. // Avatar (on React Native) now has the ability to generate an
  239. // automatically-colored default image when no URI/URL is specified or
  240. // when it fails to load. In order to make the coloring permanent(ish)
  241. // per participant, Avatar will need something permanent(ish) per
  242. // perticipant, obviously. A participant's ID is such a piece of data.
  243. // But the local participant changes her ID as she joins, leaves.
  244. // TODO @lyubomir: The participants may change their avatar URLs at
  245. // runtime which means that, if their old and new avatar URLs fail to
  246. // download, Avatar will change their automatically-generated colors.
  247. avatar || participant.local || (avatar = `#${participant.id}`);
  248. // ParticipantView knows before Avatar that an avatar URL will be used
  249. // so it's advisable to prefetch here.
  250. avatar && !avatar.startsWith('#')
  251. && FastImage.preload([ { uri: avatar } ]);
  252. }
  253. return {
  254. _avatar: avatar,
  255. _connectionStatus:
  256. connectionStatus
  257. || JitsiParticipantConnectionStatus.ACTIVE,
  258. _participantName: participantName,
  259. _renderVideo: shouldRenderParticipantVideo(state, participantId),
  260. _videoTrack:
  261. getTrackByMediaTypeAndParticipant(
  262. state['features/base/tracks'],
  263. MEDIA_TYPE.VIDEO,
  264. participantId)
  265. };
  266. }
  267. export default translate(connect(_mapStateToProps)(ParticipantView));