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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. if (viewableItems[0]?.index === 0) {
  124. // Skip the local thumbnail.
  125. viewableItems.shift();
  126. }
  127. if (viewableItems.length === 0) {
  128. // User might be fast-scrolling, it will stabilize.
  129. return;
  130. }
  131. // We are off by one in the remote participants array.
  132. const startIndex = viewableItems[0].index - 1;
  133. const endIndex = viewableItems[viewableItems.length - 1].index - 1;
  134. this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
  135. }
  136. /**
  137. * Implements React's {@link Component#render()}.
  138. *
  139. * @inheritdoc
  140. * @returns {ReactElement}
  141. */
  142. render() {
  143. const { _columns, _height, _thumbnailHeight, _width, onClick } = this.props;
  144. const participants = this._getSortedParticipants();
  145. const initialRowsToRender = Math.ceil(_height / (_thumbnailHeight + (2 * styles.thumbnail.margin)));
  146. if (this._flatListStyles.minHeight !== _height || this._flatListStyles.minWidth !== _width) {
  147. this._flatListStyles = {
  148. ...styles.flatListTileView,
  149. minHeight: _height,
  150. minWidth: _width
  151. };
  152. }
  153. if (this._contentContainerStyles.minHeight !== _height || this._contentContainerStyles.minWidth !== _width) {
  154. this._contentContainerStyles = {
  155. ...styles.contentContainer,
  156. minHeight: _height,
  157. minWidth: _width
  158. };
  159. }
  160. return (
  161. <TouchableWithoutFeedback onPress = { onClick }>
  162. <View style = { styles.flatListContainer }>
  163. <FlatList
  164. bounces = { false }
  165. contentContainerStyle = { this._contentContainerStyles }
  166. data = { participants }
  167. horizontal = { false }
  168. initialNumToRender = { initialRowsToRender }
  169. key = { _columns }
  170. keyExtractor = { this._keyExtractor }
  171. numColumns = { _columns }
  172. onViewableItemsChanged = { this._onViewableItemsChanged }
  173. renderItem = { this._renderThumbnail }
  174. showsHorizontalScrollIndicator = { false }
  175. showsVerticalScrollIndicator = { false }
  176. style = { this._flatListStyles }
  177. viewabilityConfig = { this._viewabilityConfig }
  178. windowSize = { 2 } />
  179. </View>
  180. </TouchableWithoutFeedback>
  181. );
  182. }
  183. /**
  184. * Returns all participants with the local participant at the end.
  185. *
  186. * @private
  187. * @returns {Participant[]}
  188. */
  189. _getSortedParticipants() {
  190. const { _localParticipant, _remoteParticipants } = this.props;
  191. if (!_localParticipant) {
  192. return EMPTY_ARRAY;
  193. }
  194. return [ _localParticipant?.id, ..._remoteParticipants ];
  195. }
  196. _renderThumbnail: Object => Object;
  197. /**
  198. * Creates React Element to display each participant in a thumbnail.
  199. *
  200. * @private
  201. * @returns {ReactElement}
  202. */
  203. _renderThumbnail({ item/* , index , separators */ }) {
  204. const { _thumbnailHeight } = this.props;
  205. return (
  206. <Thumbnail
  207. disableTint = { true }
  208. height = { _thumbnailHeight }
  209. key = { item }
  210. participantID = { item }
  211. renderDisplayName = { true }
  212. tileView = { true } />)
  213. ;
  214. }
  215. }
  216. /**
  217. * Maps (parts of) the redux state to the associated {@code TileView}'s props.
  218. *
  219. * @param {Object} state - The redux state.
  220. * @private
  221. * @returns {Props}
  222. */
  223. function _mapStateToProps(state) {
  224. const responsiveUi = state['features/base/responsive-ui'];
  225. const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
  226. const { height } = tileViewDimensions.thumbnailSize;
  227. const { columns } = tileViewDimensions;
  228. return {
  229. _aspectRatio: responsiveUi.aspectRatio,
  230. _columns: columns,
  231. _height: responsiveUi.clientHeight,
  232. _localParticipant: getLocalParticipant(state),
  233. _participantCount: getParticipantCountWithFake(state),
  234. _remoteParticipants: remoteParticipants,
  235. _thumbnailHeight: height,
  236. _width: responsiveUi.clientWidth
  237. };
  238. }
  239. export default connect(_mapStateToProps)(TileView);