Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

TileView.tsx 8.8KB

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