From d7407525221804e673ef7ccdb14da728704890ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 2 Jun 2020 11:03:17 +0200 Subject: [PATCH] rn,responsive-ui: refactor dimensions detection Use a dimensions detecting root component. The Dimensions module does not measure the app's view size, but the Window, which may not be the same, for example on iOS when PiP is used. Also refactor the aspect ratio wrap component since it can be taken directly from the store. Last, remove the use of DimensionsDetector on LargeVideo and TileView since they occupy the full-screen anyway. Fixes PiP mode on iOS. --- react/features/app/components/App.native.js | 36 ++++- .../components/AspectRatioAware.js | 70 --------- .../components/DimensionsDetector.native.js | 8 +- .../base/responsive-ui/components/index.js | 1 - .../base/responsive-ui/components/styles.js | 14 -- .../base/responsive-ui/middleware.native.js | 66 +------- react/features/base/responsive-ui/reducer.js | 1 - .../components/native/Conference.js | 93 ++---------- .../conference/components/native/Labels.js | 26 ++-- .../conference/components/web/Labels.js | 3 +- .../filmstrip/components/native/Filmstrip.js | 82 +++------- .../filmstrip/components/native/TileView.js | 143 ++++++------------ .../components/LargeVideo.native.js | 82 +++++----- 13 files changed, 184 insertions(+), 441 deletions(-) delete mode 100644 react/features/base/responsive-ui/components/AspectRatioAware.js delete mode 100644 react/features/base/responsive-ui/components/styles.js diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index 393d6a56108e..54aa783cc415 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -9,7 +9,7 @@ import { DialogContainer } from '../../base/dialog'; import { CALL_INTEGRATION_ENABLED, updateFlags } from '../../base/flags'; import '../../base/jwt'; import { Platform } from '../../base/react'; -import '../../base/responsive-ui'; +import { DimensionsDetector, clientResized } from '../../base/responsive-ui'; import { updateSettings } from '../../base/settings'; import '../../google-api'; import '../../mobile/audio-mode'; @@ -78,6 +78,9 @@ export class App extends AbstractApp { // This will effectively kill the app. In accord with the Web, do not // kill the app. this._maybeDisableExceptionsManager(); + + // Bind event handler so it is only bound once per instance. + this._onDimensionsChanged = this._onDimensionsChanged.bind(this); } /** @@ -107,6 +110,21 @@ export class App extends AbstractApp { }); } + /** + * Overrides the parent method to inject {@link DimensionsDetector} as + * the top most component. + * + * @override + */ + _createMainElement(component, props) { + return ( + + { super._createMainElement(component, props) } + + ); + } + /** * Attempts to disable the use of React Native * {@link ExceptionsManager#handleException} on platforms and in @@ -144,6 +162,22 @@ export class App extends AbstractApp { } } + _onDimensionsChanged: (width: number, height: number) => void; + + /** + * Updates the known available size for the app to occupy. + * + * @param {number} width - The component's current width. + * @param {number} height - The component's current height. + * @private + * @returns {void} + */ + _onDimensionsChanged(width: number, height: number) { + const { dispatch } = this.state.store; + + dispatch(clientResized(width, height)); + } + /** * Renders the platform specific dialog container. * diff --git a/react/features/base/responsive-ui/components/AspectRatioAware.js b/react/features/base/responsive-ui/components/AspectRatioAware.js deleted file mode 100644 index 312b8c948417..000000000000 --- a/react/features/base/responsive-ui/components/AspectRatioAware.js +++ /dev/null @@ -1,70 +0,0 @@ -// @flow - -import React, { Component } from 'react'; - -import { connect } from '../../redux'; -import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from '../constants'; - -/** - * The type of the React {@code Component} props of {@link AspectRatioAware}. - */ -type Props = { - aspectRatio: ASPECT_RATIO_NARROW | ASPECT_RATIO_WIDE -}; - -/** - * Determines whether a specific React {@code Component} decorated into an - * {@link AspectRatioAware} has {@link ASPECT_RATIO_NARROW} as the value of its - * {@code aspectRatio} React prop. - * - * @param {AspectRatioAware} component - An {@link AspectRatioAware} which may - * have an {@code aspectRatio} React prop. - * @returns {boolean} - */ -export function isNarrowAspectRatio(component: React$Component<*>) { - return component.props.aspectRatio === ASPECT_RATIO_NARROW; -} - -/** - * Decorates a specific React {@code Component} class into an - * {@link AspectRatioAware} which provides the React prop {@code aspectRatio} - * updated on each redux state change. - * - * @param {Class} WrappedComponent - A React {@code Component} - * class to be wrapped. - * @returns {AspectRatioAwareWrapper} - */ -export function makeAspectRatioAware( - WrappedComponent: Class> -): Class> { - /** - * Renders {@code WrappedComponent} with the React prop {@code aspectRatio}. - */ - class AspectRatioAware extends Component { - /** - * Implement's React render method to wrap the nested component. - * - * @returns {React$Element} - */ - render(): React$Element<*> { - return ; - } - } - - return connect(_mapStateToProps)(AspectRatioAware); -} - -/** - * Maps (parts of) the redux state to {@link AspectRatioAware} props. - * - * @param {Object} state - The whole redux state. - * @private - * @returns {{ - * aspectRatio: Symbol - * }} - */ -function _mapStateToProps(state) { - return { - aspectRatio: state['features/base/responsive-ui'].aspectRatio - }; -} diff --git a/react/features/base/responsive-ui/components/DimensionsDetector.native.js b/react/features/base/responsive-ui/components/DimensionsDetector.native.js index 06f7b44c77a1..48518badc595 100644 --- a/react/features/base/responsive-ui/components/DimensionsDetector.native.js +++ b/react/features/base/responsive-ui/components/DimensionsDetector.native.js @@ -1,13 +1,9 @@ // @flow import React, { PureComponent } from 'react'; -import { View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; -import styles from './styles'; -/** - * AspectRatioDetector component's property types. - */ type Props = { /** @@ -64,7 +60,7 @@ export default class DimensionsDetector extends PureComponent { return ( + style = { StyleSheet.absoluteFillObject } > { this.props.children } ); diff --git a/react/features/base/responsive-ui/components/index.js b/react/features/base/responsive-ui/components/index.js index d7dac5883055..5c87f64436ff 100644 --- a/react/features/base/responsive-ui/components/index.js +++ b/react/features/base/responsive-ui/components/index.js @@ -1,2 +1 @@ -export * from './AspectRatioAware'; export { default as DimensionsDetector } from './DimensionsDetector'; diff --git a/react/features/base/responsive-ui/components/styles.js b/react/features/base/responsive-ui/components/styles.js deleted file mode 100644 index 4d63f6b0164d..000000000000 --- a/react/features/base/responsive-ui/components/styles.js +++ /dev/null @@ -1,14 +0,0 @@ -import { createStyleSheet } from '../../styles'; - -/** - * The styles of the feature base/responsive-ui. - */ -export default createStyleSheet({ - /** - * The style of {@link DimensionsDetector} used on react-native. - */ - dimensionsDetector: { - alignSelf: 'stretch', - flex: 1 - } -}); diff --git a/react/features/base/responsive-ui/middleware.native.js b/react/features/base/responsive-ui/middleware.native.js index 5f3ea0b4b0f2..a7b9bbd25f57 100644 --- a/react/features/base/responsive-ui/middleware.native.js +++ b/react/features/base/responsive-ui/middleware.native.js @@ -1,16 +1,10 @@ // @flow -import { Dimensions } from 'react-native'; - -import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app'; import { MiddlewareRegistry } from '../../base/redux'; +import { CLIENT_RESIZED } from './actionTypes'; import { setAspectRatio, setReducedUI } from './actions'; -/** - * Dimensions change handler. - */ -let handler; /** * Middleware that handles widnow dimension changes and updates the aspect ratio and @@ -19,65 +13,19 @@ let handler; * @param {Store} store - The redux store. * @returns {Function} */ -MiddlewareRegistry.register(store => next => action => { +MiddlewareRegistry.register(({ dispatch }) => next => action => { const result = next(action); switch (action.type) { - case APP_WILL_UNMOUNT: { - _appWillUnmount(); + case CLIENT_RESIZED: { + const { clientWidth: width, clientHeight: height } = action; + + dispatch(setAspectRatio(width, height)); + dispatch(setReducedUI(width, height)); break; } - case APP_WILL_MOUNT: - _appWillMount(store); - break; } return result; }); - -/** - * Notifies this feature that the action {@link APP_WILL_MOUNT} is being - * dispatched within a specific redux {@code store}. - * - * @param {Store} store - The redux store in which the specified {@code action} - * is being dispatched. - * @private - * @returns {void} - */ -function _appWillMount(store) { - handler = dim => { - _onDimensionsChange(dim, store); - }; - - Dimensions.addEventListener('change', handler); -} - -/** - * Notifies this feature that the action {@link APP_WILL_UNMOUNT} is being - * dispatched within a specific redux {@code store}. - * - * @private - * @returns {void} - */ -function _appWillUnmount() { - Dimensions.removeEventListener('change', handler); - - handler = undefined; -} - -/** - * Handles window dimension changes. - * - * @param {Object} dimensions - The new dimensions. - * @param {Store} store - The redux store. - * @private - * @returns {void} - */ -function _onDimensionsChange(dimensions, store) { - const { width, height } = dimensions.window; - const { dispatch } = store; - - dispatch(setAspectRatio(width, height)); - dispatch(setReducedUI(width, height)); -} diff --git a/react/features/base/responsive-ui/reducer.js b/react/features/base/responsive-ui/reducer.js index 59f2d5ed28cc..619e5acf8793 100644 --- a/react/features/base/responsive-ui/reducer.js +++ b/react/features/base/responsive-ui/reducer.js @@ -23,7 +23,6 @@ const DEFAULT_STATE = { ReducerRegistry.register('features/base/responsive-ui', (state = DEFAULT_STATE, action) => { switch (action.type) { case CLIENT_RESIZED: { - return { ...state, clientWidth: action.clientWidth, diff --git a/react/features/conference/components/native/Conference.js b/react/features/conference/components/native/Conference.js index 869c2edd5f0b..9488d8df5b28 100644 --- a/react/features/conference/components/native/Conference.js +++ b/react/features/conference/components/native/Conference.js @@ -8,10 +8,7 @@ import { appNavigate } from '../../../app'; import { PIP_ENABLED, getFeatureFlag } from '../../../base/flags'; import { Container, LoadingIndicator, TintedView } from '../../../base/react'; import { connect } from '../../../base/redux'; -import { - isNarrowAspectRatio, - makeAspectRatioAware -} from '../../../base/responsive-ui'; +import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants'; import { TestConnectionInfo } from '../../../base/testing'; import { ConferenceNotification, isCalendarEnabled } from '../../../calendar-sync'; import { Chat } from '../../../chat'; @@ -45,10 +42,13 @@ import styles, { NAVBAR_GRADIENT_COLORS } from './styles'; */ type Props = AbstractProps & { + /** + * Application's aspect ratio. + */ + _aspectRatio: Symbol, + /** * Wherther the calendar feature is enabled or not. - * - * @private */ _calendarEnabled: boolean, @@ -57,15 +57,11 @@ type Props = AbstractProps & { * conference which includes establishing the XMPP connection and then * joining the room. If truthy, then an activity/loading indicator will be * rendered. - * - * @private */ _connecting: boolean, /** * Set to {@code true} when the filmstrip is currently visible. - * - * @private */ _filmstripVisible: boolean, @@ -76,34 +72,17 @@ type Props = AbstractProps & { /** * Whether Picture-in-Picture is enabled. - * - * @private */ _pictureInPictureEnabled: boolean, /** * The indicator which determines whether the UI is reduced (to accommodate * smaller display areas). - * - * @private */ _reducedUI: boolean, - /** - * The handler which dispatches the (redux) action {@link setToolboxVisible} - * to show/hide the {@link Toolbox}. - * - * @param {boolean} visible - {@code true} to show the {@code Toolbox} or - * {@code false} to hide it. - * @private - * @returns {void} - */ - _setToolboxVisible: Function, - /** * The indicator which determines whether the Toolbox is visible. - * - * @private */ _toolboxVisible: boolean, @@ -249,6 +228,7 @@ class Conference extends AbstractConference { */ _renderContent() { const { + _aspectRatio, _connecting, _filmstripVisible, _largeVideoParticipantId, @@ -257,7 +237,8 @@ class Conference extends AbstractConference { _toolboxVisible } = this.props; const showGradient = _toolboxVisible; - const applyGradientStretching = _filmstripVisible && isNarrowAspectRatio(this) && !_shouldDisplayTileView; + const applyGradientStretching + = _filmstripVisible && _aspectRatio === ASPECT_RATIO_NARROW && !_shouldDisplayTileView; if (_reducedUI) { return this._renderContentForReducedUi(); @@ -393,7 +374,9 @@ class Conference extends AbstractConference { // flex layout. The only option that seemed to limit the notification's // size was explicit 'width' value which is not better than the margin // added here. - if (this.props._filmstripVisible && !isNarrowAspectRatio(this)) { + const { _aspectRatio, _filmstripVisible } = this.props; + + if (_filmstripVisible && _aspectRatio !== ASPECT_RATIO_NARROW) { notificationsStyle.marginRight = FILMSTRIP_SIZE; } @@ -433,7 +416,7 @@ function _mapStateToProps(state) { joining, leaving } = state['features/base/conference']; - const { reducedUI } = state['features/base/responsive-ui']; + const { aspectRatio, reducedUI } = state['features/base/responsive-ui']; // XXX There is a window of time between the successful establishment of the // XMPP connection and the subsequent commencement of joining the MUC during @@ -449,61 +432,15 @@ function _mapStateToProps(state) { return { ...abstractMapStateToProps(state), - - /** - * Wherther the calendar feature is enabled or not. - * - * @private - * @type {boolean} - */ + _aspectRatio: aspectRatio, _calendarEnabled: isCalendarEnabled(state), - - /** - * The indicator which determines that we are still connecting to the - * conference which includes establishing the XMPP connection and then - * joining the room. If truthy, then an activity/loading indicator will - * be rendered. - * - * @private - * @type {boolean} - */ _connecting: Boolean(connecting_), - - /** - * Is {@code true} when the filmstrip is currently visible. - */ _filmstripVisible: isFilmstripVisible(state), - - /** - * The ID of the participant currently on stage. - */ _largeVideoParticipantId: state['features/large-video'].participantId, - - /** - * Whether Picture-in-Picture is enabled. - * - * @private - * @type {boolean} - */ _pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED), - - /** - * The indicator which determines whether the UI is reduced (to - * accommodate smaller display areas). - * - * @private - * @type {boolean} - */ _reducedUI: reducedUI, - - /** - * The indicator which determines whether the Toolbox is visible. - * - * @private - * @type {boolean} - */ _toolboxVisible: isToolboxVisible(state) }; } -export default connect(_mapStateToProps)(makeAspectRatioAware(Conference)); +export default connect(_mapStateToProps)(Conference); diff --git a/react/features/conference/components/native/Labels.js b/react/features/conference/components/native/Labels.js index 9054f246fe30..e6dcbb5b33d0 100644 --- a/react/features/conference/components/native/Labels.js +++ b/react/features/conference/components/native/Labels.js @@ -5,10 +5,7 @@ import { TouchableOpacity, View } from 'react-native'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { connect } from '../../../base/redux'; -import { - isNarrowAspectRatio, - makeAspectRatioAware -} from '../../../base/responsive-ui'; +import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui/constants'; import { RecordingExpandedLabel } from '../../../recording'; @@ -29,14 +26,19 @@ import styles from './styles'; type Props = AbstractLabelsProps & { /** - * Function to translate i18n labels. + * Application's aspect ratio. */ - t: Function, + _aspectRatio: Symbol, /** * True if the labels should be visible, false otherwise. */ - _visible: boolean + _visible: boolean, + + /** + * Function to translate i18n labels. + */ + t: Function }; type State = { @@ -149,12 +151,13 @@ class Labels extends AbstractLabels { * @inheritdoc */ render() { - if (!this.props._visible) { + const { _aspectRatio, _filmstripVisible, _visible } = this.props; + + if (!_visible) { return null; } - const wide = !isNarrowAspectRatio(this); - const { _filmstripVisible } = this.props; + const wide = _aspectRatio === ASPECT_RATIO_WIDE; return ( { function _mapStateToProps(state) { return { ..._abstractMapStateToProps(state), + _aspectRatio: state['features/base/responsive-ui'].aspectRatio, _visible: !shouldDisplayNotifications(state) }; } -export default connect(_mapStateToProps)(makeAspectRatioAware(Labels)); +export default connect(_mapStateToProps)(Labels); diff --git a/react/features/conference/components/web/Labels.js b/react/features/conference/components/web/Labels.js index b372d57ff063..6cb6dc5db03e 100644 --- a/react/features/conference/components/web/Labels.js +++ b/react/features/conference/components/web/Labels.js @@ -41,8 +41,7 @@ class Labels extends AbstractLabels { */ static getDerivedStateFromProps(props: Props, prevState: State) { return { - filmstripBecomingVisible: !prevState.filmstripBecomingVisible - && props._filmstripVisible + filmstripBecomingVisible: !prevState.filmstripBecomingVisible && props._filmstripVisible }; } diff --git a/react/features/filmstrip/components/native/Filmstrip.js b/react/features/filmstrip/components/native/Filmstrip.js index 288cd4a2bd85..a4589c312bf1 100644 --- a/react/features/filmstrip/components/native/Filmstrip.js +++ b/react/features/filmstrip/components/native/Filmstrip.js @@ -5,10 +5,7 @@ import { ScrollView } from 'react-native'; import { Container, Platform } from '../../../base/react'; import { connect } from '../../../base/redux'; -import { - isNarrowAspectRatio, - makeAspectRatioAware -} from '../../../base/responsive-ui'; +import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants'; import { isFilmstripVisible } from '../../functions'; import LocalThumbnail from './LocalThumbnail'; @@ -20,24 +17,23 @@ import styles from './styles'; */ type Props = { + /** + * Application's aspect ratio. + */ + _aspectRatio: Symbol, + /** * The indicator which determines whether the filmstrip is enabled. - * - * @private */ _enabled: boolean, /** * The participants in the conference. - * - * @private */ _participants: Array, /** * The indicator which determines whether the filmstrip is visible. - * - * @private */ _visible: boolean }; @@ -90,40 +86,36 @@ class Filmstrip extends Component { * @returns {ReactElement} */ render() { - if (!this.props._enabled) { + const { _aspectRatio, _enabled, _participants, _visible } = this.props; + + if (!_enabled) { return null; } - const isNarrowAspectRatio_ = isNarrowAspectRatio(this); - const filmstripStyle - = isNarrowAspectRatio_ - ? styles.filmstripNarrow - : styles.filmstripWide; + const isNarrowAspectRatio = _aspectRatio === ASPECT_RATIO_NARROW; + const filmstripStyle = isNarrowAspectRatio ? styles.filmstripNarrow : styles.filmstripWide; return ( + visible = { _visible }> { this._separateLocalThumbnail - && !isNarrowAspectRatio_ + && !isNarrowAspectRatio && } { - !this._separateLocalThumbnail - && !isNarrowAspectRatio_ + !this._separateLocalThumbnail && !isNarrowAspectRatio && } { - this._sort( - this.props._participants, - isNarrowAspectRatio_) + this._sort(_participants, isNarrowAspectRatio) .map(p => ( { } { - !this._separateLocalThumbnail - && isNarrowAspectRatio_ + !this._separateLocalThumbnail && isNarrowAspectRatio && } { - this._separateLocalThumbnail - && isNarrowAspectRatio_ + this._separateLocalThumbnail && isNarrowAspectRatio && } @@ -150,13 +140,13 @@ class Filmstrip extends Component { * * @param {Participant[]} participants - The array of {@code Participant}s * to sort in display order. - * @param {boolean} isNarrowAspectRatio_ - Indicates if the aspect ratio is + * @param {boolean} isNarrowAspectRatio - Indicates if the aspect ratio is * wide or narrow. * @private * @returns {Participant[]} A new array containing the elements of the * specified {@code participants} array sorted in display order. */ - _sort(participants, isNarrowAspectRatio_) { + _sort(participants, isNarrowAspectRatio) { // XXX Array.prototype.sort() is not appropriate because (1) it operates // in place and (2) it is not necessarily stable. @@ -164,7 +154,7 @@ class Filmstrip extends Component { ...participants ]; - if (isNarrowAspectRatio_) { + if (isNarrowAspectRatio) { // When the narrow aspect ratio is used, we want to have the remote // participants from right to left with the newest added/joined to // the leftmost side. The local participant is the leftmost item. @@ -180,42 +170,18 @@ class Filmstrip extends Component { * * @param {Object} state - The redux state. * @private - * @returns {{ - * _participants: Participant[], - * _visible: boolean - * }} + * @returns {Props} */ function _mapStateToProps(state) { const participants = state['features/base/participants']; const { enabled } = state['features/filmstrip']; return { - /** - * The indicator which determines whether the filmstrip is enabled. - * - * @private - * @type {boolean} - */ + _aspectRatio: state['features/base/responsive-ui'].aspectRatio, _enabled: enabled, - - /** - * The remote participants in the conference. - * - * @private - * @type {Participant[]} - */ _participants: participants.filter(p => !p.local), - - /** - * The indicator which determines whether the filmstrip is visible. The - * mobile/react-native Filmstrip is visible when there are at least 2 - * participants in the conference (including the local one). - * - * @private - * @type {boolean} - */ _visible: isFilmstripVisible(state) }; } -export default connect(_mapStateToProps)(makeAspectRatioAware(Filmstrip)); +export default connect(_mapStateToProps)(Filmstrip); diff --git a/react/features/filmstrip/components/native/TileView.js b/react/features/filmstrip/components/native/TileView.js index 4171bf67b837..91c23e0b11f0 100644 --- a/react/features/filmstrip/components/native/TileView.js +++ b/react/features/filmstrip/components/native/TileView.js @@ -13,11 +13,7 @@ import { setMaxReceiverVideoQuality } from '../../../base/conference'; import { connect } from '../../../base/redux'; -import { - DimensionsDetector, - isNarrowAspectRatio, - makeAspectRatioAware -} from '../../../base/responsive-ui'; +import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants'; import Thumbnail from './Thumbnail'; import styles from './styles'; @@ -28,35 +24,34 @@ import styles from './styles'; type Props = { /** - * The participants in the conference. + * Application's aspect ratio. */ - _participants: Array, + _aspectRatio: Symbol, /** - * Invoked to update the receiver video quality. + * Application's viewport height. */ - dispatch: Dispatch, + _height: number, /** - * Callback to invoke when tile view is tapped. + * The participants in the conference. */ - onClick: Function -}; + _participants: Array, -/** - * The type of the React {@link Component} state of {@link TileView}. - */ -type State = { + /** + * Application's viewport height. + */ + _width: number, /** - * The available width for {@link TileView} to occupy. + * Invoked to update the receiver video quality. */ - height: number, + dispatch: Dispatch, /** - * The available height for {@link TileView} to occupy. + * Callback to invoke when tile view is tapped. */ - width: number + onClick: Function }; /** @@ -82,25 +77,7 @@ const TILE_ASPECT_RATIO = 1; * * @extends Component */ -class TileView extends Component { - state = { - height: 0, - width: 0 - }; - - /** - * Initializes a new {@code TileView} instance. - * - * @param {Object} props - The read-only properties with which the new - * instance is to be initialized. - */ - constructor(props: Props) { - super(props); - - // Bind event handler so it is only bound once per instance. - this._onDimensionsChanged = this._onDimensionsChanged.bind(this); - } - +class TileView extends Component { /** * Implements React's {@link Component#componentDidMount}. * @@ -126,32 +103,27 @@ class TileView extends Component { * @returns {ReactElement} */ render() { - const { onClick } = this.props; - const { height, width } = this.state; - const rowElements = this._groupIntoRows( - this._renderThumbnails(), this._getColumnCount()); + const { _height, _width, onClick } = this.props; + const rowElements = this._groupIntoRows(this._renderThumbnails(), this._getColumnCount()); return ( - - - - - { rowElements } - - - - + + + + { rowElements } + + + ); } @@ -167,7 +139,7 @@ class TileView extends Component { // 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 (isNarrowAspectRatio(this)) { + if (this.props._aspectRatio === ASPECT_RATIO_NARROW) { return participantCount >= 3 ? 2 : 1; } @@ -209,19 +181,17 @@ class TileView extends Component { * @returns {Object} */ _getTileDimensions() { - const { _participants } = this.props; - const { height, width } = this.state; + const { _height, _participants, _width } = this.props; const columns = this._getColumnCount(); const participantCount = _participants.length; - const heightToUse = height - (MARGIN * 2); - const widthToUse = width - (MARGIN * 2); + 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); + tileWidth = Math.min(widthToUse / columns, heightToUse / 2); } else { tileWidth = Math.min(widthToUse / columns, heightToUse); } @@ -247,8 +217,7 @@ class TileView extends Component { for (let i = 0; i < thumbnails.length; i++) { if (i % rowLength === 0) { - const thumbnailsInRow - = thumbnails.slice(i, i + rowLength); + const thumbnailsInRow = thumbnails.slice(i, i + rowLength); rowElements.push( { return rowElements; } - _onDimensionsChanged: (width: number, height: number) => void; - - /** - * Updates the known available state for {@link TileView} to occupy. - * - * @param {number} width - The component's current width. - * @param {number} height - The component's current height. - * @private - * @returns {void} - */ - _onDimensionsChanged(width: number, height: number) { - this.setState({ - height, - width - }); - } - /** * Creates React Elements to display each participant in a thumbnail. Each * tile will be. @@ -326,14 +278,17 @@ class TileView extends Component { * * @param {Object} state - The redux state. * @private - * @returns {{ - * _participants: Participant[] - * }} + * @returns {Props} */ function _mapStateToProps(state) { + const responsiveUi = state['features/base/responsive-ui']; + return { - _participants: state['features/base/participants'] + _aspectRatio: responsiveUi.aspectRatio, + _height: responsiveUi.clientHeight, + _participants: state['features/base/participants'], + _width: responsiveUi.clientWidth }; } -export default connect(_mapStateToProps)(makeAspectRatioAware(TileView)); +export default connect(_mapStateToProps)(TileView); diff --git a/react/features/large-video/components/LargeVideo.native.js b/react/features/large-video/components/LargeVideo.native.js index 35fc9673c6ee..5377089b99c9 100644 --- a/react/features/large-video/components/LargeVideo.native.js +++ b/react/features/large-video/components/LargeVideo.native.js @@ -1,11 +1,10 @@ // @flow -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { ColorSchemeRegistry } from '../../base/color-scheme'; import { ParticipantView } from '../../base/participants'; import { connect } from '../../base/redux'; -import { DimensionsDetector } from '../../base/responsive-ui'; import { StyleType } from '../../base/styles'; import { AVATAR_SIZE } from './styles'; @@ -15,6 +14,11 @@ import { AVATAR_SIZE } from './styles'; */ type Props = { + /** + * Application's viewport height. + */ + _height: number, + /** * The ID of the participant (to be) depicted by LargeVideo. * @@ -27,6 +31,11 @@ type Props = { */ _styles: StyleType, + /** + * Application's viewport height. + */ + _width: number, + /** * Callback to invoke when the {@code LargeVideo} is clicked/pressed. */ @@ -62,50 +71,33 @@ const DEFAULT_STATE = { * * @extends Component */ -class LargeVideo extends Component { +class LargeVideo extends PureComponent { state = { ...DEFAULT_STATE }; - /** Initializes a new {@code LargeVideo} instance. - * - * @param {Object} props - The read-only properties with which the new - * instance is to be initialized. - */ - constructor(props: Props) { - super(props); - - // Bind event handlers so they are only bound once per instance. - this._onDimensionsChanged = this._onDimensionsChanged.bind(this); - } - - _onDimensionsChanged: (width: number, height: number) => void; - /** - * Handle this component's dimension changes. In case we deem it's too + * Handles dimension changes. In case we deem it's too * small, the connectivity indicator won't be rendered and the avatar * will occupy the entirety of the available screen state. * - * @param {number} width - The component's current width. - * @param {number} height - The component's current height. - * @private - * @returns {void} + * @inheritdoc */ - _onDimensionsChanged(width: number, height: number) { + static getDerivedStateFromProps(props: Props) { + const { _height, _width } = props; + // Get the size, rounded to the nearest even number. - const size = 2 * Math.round(Math.min(height, width) / 2); - let nextState; + const size = 2 * Math.round(Math.min(_height, _width) / 2); if (size < AVATAR_SIZE * 1.5) { - nextState = { + return { avatarSize: size - 15, // Leave some margin. useConnectivityInfoLabel: false }; - } else { - nextState = DEFAULT_STATE; } - this.setState(nextState); + return DEFAULT_STATE; + } /** @@ -126,18 +118,15 @@ class LargeVideo extends Component { } = this.props; return ( - - - + ); } } @@ -147,15 +136,16 @@ class LargeVideo extends Component { * * @param {Object} state - Redux state. * @private - * @returns {{ - * _participantId: string, - * _styles: StyleType - * }} + * @returns {Props} */ function _mapStateToProps(state) { + const { clientHeight: height, clientWidth: width } = state['features/base/responsive-ui']; + return { + _height: height, _participantId: state['features/large-video'].participantId, - _styles: ColorSchemeRegistry.get(state, 'LargeVideo') + _styles: ColorSchemeRegistry.get(state, 'LargeVideo'), + _width: width }; }