Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

TileView.js 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // @flow
  2. import React, { Component } from 'react';
  3. import {
  4. ScrollView,
  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 { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
  12. import { setTileViewDimensions } from '../../actions.native';
  13. import Thumbnail from './Thumbnail';
  14. import styles from './styles';
  15. /**
  16. * The type of the React {@link Component} props of {@link TileView}.
  17. */
  18. type Props = {
  19. /**
  20. * Application's aspect ratio.
  21. */
  22. _aspectRatio: Symbol,
  23. /**
  24. * Application's viewport height.
  25. */
  26. _height: number,
  27. /**
  28. * The local participant.
  29. */
  30. _localParticipant: Object,
  31. /**
  32. * The number of participants in the conference.
  33. */
  34. _participantCount: number,
  35. /**
  36. * An array with the IDs of the remote participants in the conference.
  37. */
  38. _remoteParticipants: Array<string>,
  39. /**
  40. * Application's viewport height.
  41. */
  42. _width: number,
  43. /**
  44. * Invoked to update the receiver video quality.
  45. */
  46. dispatch: Dispatch<any>,
  47. /**
  48. * Callback to invoke when tile view is tapped.
  49. */
  50. onClick: Function
  51. };
  52. /**
  53. * The margin for each side of the tile view. Taken away from the available
  54. * height and width for the tile container to display in.
  55. *
  56. * @private
  57. * @type {number}
  58. */
  59. const MARGIN = 10;
  60. /**
  61. * The aspect ratio the tiles should display in.
  62. *
  63. * @private
  64. * @type {number}
  65. */
  66. const TILE_ASPECT_RATIO = 1;
  67. /**
  68. * Implements a React {@link Component} which displays thumbnails in a two
  69. * dimensional grid.
  70. *
  71. * @extends Component
  72. */
  73. class TileView extends Component<Props> {
  74. /**
  75. * Implements React's {@link Component#componentDidMount}.
  76. *
  77. * @inheritdoc
  78. */
  79. componentDidMount() {
  80. this._updateReceiverQuality();
  81. }
  82. /**
  83. * Implements React's {@link Component#componentDidUpdate}.
  84. *
  85. * @inheritdoc
  86. */
  87. componentDidUpdate() {
  88. this._updateReceiverQuality();
  89. }
  90. /**
  91. * Implements React's {@link Component#render()}.
  92. *
  93. * @inheritdoc
  94. * @returns {ReactElement}
  95. */
  96. render() {
  97. const { _height, _width, onClick } = this.props;
  98. const rowElements = this._groupIntoRows(this._renderThumbnails(), this._getColumnCount());
  99. return (
  100. <ScrollView
  101. style = {{
  102. ...styles.tileView,
  103. height: _height,
  104. width: _width
  105. }}>
  106. <TouchableWithoutFeedback onPress = { onClick }>
  107. <View
  108. style = {{
  109. ...styles.tileViewRows,
  110. minHeight: _height,
  111. minWidth: _width
  112. }}>
  113. { rowElements }
  114. </View>
  115. </TouchableWithoutFeedback>
  116. </ScrollView>
  117. );
  118. }
  119. /**
  120. * Returns how many columns should be displayed for tile view.
  121. *
  122. * @returns {number}
  123. * @private
  124. */
  125. _getColumnCount() {
  126. const participantCount = this.props._participantCount;
  127. // For narrow view, tiles should stack on top of each other for a lonely
  128. // call and a 1:1 call. Otherwise tiles should be grouped into rows of
  129. // two.
  130. if (this.props._aspectRatio === ASPECT_RATIO_NARROW) {
  131. return participantCount >= 3 ? 2 : 1;
  132. }
  133. if (participantCount === 4) {
  134. // In wide view, a four person call should display as a 2x2 grid.
  135. return 2;
  136. }
  137. return Math.min(3, participantCount);
  138. }
  139. /**
  140. * Returns all participants with the local participant at the end.
  141. *
  142. * @private
  143. * @returns {Participant[]}
  144. */
  145. _getSortedParticipants() {
  146. const { _localParticipant, _remoteParticipants } = this.props;
  147. const participants = [ ..._remoteParticipants ];
  148. _localParticipant && participants.push(_localParticipant.id);
  149. return participants;
  150. }
  151. /**
  152. * Calculate the height and width for the tiles.
  153. *
  154. * @private
  155. * @returns {Object}
  156. */
  157. _getTileDimensions() {
  158. const { _height, _participantCount, _width } = this.props;
  159. const columns = this._getColumnCount();
  160. const heightToUse = _height - (MARGIN * 2);
  161. const widthToUse = _width - (MARGIN * 2);
  162. let tileWidth;
  163. // If there is going to be at least two rows, ensure that at least two
  164. // rows display fully on screen.
  165. if (_participantCount / columns > 1) {
  166. tileWidth = Math.min(widthToUse / columns, heightToUse / 2);
  167. } else {
  168. tileWidth = Math.min(widthToUse / columns, heightToUse);
  169. }
  170. return {
  171. height: tileWidth / TILE_ASPECT_RATIO,
  172. width: tileWidth
  173. };
  174. }
  175. /**
  176. * Splits a list of thumbnails into React Elements with a maximum of
  177. * {@link rowLength} thumbnails in each.
  178. *
  179. * @param {Array} thumbnails - The list of thumbnails that should be split
  180. * into separate row groupings.
  181. * @param {number} rowLength - How many thumbnails should be in each row.
  182. * @private
  183. * @returns {ReactElement[]}
  184. */
  185. _groupIntoRows(thumbnails, rowLength) {
  186. const rowElements = [];
  187. for (let i = 0; i < thumbnails.length; i++) {
  188. if (i % rowLength === 0) {
  189. const thumbnailsInRow = thumbnails.slice(i, i + rowLength);
  190. rowElements.push(
  191. <View
  192. key = { rowElements.length }
  193. style = { styles.tileViewRow }>
  194. { thumbnailsInRow }
  195. </View>
  196. );
  197. }
  198. }
  199. return rowElements;
  200. }
  201. /**
  202. * Creates React Elements to display each participant in a thumbnail. Each
  203. * tile will be.
  204. *
  205. * @private
  206. * @returns {ReactElement[]}
  207. */
  208. _renderThumbnails() {
  209. const styleOverrides = {
  210. aspectRatio: TILE_ASPECT_RATIO,
  211. flex: 0,
  212. height: this._getTileDimensions().height,
  213. maxHeight: null,
  214. maxWidth: null,
  215. width: null
  216. };
  217. return this._getSortedParticipants()
  218. .map(id => (
  219. <Thumbnail
  220. disableTint = { true }
  221. key = { id }
  222. participantID = { id }
  223. renderDisplayName = { true }
  224. styleOverrides = { styleOverrides }
  225. tileView = { true } />));
  226. }
  227. /**
  228. * Sets the receiver video quality based on the dimensions of the thumbnails
  229. * that are displayed.
  230. *
  231. * @private
  232. * @returns {void}
  233. */
  234. _updateReceiverQuality() {
  235. const { height, width } = this._getTileDimensions();
  236. this.props.dispatch(setTileViewDimensions({
  237. thumbnailSize: {
  238. height,
  239. width
  240. }
  241. }));
  242. }
  243. }
  244. /**
  245. * Maps (parts of) the redux state to the associated {@code TileView}'s props.
  246. *
  247. * @param {Object} state - The redux state.
  248. * @private
  249. * @returns {Props}
  250. */
  251. function _mapStateToProps(state) {
  252. const responsiveUi = state['features/base/responsive-ui'];
  253. const { remoteParticipants } = state['features/filmstrip'];
  254. return {
  255. _aspectRatio: responsiveUi.aspectRatio,
  256. _height: responsiveUi.clientHeight,
  257. _localParticipant: getLocalParticipant(state),
  258. _participantCount: getParticipantCountWithFake(state),
  259. _remoteParticipants: remoteParticipants,
  260. _width: responsiveUi.clientWidth
  261. };
  262. }
  263. export default connect(_mapStateToProps)(TileView);