feat(toolbox): Adaptive toolbar on mobile

This commit is contained in:
Vlad Piersec 2021-03-22 11:02:57 +02:00 committed by vp8x8
parent 8da154b185
commit e7297714c6
5 changed files with 132 additions and 91 deletions

View File

@ -29,6 +29,7 @@ export const ColorPalette = {
overflowMenuItemUnderlay: '#EEEEEE',
red: '#D00000',
transparent: 'rgba(0, 0, 0, 0)',
toggled: 'rgba(255,255,255,.15)',
warning: 'rgb(215, 121, 118)',
white: '#FFFFFF',

View File

@ -18,6 +18,7 @@ import { RoomLockButton } from '../../../room-lock';
import { SharedVideoButton } from '../../../shared-video/components';
import { ClosedCaptionButton } from '../../../subtitles';
import { TileViewButton } from '../../../video-layout';
import { getMovableButtons } from '../../functions.native';
import HelpButton from '../HelpButton';
import MuteEveryoneButton from '../MuteEveryoneButton';
@ -48,6 +49,11 @@ type Props = {
*/
_recordingEnabled: boolean,
/**
* The width of the screen.
*/
_width: number,
/**
* Used for hiding the dialog when the selection was completed.
*/
@ -108,8 +114,9 @@ class OverflowMenu extends PureComponent<Props, State> {
* @returns {ReactElement}
*/
render() {
const { _bottomSheetStyles } = this.props;
const { _bottomSheetStyles, _width } = this.props;
const { showMore } = this.state;
const toolbarButtons = getMovableButtons(_width);
const buttonProps = {
afterClick: this._onCancel,
@ -129,15 +136,15 @@ class OverflowMenu extends PureComponent<Props, State> {
onSwipe = { this._onSwipe }
renderHeader = { this._renderMenuExpandToggle }>
<AudioRouteButton { ...buttonProps } />
<InviteButton { ...buttonProps } />
{!toolbarButtons.has('invite') && <InviteButton { ...buttonProps } />}
<AudioOnlyButton { ...buttonProps } />
<RaiseHandButton { ...buttonProps } />
{!toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
<LobbyModeButton { ...buttonProps } />
<ScreenSharingButton { ...buttonProps } />
<MoreOptionsButton { ...moreOptionsButtonProps } />
<Collapsible collapsed = { !showMore }>
<ToggleCameraButton { ...buttonProps } />
<TileViewButton { ...buttonProps } />
{!toolbarButtons.has('togglecamera') && <ToggleCameraButton { ...buttonProps } />}
{!toolbarButtons.has('tileview') && <TileViewButton { ...buttonProps } />}
<RecordButton { ...buttonProps } />
<LiveStreamButton { ...buttonProps } />
<SharedVideoButton { ...buttonProps } />
@ -244,7 +251,8 @@ class OverflowMenu extends PureComponent<Props, State> {
function _mapStateToProps(state) {
return {
_bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
_isOpen: isDialogOpen(state, OverflowMenu_)
_isOpen: isDialogOpen(state, OverflowMenu_),
_width: state['features/base/responsive-ui'].clientWidth
};
}

View File

@ -1,18 +1,22 @@
// @flow
import React, { PureComponent } from 'react';
import React from 'react';
import { SafeAreaView, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import { ChatButton } from '../../../chat';
import { isToolboxVisible } from '../../functions';
import { InviteButton } from '../../../invite';
import { TileViewButton } from '../../../video-layout';
import { isToolboxVisible, getMovableButtons } from '../../functions.native';
import AudioMuteButton from '../AudioMuteButton';
import HangupButton from '../HangupButton';
import VideoMuteButton from '../VideoMuteButton';
import OverflowMenuButton from './OverflowMenuButton';
import RaiseHandButton from './RaiseHandButton';
import ToggleCameraButton from './ToggleCameraButton';
import styles from './styles';
/**
@ -30,6 +34,11 @@ type Props = {
*/
_visible: boolean,
/**
* The width of the screen.
*/
_width: number,
/**
* The redux {@code dispatch} function.
*/
@ -37,80 +46,64 @@ type Props = {
};
/**
* Implements the conference toolbox on React Native.
* Implements the conference Toolbox on React Native.
*
* @param {Object} props - The props of the component.
* @returns {React$Element}.
*/
class Toolbox extends PureComponent<Props> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
if (!this.props._visible) {
return null;
}
function Toolbox(props: Props) {
if (!props._visible) {
return null;
}
const { _styles } = this.props;
const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
const { _styles, _width } = props;
const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
const additionalButtons = getMovableButtons(_width);
const backgroundToggledStyle = {
...toggledButtonStyles,
style: [
toggledButtonStyles.style,
_styles.backgroundToggle
]
};
return (
<View
return (
<View
pointerEvents = 'box-none'
style = { styles.toolboxContainer }>
<SafeAreaView
accessibilityRole = 'toolbar'
pointerEvents = 'box-none'
style = { styles.toolboxContainer }>
<SafeAreaView
accessibilityRole = 'toolbar'
pointerEvents = 'box-none'
style = { styles.toolbox }>
<AudioMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
<VideoMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
<ChatButton
styles = { buttonStylesBorderless }
toggledStyles = { this._getChatButtonToggledStyle(toggledButtonStyles) } />
<OverflowMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
<HangupButton
styles = { hangupButtonStyles } />
</SafeAreaView>
</View>
);
}
style = { styles.toolbox }>
<AudioMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
<VideoMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
{ additionalButtons.has('chat')
&& <ChatButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
/**
* Constructs the toggled style of the chat button. This cannot be done by
* simple style inheritance due to the size calculation done in this
* component.
*
* @param {Object} baseStyle - The base style that was originally
* calculated.
* @returns {Object | Array}
*/
_getChatButtonToggledStyle(baseStyle) {
const { _styles } = this.props;
if (Array.isArray(baseStyle.style)) {
return {
...baseStyle,
style: [
...baseStyle.style,
_styles.chatButtonOverride.toggled
]
};
}
return {
...baseStyle,
style: [
baseStyle.style,
_styles.chatButtonOverride.toggled
]
};
}
{ additionalButtons.has('raisehand')
&& <RaiseHandButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
{additionalButtons.has('tileview') && <TileViewButton styles = { buttonStylesBorderless } />}
{additionalButtons.has('invite') && <InviteButton styles = { buttonStylesBorderless } />}
{additionalButtons.has('togglecamera')
&& <ToggleCameraButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
<OverflowMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
<HangupButton
styles = { hangupButtonStyles } />
</SafeAreaView>
</View>
);
}
/**
@ -125,7 +118,8 @@ class Toolbox extends PureComponent<Props> {
function _mapStateToProps(state: Object): Object {
return {
_styles: ColorSchemeRegistry.get(state, 'Toolbox'),
_visible: isToolboxVisible(state)
_visible: isToolboxVisible(state),
_width: state['features/base/responsive-ui'].clientWidth
};
}

View File

@ -17,10 +17,8 @@ const toolbarButton = {
flexDirection: 'row',
height: BUTTON_SIZE,
justifyContent: 'center',
// XXX We probably tested BoxModel.margin and discovered it to be too small
// for our taste.
marginHorizontal: 7,
marginHorizontal: 6,
marginTop: 6,
width: BUTTON_SIZE
};
@ -65,7 +63,8 @@ const styles = {
toolbox: {
alignItems: 'center',
backgroundColor: ColorPalette.darkBackground,
borderRadius: 3,
borderTopLeftRadius: 3,
borderTopRightRadius: 3,
flexDirection: 'row',
flexGrow: 0,
justifyContent: 'space-between',
@ -108,14 +107,8 @@ ColorSchemeRegistry.register('Toolbox', {
}
},
/**
* Overrides to the standard styles that we apply to the chat button, as
* that behaves slightly differently to other buttons.
*/
chatButtonOverride: {
toggled: {
backgroundColor: ColorPalette.blue
}
backgroundToggle: {
backgroundColor: ColorPalette.toggled
},
hangupButtonStyles: {

View File

@ -5,6 +5,51 @@ import { TOOLBOX_ALWAYS_VISIBLE, getFeatureFlag, TOOLBOX_ENABLED } from '../base
import { toState } from '../base/redux';
import { isLocalVideoTrackDesktop } from '../base/tracks';
const WIDTH = {
FIT_9_ICONS: 560,
FIT_8_ICONS: 500,
FIT_7_ICONS: 440,
FIT_6_ICONS: 380
};
/**
* Returns a set of the buttons that are shown in the toolbar
* but removed from the overflow menu, based on the width of the screen.
*
* @param {number} width - The width of the screen.
* @returns {Set}
*/
export function getMovableButtons(width: number): Set<string> {
let buttons = [];
switch (true) {
case width >= WIDTH.FIT_9_ICONS: {
buttons = [ 'togglecamera', 'chat', 'invite', 'raisehand', 'tileview' ];
break;
}
case width >= WIDTH.FIT_8_ICONS: {
buttons = [ 'chat', 'invite', 'raisehand', 'tileview' ];
break;
}
case width >= WIDTH.FIT_7_ICONS: {
buttons = [ 'chat', 'raisehand', 'invite' ];
break;
}
case width >= WIDTH.FIT_6_ICONS: {
buttons = [ 'chat', 'raisehand' ];
break;
}
default: {
buttons = [ 'chat' ];
}
}
return new Set(buttons);
}
/**
* Returns true if the toolbox is visible.
*