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.

TileView.js 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // @flow
  2. import React, { PureComponent } from 'react';
  3. import {
  4. FlatList,
  5. TouchableWithoutFeedback,
  6. View
  7. } from 'react-native';
  8. import type { Dispatch } from 'redux';
  9. import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants';
  10. import { connect } from '../../../base/redux';
  11. import { setVisibleRemoteParticipants } from '../../actions.web';
  12. import Thumbnail from './Thumbnail';
  13. import styles from './styles';
  14. /**
  15. * The type of the React {@link Component} props of {@link TileView}.
  16. */
  17. type Props = {
  18. /**
  19. * Application's aspect ratio.
  20. */
  21. _aspectRatio: Symbol,
  22. /**
  23. * The number of columns.
  24. */
  25. _columns: number,
  26. /**
  27. * Application's viewport height.
  28. */
  29. _height: number,
  30. /**
  31. * The local participant.
  32. */
  33. _localParticipant: Object,
  34. /**
  35. * The number of participants in the conference.
  36. */
  37. _participantCount: number,
  38. /**
  39. * An array with the IDs of the remote participants in the conference.
  40. */
  41. _remoteParticipants: Array<string>,
  42. /**
  43. * The thumbnail height.
  44. */
  45. _thumbnailHeight: number,
  46. /**
  47. * Application's viewport height.
  48. */
  49. _width: number,
  50. /**
  51. * Invoked to update the receiver video quality.
  52. */
  53. dispatch: Dispatch<any>,
  54. /**
  55. * Callback to invoke when tile view is tapped.
  56. */
  57. onClick: Function
  58. };
  59. /**
  60. * An empty array. The purpose of the constant is to use the same reference every time we need an empty array.
  61. * This will prevent unnecessary re-renders.
  62. */
  63. const EMPTY_ARRAY = [];
  64. /**
  65. * Implements a React {@link PureComponent} which displays thumbnails in a two
  66. * dimensional grid.
  67. *
  68. * @extends PureComponent
  69. */
  70. class TileView extends PureComponent<Props> {
  71. /**
  72. * The styles for the content container of the FlatList.
  73. */
  74. _contentContainerStyles: Object;
  75. /**
  76. * The styles for the FlatList.
  77. */
  78. _flatListStyles: Object;
  79. /**
  80. * The FlatList's viewabilityConfig.
  81. */
  82. _viewabilityConfig: Object;
  83. /**
  84. * Creates new TileView component.
  85. *
  86. * @param {Props} props - The props of the component.
  87. */
  88. constructor(props: Props) {
  89. super(props);
  90. this._keyExtractor = this._keyExtractor.bind(this);
  91. this._onViewableItemsChanged = this._onViewableItemsChanged.bind(this);
  92. this._renderThumbnail = this._renderThumbnail.bind(this);
  93. this._viewabilityConfig = {
  94. itemVisiblePercentThreshold: 30,
  95. minimumViewTime: 500
  96. };
  97. this._flatListStyles = {
  98. ...styles.flatListTileView
  99. };
  100. this._contentContainerStyles = {
  101. ...styles.contentContainer
  102. };
  103. }
  104. _keyExtractor: string => string;
  105. /**
  106. * Returns a key for a passed item of the list.
  107. *
  108. * @param {string} item - The user ID.
  109. * @returns {string} - The user ID.
  110. */
  111. _keyExtractor(item) {
  112. return item;
  113. }
  114. _onViewableItemsChanged: Object => void;
  115. /**
  116. * A handler for visible items changes.
  117. *
  118. * @param {Object} data - The visible items data.
  119. * @param {Array<Object>} data.viewableItems - The visible items array.
  120. * @returns {void}
  121. */
  122. _onViewableItemsChanged({ viewableItems = [] }: { viewableItems: Array<Object> }) {
  123. const indexArray = viewableItems.map(i => i.index);
  124. // We need to shift the start index of the remoteParticipants array with 1 because of the local video placed
  125. // at the beginning and in the same time we don't need to adjust the end index because the end index will not be
  126. // included.
  127. const startIndex = Math.max(Math.min(...indexArray) - 1, 0);
  128. const endIndex = Math.max(...indexArray);
  129. this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
  130. }
  131. /**
  132. * Implements React's {@link Component#render()}.
  133. *
  134. * @inheritdoc
  135. * @returns {ReactElement}
  136. */
  137. render() {
  138. const { _columns, _height, _thumbnailHeight, _width, onClick } = this.props;
  139. const participants = this._getSortedParticipants();
  140. const initialRowsToRender = Math.ceil(_height / (_thumbnailHeight + (2 * styles.thumbnail.margin)));
  141. if (this._flatListStyles.minHeight !== _height || this._flatListStyles.minWidth !== _width) {
  142. this._flatListStyles = {
  143. ...styles.flatListTileView,
  144. minHeight: _height,
  145. minWidth: _width
  146. };
  147. }
  148. if (this._contentContainerStyles.minHeight !== _height || this._contentContainerStyles.minWidth !== _width) {
  149. this._contentContainerStyles = {
  150. ...styles.contentContainer,
  151. minHeight: _height,
  152. minWidth: _width
  153. };
  154. }
  155. return (
  156. <TouchableWithoutFeedback onPress = { onClick }>
  157. <View style = { styles.flatListContainer }>
  158. <FlatList
  159. bounces = { false }
  160. contentContainerStyle = { this._contentContainerStyles }
  161. data = { participants }
  162. horizontal = { false }
  163. initialNumToRender = { initialRowsToRender }
  164. key = { _columns }
  165. keyExtractor = { this._keyExtractor }
  166. numColumns = { _columns }
  167. onViewableItemsChanged = { this._onViewableItemsChanged }
  168. renderItem = { this._renderThumbnail }
  169. showsHorizontalScrollIndicator = { false }
  170. showsVerticalScrollIndicator = { false }
  171. style = { this._flatListStyles }
  172. viewabilityConfig = { this._viewabilityConfig }
  173. windowSize = { 2 } />
  174. </View>
  175. </TouchableWithoutFeedback>
  176. );
  177. }
  178. /**
  179. * Returns all participants with the local participant at the end.
  180. *
  181. * @private
  182. * @returns {Participant[]}
  183. */
  184. _getSortedParticipants() {
  185. const { _localParticipant, _remoteParticipants } = this.props;
  186. if (!_localParticipant) {
  187. return EMPTY_ARRAY;
  188. }
  189. return [ _localParticipant?.id, ..._remoteParticipants ];
  190. }
  191. _renderThumbnail: Object => Object;
  192. /**
  193. * Creates React Element to display each participant in a thumbnail.
  194. *
  195. * @private
  196. * @returns {ReactElement}
  197. */
  198. _renderThumbnail({ item/* , index , separators */ }) {
  199. const { _thumbnailHeight } = this.props;
  200. return (
  201. <Thumbnail
  202. disableTint = { true }
  203. height = { _thumbnailHeight }
  204. key = { item }
  205. participantID = { item }
  206. renderDisplayName = { true }
  207. tileView = { true } />)
  208. ;
  209. }
  210. }
  211. /**
  212. * Maps (parts of) the redux state to the associated {@code TileView}'s props.
  213. *
  214. * @param {Object} state - The redux state.
  215. * @private
  216. * @returns {Props}
  217. */
  218. function _mapStateToProps(state) {
  219. const responsiveUi = state['features/base/responsive-ui'];
  220. const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
  221. const { height } = tileViewDimensions.thumbnailSize;
  222. const { columns } = tileViewDimensions;
  223. return {
  224. _aspectRatio: responsiveUi.aspectRatio,
  225. _columns: columns,
  226. _height: responsiveUi.clientHeight,
  227. _localParticipant: getLocalParticipant(state),
  228. _participantCount: getParticipantCountWithFake(state),
  229. _remoteParticipants: remoteParticipants,
  230. _thumbnailHeight: height,
  231. _width: responsiveUi.clientWidth
  232. };
  233. }
  234. export default connect(_mapStateToProps)(TileView);