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

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