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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 FlatList's viewabilityConfig.
  73. */
  74. _viewabilityConfig: Object;
  75. /**
  76. * The styles for the FlatList.
  77. */
  78. _flatListStyles: Object;
  79. /**
  80. * The styles for the content container of the FlatList.
  81. */
  82. _contentContainerStyles: 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. };
  96. this._flatListStyles = {
  97. ...styles.flatListTileView
  98. };
  99. this._contentContainerStyles = {
  100. ...styles.contentContainer
  101. };
  102. }
  103. _keyExtractor: string => string;
  104. /**
  105. * Returns a key for a passed item of the list.
  106. *
  107. * @param {string} item - The user ID.
  108. * @returns {string} - The user ID.
  109. */
  110. _keyExtractor(item) {
  111. return item;
  112. }
  113. _onViewableItemsChanged: Object => void;
  114. /**
  115. * A handler for visible items changes.
  116. *
  117. * @param {Object} data - The visible items data.
  118. * @param {Array<Object>} data.viewableItems - The visible items array.
  119. * @returns {void}
  120. */
  121. _onViewableItemsChanged({ viewableItems = [] }: { viewableItems: Array<Object> }) {
  122. const indexArray = viewableItems.map(i => i.index);
  123. // We need to shift the start index of the remoteParticipants array with 1 because of the local video placed
  124. // at the beginning and in the same time we don't need to adjust the end index because the end index will not be
  125. // included.
  126. const startIndex = Math.max(Math.min(...indexArray) - 1, 0);
  127. const endIndex = Math.max(...indexArray);
  128. this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
  129. }
  130. /**
  131. * Implements React's {@link Component#render()}.
  132. *
  133. * @inheritdoc
  134. * @returns {ReactElement}
  135. */
  136. render() {
  137. const { _columns, _height, _thumbnailHeight, _width, onClick } = this.props;
  138. const participants = this._getSortedParticipants();
  139. const initialRowsToRender = Math.ceil(_height / (_thumbnailHeight + (2 * styles.thumbnail.margin)));
  140. if (this._flatListStyles.minHeight !== _height || this._flatListStyles.minWidth !== _width) {
  141. this._flatListStyles = {
  142. ...styles.flatListTileView,
  143. minHeight: _height,
  144. minWidth: _width
  145. };
  146. }
  147. if (this._contentContainerStyles.minHeight !== _height || this._contentContainerStyles.minWidth !== _width) {
  148. this._contentContainerStyles = {
  149. ...styles.contentContainer,
  150. minHeight: _height,
  151. minWidth: _width
  152. };
  153. }
  154. return (
  155. <TouchableWithoutFeedback onPress = { onClick }>
  156. <View style = { styles.flatListContainer }>
  157. <FlatList
  158. contentContainerStyle = { this._contentContainerStyles }
  159. data = { participants }
  160. horizontal = { false }
  161. initialNumToRender = { initialRowsToRender }
  162. key = { _columns }
  163. keyExtractor = { this._keyExtractor }
  164. numColumns = { _columns }
  165. onViewableItemsChanged = { this._onViewableItemsChanged }
  166. renderItem = { this._renderThumbnail }
  167. showsHorizontalScrollIndicator = { false }
  168. showsVerticalScrollIndicator = { false }
  169. style = { this._flatListStyles }
  170. viewabilityConfig = { this._viewabilityConfig }
  171. windowSize = { 2 } />
  172. </View>
  173. </TouchableWithoutFeedback>
  174. );
  175. }
  176. /**
  177. * Returns all participants with the local participant at the end.
  178. *
  179. * @private
  180. * @returns {Participant[]}
  181. */
  182. _getSortedParticipants() {
  183. const { _localParticipant, _remoteParticipants } = this.props;
  184. if (!_localParticipant) {
  185. return EMPTY_ARRAY;
  186. }
  187. return [ _localParticipant?.id, ..._remoteParticipants ];
  188. }
  189. _renderThumbnail: Object => Object;
  190. /**
  191. * Creates React Element to display each participant in a thumbnail.
  192. *
  193. * @private
  194. * @returns {ReactElement}
  195. */
  196. _renderThumbnail({ item/* , index , separators */ }) {
  197. const { _thumbnailHeight } = this.props;
  198. return (
  199. <Thumbnail
  200. disableTint = { true }
  201. height = { _thumbnailHeight }
  202. key = { item }
  203. participantID = { item }
  204. renderDisplayName = { true }
  205. tileView = { true } />)
  206. ;
  207. }
  208. }
  209. /**
  210. * Maps (parts of) the redux state to the associated {@code TileView}'s props.
  211. *
  212. * @param {Object} state - The redux state.
  213. * @private
  214. * @returns {Props}
  215. */
  216. function _mapStateToProps(state) {
  217. const responsiveUi = state['features/base/responsive-ui'];
  218. const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
  219. const { height } = tileViewDimensions.thumbnailSize;
  220. const { columns } = tileViewDimensions;
  221. return {
  222. _aspectRatio: responsiveUi.aspectRatio,
  223. _columns: columns,
  224. _height: responsiveUi.clientHeight,
  225. _localParticipant: getLocalParticipant(state),
  226. _participantCount: getParticipantCountWithFake(state),
  227. _remoteParticipants: remoteParticipants,
  228. _thumbnailHeight: height,
  229. _width: responsiveUi.clientWidth
  230. };
  231. }
  232. export default connect(_mapStateToProps)(TileView);