diff --git a/react/features/conference/components/Conference.native.js b/react/features/conference/components/Conference.native.js index 095a71d5b..c26b90d2b 100644 --- a/react/features/conference/components/Conference.native.js +++ b/react/features/conference/components/Conference.native.js @@ -129,7 +129,8 @@ class Conference extends Component { this._onHardwareBackPress); } - // Show the toolbox if we are the only participant. + // Show the toolbox if we are the only participant; otherwise, the whole + // UI looks too unpopulated the LargeVideo visible. const { _participantCount, _setToolboxVisible } = this.props; _participantCount === 1 && _setToolboxVisible(true); diff --git a/react/features/toolbox/components/native/Toolbox.js b/react/features/toolbox/components/native/Toolbox.js index d7dcf523b..04dc70405 100644 --- a/react/features/toolbox/components/native/Toolbox.js +++ b/react/features/toolbox/components/native/Toolbox.js @@ -22,7 +22,8 @@ import styles, { import VideoMuteButton from '../VideoMuteButton'; /** - * The number of buttons to render in {@link Toolbox}. + * The number of buttons other than {@link HangupButton} to render in + * {@link Toolbox}. * * @private * @type number @@ -85,25 +86,96 @@ class Toolbox extends Component { } /** - * Renders the toolbar. It will only be rendered if the {@code Toolbox} is - * visible and we have already calculated the available width. This avoids - * a weird effect in which the toolbar is displayed and then changes size. + * Implements React's {@link Component#render()}. * + * @inheritdoc * @returns {ReactElement} */ - _renderToolbar() { - const { _visible } = this.props; + render() { + const toolboxStyle + = isNarrowAspectRatio(this) + ? styles.toolboxNarrow + : styles.toolboxWide; + + return ( + + { this._renderToolbar() } + + ); + } + + /** + * Calculates how large our toolbar buttons can be, given the available + * width. In the future we might want to have a size threshold, and once + * it's passed a completely different style could be used, akin to the web. + * + * @private + * @returns {number} + */ + _calculateButtonSize() { const { width } = this.state; - if (!_visible || !width) { - return null; + if (width <= 0) { + // We don't know how much space is allocated to the toolbar yet. + return width; } + const hangupButtonSize = styles.hangupButton.width; + const { style } = toolbarButtonStyles; + let buttonSize + = (width + + // Account for HangupButton without its margin which is not + // included in _BUTTON_COUNT: + - hangupButtonSize + + // Account for the horizontal margins of all buttons: + - ((_BUTTON_COUNT + 1) * style.marginHorizontal * 2)) + / _BUTTON_COUNT; + + // Well, don't return a non-positive button size. + if (buttonSize <= 0) { + buttonSize = style.width; + } + + // The button should be at most _BUTTON_SIZE_FACTOR of the hangup + // button's size. + buttonSize + = Math.min(buttonSize, hangupButtonSize * _BUTTON_SIZE_FACTOR); + + // Make sure it's an even number. + return 2 * Math.round(buttonSize / 2); + } + + _onLayout: (Object) => void; + + /** + * Handles the "on layout" View's event and stores the width as state. + * + * @param {Object} event - The "on layout" event object/structure passed + * by react-native. + * @private + * @returns {void} + */ + _onLayout({ nativeEvent: { layout: { width } } }) { + this.setState({ width }); + } + + /** + * Renders the toolbar. In order to avoid a weird visual effect in which the + * toolbar is (visually) rendered and then visibly changes its size, it is + * rendered only after we've figured out the width available to the toolbar. + * + * @returns {React$Node} + */ + _renderToolbar() { + const buttonSize = this._calculateButtonSize(); let buttonStyles = toolbarButtonStyles; let toggledButtonStyles = toolbarToggledButtonStyles; - const buttonSize = this._calculateButtonSize(); - if (buttonSize > 0) { const extraButtonStyle = { borderRadius: buttonSize / 2, @@ -111,14 +183,26 @@ class Toolbox extends Component { width: buttonSize }; - buttonStyles = { - ...buttonStyles, - style: [ buttonStyles.style, extraButtonStyle ] - }; - toggledButtonStyles = { - ...toggledButtonStyles, - style: [ toggledButtonStyles.style, extraButtonStyle ] - }; + // XXX The following width equality checks attempt to minimize + // unnecessary objects and possibly re-renders. + if (buttonStyles.style.width !== extraButtonStyle.width) { + buttonStyles = { + ...buttonStyles, + style: [ buttonStyles.style, extraButtonStyle ] + }; + } + if (toggledButtonStyles.style.width !== extraButtonStyle.width) { + toggledButtonStyles = { + ...toggledButtonStyles, + style: [ toggledButtonStyles.style, extraButtonStyle ] + }; + } + } else { + // XXX In order to avoid a weird visual effect in which the toolbar + // is (visually) rendered and then visibly changes its size, it is + // rendered only after we've figured out the width available to the + // toolbar. + return null; } return ( @@ -139,71 +223,6 @@ class Toolbox extends Component { ); } - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const toolboxStyle - = isNarrowAspectRatio(this) - ? styles.toolboxNarrow - : styles.toolboxWide; - const { _visible } = this.props; - - return ( - - { this._renderToolbar() } - - ); - } - - /** - * Calculates how large our toolbar buttons can be, given the available - * width. In the future we might want to have a size threshold, and once - * it's passed a completely different style could be used, akin to the web. - * - * @private - * @returns {number} - */ - _calculateButtonSize() { - const { width } = this.state; - const hangupButtonSize = styles.hangupButton.width; - - let buttonSize - = (width - - hangupButtonSize - - (_BUTTON_COUNT - * styles.toolbarButton.marginHorizontal * 2)) - / _BUTTON_COUNT; - - // Make sure it's an even number. - buttonSize = 2 * Math.round(buttonSize / 2); - - // The button should be at most 80% of the hangup button's size. - return Math.min( - buttonSize, - hangupButtonSize * _BUTTON_SIZE_FACTOR); - } - - _onLayout: (Object) => void; - - /** - * Handles the "on layout" View's event and stores the width as state. - * - * @param {Object} event - The "on layout" event object/structure passed - * by react-native. - * @private - * @returns {void} - */ - _onLayout({ nativeEvent: { layout: { width } } }) { - this.setState({ width }); - } } /** diff --git a/react/features/toolbox/components/native/styles.js b/react/features/toolbox/components/native/styles.js index b0e488529..2cde59b65 100644 --- a/react/features/toolbox/components/native/styles.js +++ b/react/features/toolbox/components/native/styles.js @@ -15,6 +15,9 @@ const toolbarButton = { flexDirection: 'row', height: 40, justifyContent: 'center', + + // XXX We probably tested BoxModel.margin and discovered it to be too small + // for our taste. marginHorizontal: 7, opacity: 0.7, width: 40 @@ -181,7 +184,7 @@ const overflowMenuStyles = createStyleSheet({ flex: 1, fontSize: 16, marginLeft: 32, - opacity: 0.87 + opacity: 0.90 } });