123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- // @flow
-
- import React, { Component } from 'react';
- import {
- ScrollView,
- TouchableWithoutFeedback,
- View
- } from 'react-native';
- import type { Dispatch } from 'redux';
-
- import {
- getNearestReceiverVideoQualityLevel,
- setMaxReceiverVideoQuality
- } from '../../../base/conference';
- import { connect } from '../../../base/redux';
- import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
-
- import Thumbnail from './Thumbnail';
- import styles from './styles';
-
- /**
- * The type of the React {@link Component} props of {@link TileView}.
- */
- type Props = {
-
- /**
- * Application's aspect ratio.
- */
- _aspectRatio: Symbol,
-
- /**
- * Application's viewport height.
- */
- _height: number,
-
- /**
- * The participants in the conference.
- */
- _participants: Array<Object>,
-
- /**
- * Application's viewport height.
- */
- _width: number,
-
- /**
- * Invoked to update the receiver video quality.
- */
- dispatch: Dispatch<any>,
-
- /**
- * Callback to invoke when tile view is tapped.
- */
- onClick: Function
- };
-
- /**
- * The margin for each side of the tile view. Taken away from the available
- * height and width for the tile container to display in.
- *
- * @private
- * @type {number}
- */
- const MARGIN = 10;
-
- /**
- * The aspect ratio the tiles should display in.
- *
- * @private
- * @type {number}
- */
- const TILE_ASPECT_RATIO = 1;
-
- /**
- * Implements a React {@link Component} which displays thumbnails in a two
- * dimensional grid.
- *
- * @extends Component
- */
- class TileView extends Component<Props> {
- /**
- * Implements React's {@link Component#componentDidMount}.
- *
- * @inheritdoc
- */
- componentDidMount() {
- this._updateReceiverQuality();
- }
-
- /**
- * Implements React's {@link Component#componentDidUpdate}.
- *
- * @inheritdoc
- */
- componentDidUpdate() {
- this._updateReceiverQuality();
- }
-
- /**
- * Implements React's {@link Component#render()}.
- *
- * @inheritdoc
- * @returns {ReactElement}
- */
- render() {
- const { _height, _width, onClick } = this.props;
- const rowElements = this._groupIntoRows(this._renderThumbnails(), this._getColumnCount());
-
- return (
- <ScrollView
- style = {{
- ...styles.tileView,
- height: _height,
- width: _width
- }}>
- <TouchableWithoutFeedback onPress = { onClick }>
- <View
- style = {{
- ...styles.tileViewRows,
- minHeight: _height,
- minWidth: _width
- }}>
- { rowElements }
- </View>
- </TouchableWithoutFeedback>
- </ScrollView>
- );
- }
-
- /**
- * Returns how many columns should be displayed for tile view.
- *
- * @returns {number}
- * @private
- */
- _getColumnCount() {
- const participantCount = this.props._participants.length;
-
- // For narrow view, tiles should stack on top of each other for a lonely
- // call and a 1:1 call. Otherwise tiles should be grouped into rows of
- // two.
- if (this.props._aspectRatio === ASPECT_RATIO_NARROW) {
- return participantCount >= 3 ? 2 : 1;
- }
-
- if (participantCount === 4) {
- // In wide view, a four person call should display as a 2x2 grid.
- return 2;
- }
-
- return Math.min(3, participantCount);
- }
-
- /**
- * Returns all participants with the local participant at the end.
- *
- * @private
- * @returns {Participant[]}
- */
- _getSortedParticipants() {
- const participants = [];
- let localParticipant;
-
- for (const participant of this.props._participants) {
- if (participant.local) {
- localParticipant = participant;
- } else {
- participants.push(participant);
- }
- }
-
- localParticipant && participants.push(localParticipant);
-
- return participants;
- }
-
- /**
- * Calculate the height and width for the tiles.
- *
- * @private
- * @returns {Object}
- */
- _getTileDimensions() {
- const { _height, _participants, _width } = this.props;
- const columns = this._getColumnCount();
- const participantCount = _participants.length;
- const heightToUse = _height - (MARGIN * 2);
- const widthToUse = _width - (MARGIN * 2);
- let tileWidth;
-
- // If there is going to be at least two rows, ensure that at least two
- // rows display fully on screen.
- if (participantCount / columns > 1) {
- tileWidth = Math.min(widthToUse / columns, heightToUse / 2);
- } else {
- tileWidth = Math.min(widthToUse / columns, heightToUse);
- }
-
- return {
- height: tileWidth / TILE_ASPECT_RATIO,
- width: tileWidth
- };
- }
-
- /**
- * Splits a list of thumbnails into React Elements with a maximum of
- * {@link rowLength} thumbnails in each.
- *
- * @param {Array} thumbnails - The list of thumbnails that should be split
- * into separate row groupings.
- * @param {number} rowLength - How many thumbnails should be in each row.
- * @private
- * @returns {ReactElement[]}
- */
- _groupIntoRows(thumbnails, rowLength) {
- const rowElements = [];
-
- for (let i = 0; i < thumbnails.length; i++) {
- if (i % rowLength === 0) {
- const thumbnailsInRow = thumbnails.slice(i, i + rowLength);
-
- rowElements.push(
- <View
- key = { rowElements.length }
- style = { styles.tileViewRow }>
- { thumbnailsInRow }
- </View>
- );
- }
- }
-
- return rowElements;
- }
-
- /**
- * Creates React Elements to display each participant in a thumbnail. Each
- * tile will be.
- *
- * @private
- * @returns {ReactElement[]}
- */
- _renderThumbnails() {
- const styleOverrides = {
- aspectRatio: TILE_ASPECT_RATIO,
- flex: 0,
- height: this._getTileDimensions().height,
- width: null
- };
-
- return this._getSortedParticipants()
- .map(participant => (
- <Thumbnail
- disableTint = { true }
- key = { participant.id }
- participant = { participant }
- renderDisplayName = { true }
- styleOverrides = { styleOverrides }
- tileView = { true } />));
- }
-
- /**
- * Sets the receiver video quality based on the dimensions of the thumbnails
- * that are displayed.
- *
- * @private
- * @returns {void}
- */
- _updateReceiverQuality() {
- const { height } = this._getTileDimensions();
- const qualityLevel = getNearestReceiverVideoQualityLevel(height);
-
- this.props.dispatch(setMaxReceiverVideoQuality(qualityLevel));
- }
- }
-
- /**
- * Maps (parts of) the redux state to the associated {@code TileView}'s props.
- *
- * @param {Object} state - The redux state.
- * @private
- * @returns {Props}
- */
- function _mapStateToProps(state) {
- const responsiveUi = state['features/base/responsive-ui'];
-
- return {
- _aspectRatio: responsiveUi.aspectRatio,
- _height: responsiveUi.clientHeight,
- _participants: state['features/base/participants'],
- _width: responsiveUi.clientWidth
- };
- }
-
- export default connect(_mapStateToProps)(TileView);
|