feat(toolbox/native): reorganizing buttons in the toolbox and overflow menu (#15543)

Configures what buttons can be visible inside Toolbox and OverflowMenu, based on priority and config overrides, just like web does.
This commit is contained in:
Calinteodor
2025-02-11 16:17:13 +02:00
committed by GitHub
parent a6d333c07a
commit 405af3af5f
26 changed files with 694 additions and 355 deletions

View File

@@ -40,7 +40,7 @@
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
// For testing configOverrides a room needs to be set
// builder.room = @"test0988test";
// builder.room = @"https://meet.jit.si/test0988test";
[builder setFeatureFlag:@"welcomepage.enabled" withBoolean:YES];
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];

View File

@@ -13,6 +13,7 @@ import '../mobile/react-native-sdk/middleware';
import '../mobile/watchos/middleware';
import '../share-room/middleware';
import '../shared-video/middleware';
import '../toolbox/middleware.native';
import '../whiteboard/middleware.native';
import './middlewares.any';

View File

@@ -1,3 +1,5 @@
/* eslint-disable require-jsdoc */
import { IReduxState, IStore } from '../../app/types';
import JitsiMeetJS from '../lib-jitsi-meet';
import { updateSettings } from '../settings/actions';
@@ -157,7 +159,8 @@ export function getDevicesFromURL(state: IReduxState) {
* @returns {Object} An object with the media devices split by type. The keys
* are device type and the values are arrays with devices matching the device
* type.
*/
*/
// @ts-ignore
export function groupDevicesByKind(devices: MediaDeviceInfo[]): IDevicesState['availableDevices'] {
return {
audioInput: devices.filter(device => device.kind === 'audioinput'),
@@ -172,7 +175,10 @@ export function groupDevicesByKind(devices: MediaDeviceInfo[]): IDevicesState['a
* @param {MediaDeviceInfo[]} devices - The devices to be filtered.
* @returns {MediaDeviceInfo[]} - The filtered devices.
*/
// @ts-ignore
export function filterIgnoredDevices(devices: MediaDeviceInfo[] = []) {
// @ts-ignore
const ignoredDevices: MediaDeviceInfo[] = [];
const filteredDevices = devices.filter(device => {
if (!device.label) {
@@ -201,6 +207,7 @@ export function filterIgnoredDevices(devices: MediaDeviceInfo[] = []) {
* @param {MediaDeviceInfo[]} devices2 - Array with devices to be compared.
* @returns {boolean} - True if the device arrays are different and false otherwise.
*/
// @ts-ignore
export function areDevicesDifferent(devices1: MediaDeviceInfo[] = [], devices2: MediaDeviceInfo[] = []) {
if (devices1.length !== devices2.length) {
return true;
@@ -304,6 +311,7 @@ export function getVideoDeviceIds(state: IReduxState) {
* @param {MediaDeviceInfo[]} devices - The devices.
* @returns {string}
*/
// @ts-ignore
function devicesToStr(devices?: MediaDeviceInfo[]) {
return devices?.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n');
}
@@ -315,6 +323,7 @@ function devicesToStr(devices?: MediaDeviceInfo[]) {
* @param {string} title - The title that will be printed in the log.
* @returns {void}
*/
// @ts-ignore
export function logDevices(devices: MediaDeviceInfo[], title = '') {
const deviceList = groupDevicesByKind(devices);
const audioInputs = devicesToStr(deviceList.audioInput);

View File

@@ -5,7 +5,7 @@ import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../../app/types';
import DeviceStatus from '../../../../prejoin/components/web/preview/DeviceStatus';
import { isRoomNameEnabled } from '../../../../prejoin/functions';
import { isRoomNameEnabled } from '../../../../prejoin/functions.web';
import Toolbox from '../../../../toolbox/components/web/Toolbox';
import { isButtonEnabled } from '../../../../toolbox/functions.web';
import { getConferenceName } from '../../../conference/functions';

View File

@@ -23,7 +23,7 @@ interface IState {
/**
* The id of the last read message.
*/
lastReadMessageId: string;
lastReadMessageId: string | null;
}
/**

View File

@@ -8,8 +8,8 @@ import { getLocalParticipant } from '../../../base/participants/functions';
import Platform from '../../../base/react/Platform.native';
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
import { getHideSelfView } from '../../../base/settings/functions.any';
import { isToolboxVisible } from '../../../toolbox/functions';
import { setVisibleRemoteParticipants } from '../../actions';
import { isToolboxVisible } from '../../../toolbox/functions.native';
import { setVisibleRemoteParticipants } from '../../actions.native';
import {
getFilmstripDimensions,
isFilmstripVisible,

View File

@@ -26,7 +26,7 @@ import {
setUserFilmstripWidth,
setUserIsResizing,
setVisibleRemoteParticipants
} from '../../actions';
} from '../../actions.web';
import {
ASPECT_RATIO_BREAKPOINT,
DEFAULT_FILMSTRIP_WIDTH,
@@ -39,10 +39,10 @@ import {
} from '../../constants';
import {
getVerticalViewMaxWidth,
isFilmstripDisabled,
isStageFilmstripTopPanel,
shouldRemoteVideosBeVisible
} from '../../functions';
import { isFilmstripDisabled } from '../../functions.web';
} from '../../functions.web';
import AudioTracksContainer from './AudioTracksContainer';
import Thumbnail from './Thumbnail';

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import RaiseHandButton from '../../../toolbox/components/native/RaiseHandButton';
import { shouldDisplayReactionsButtons } from '../../functions.native';
import ReactionsMenuButton from './ReactionsMenuButton';
const RaiseHandContainerButtons = (props: AbstractButtonProps) => {
const _shouldDisplayReactionsButtons = useSelector(shouldDisplayReactionsButtons);
return _shouldDisplayReactionsButtons
? <ReactionsMenuButton
{ ...props }
showRaiseHand = { true } />
: <RaiseHandButton { ...props } />;
};
export default RaiseHandContainerButtons;

View File

@@ -6,11 +6,13 @@ import { setVideoMuted } from '../base/media/actions';
import { VIDEO_MUTISM_AUTHORITY } from '../base/media/constants';
import {
SET_MAIN_TOOLBAR_BUTTONS_THRESHOLDS,
SET_TOOLBOX_ENABLED,
SET_TOOLBOX_SHIFT_UP,
SET_TOOLBOX_VISIBLE,
TOGGLE_TOOLBOX_VISIBLE
} from './actionTypes';
import { IMainToolbarButtonThresholds } from './types';
/**
* Enables/disables the toolbox.
@@ -118,3 +120,54 @@ export function setShiftUp(shiftUp: boolean) {
shiftUp
};
}
/**
* Sets the mainToolbarButtonsThresholds.
*
* @param {IMainToolbarButtonThresholds} thresholds - Thresholds for screen size and visible main toolbar buttons.
* @returns {Function}
*/
export function setMainToolbarThresholds(thresholds: IMainToolbarButtonThresholds) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { mainToolbarButtons } = getState()['features/base/config'];
if (!Array.isArray(mainToolbarButtons) || mainToolbarButtons.length === 0) {
return;
}
const mainToolbarButtonsThresholds: IMainToolbarButtonThresholds = [];
const mainToolbarButtonsLengthMap = new Map();
let orderIsChanged = false;
mainToolbarButtons.forEach(buttons => {
if (!Array.isArray(buttons) || buttons.length === 0) {
return;
}
mainToolbarButtonsLengthMap.set(buttons.length, buttons);
});
thresholds.forEach(({ width, order }) => {
let finalOrder = mainToolbarButtonsLengthMap.get(order.length);
if (finalOrder) {
orderIsChanged = true;
} else {
finalOrder = order;
}
mainToolbarButtonsThresholds.push({
order: finalOrder,
width
});
});
if (orderIsChanged) {
dispatch({
type: SET_MAIN_TOOLBAR_BUTTONS_THRESHOLDS,
mainToolbarButtonsThresholds
});
}
};
}

View File

@@ -36,7 +36,7 @@ export function setOverflowMenuVisible(_visible: boolean): any {
* text: string
* }}
*/
export function customButtonPressed(id: string, text: string) {
export function customButtonPressed(id: string, text: string | undefined) {
return {
type: CUSTOM_BUTTON_PRESSED,
id,

View File

@@ -8,16 +8,13 @@ import {
FULL_SCREEN_CHANGED,
SET_FULL_SCREEN,
SET_HANGUP_MENU_VISIBLE,
SET_MAIN_TOOLBAR_BUTTONS_THRESHOLDS,
SET_OVERFLOW_DRAWER,
SET_OVERFLOW_MENU_VISIBLE,
SET_TOOLBAR_HOVERED,
SET_TOOLBOX_TIMEOUT
} from './actionTypes';
import { setToolboxVisible } from './actions.web';
import { THRESHOLDS } from './constants';
import { getToolbarTimeout } from './functions.web';
import { IMainToolbarButtonThresholds } from './types';
export * from './actions.any';
@@ -124,56 +121,6 @@ export function setFullScreen(fullScreen: boolean) {
};
}
/**
* Sets the mainToolbarButtonsThresholds.
*
* @returns {Function}
*/
export function setMainToolbarThresholds() {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { mainToolbarButtons } = getState()['features/base/config'];
if (!mainToolbarButtons || !Array.isArray(mainToolbarButtons) || mainToolbarButtons.length === 0) {
return;
}
const mainToolbarButtonsThresholds: IMainToolbarButtonThresholds = [];
const mainToolbarButtonsLenghtMap = new Map();
let orderIsChanged = false;
mainToolbarButtons.forEach(buttons => {
if (!Array.isArray(buttons) || buttons.length === 0) {
return;
}
mainToolbarButtonsLenghtMap.set(buttons.length, buttons);
});
THRESHOLDS.forEach(({ width, order }) => {
let finalOrder = mainToolbarButtonsLenghtMap.get(order.length);
if (finalOrder) {
orderIsChanged = true;
} else {
finalOrder = order;
}
mainToolbarButtonsThresholds.push({
order: finalOrder,
width
});
});
if (orderIsChanged) {
dispatch({
type: SET_MAIN_TOOLBAR_BUTTONS_THRESHOLDS,
mainToolbarButtonsThresholds
});
}
};
}
/**
* Shows the toolbox for specified timeout.
*

View File

@@ -10,12 +10,12 @@ import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import styles from './styles';
interface IProps extends AbstractButtonProps {
export interface ICustomOptionButton extends AbstractButtonProps {
backgroundColor?: string;
icon: any;
id?: string;
isToolboxButton?: boolean;
text?: string;
text: string;
}
/**
@@ -23,7 +23,7 @@ interface IProps extends AbstractButtonProps {
*
* @returns {Component}
*/
class CustomOptionButton extends AbstractButton<IProps> {
class CustomOptionButton extends AbstractButton<ICustomOptionButton> {
backgroundColor = this.props.backgroundColor;
iconSrc = this.props.icon;
id = this.props.id;

View File

@@ -0,0 +1,21 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import HangupButton from '../HangupButton';
import HangupMenuButton from './HangupMenuButton';
const HangupContainerButtons = (props: AbstractButtonProps) => {
const { conference } = useSelector((state: IReduxState) => state['features/base/conference']);
const endConferenceSupported = conference?.isEndConferenceSupported();
return endConferenceSupported
// @ts-ignore
? <HangupMenuButton { ...props } />
: <HangupButton { ...props } />;
};
export default HangupContainerButtons;

View File

@@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { ViewStyle } from 'react-native';
import { Divider } from 'react-native-paper';
import { connect } from 'react-redux';
import { connect, useSelector } from 'react-redux';
import { IReduxState, IStore } from '../../../app/types';
import { hideSheet } from '../../../base/dialog/actions';
@@ -22,18 +22,17 @@ import { isSharedVideoEnabled } from '../../../shared-video/functions';
import SpeakerStatsButton from '../../../speaker-stats/components/native/SpeakerStatsButton';
import { isSpeakerStatsDisabled } from '../../../speaker-stats/functions';
import ClosedCaptionButton from '../../../subtitles/components/native/ClosedCaptionButton';
import TileViewButton from '../../../video-layout/components/TileViewButton';
import styles from '../../../video-menu/components/native/styles';
import WhiteboardButton from '../../../whiteboard/components/native/WhiteboardButton';
import { customButtonPressed } from '../../actions.native';
import { getMovableButtons } from '../../functions.native';
import { getVisibleNativeButtons } from '../../functions.native';
import { useNativeToolboxButtons } from '../../hooks.native';
import { IToolboxNativeButton } from '../../types';
import AudioOnlyButton from './AudioOnlyButton';
import CustomOptionButton from './CustomOptionButton';
import LinkToSalesforceButton from './LinkToSalesforceButton';
import OpenCarmodeButton from './OpenCarmodeButton';
import RaiseHandButton from './RaiseHandButton';
import ScreenSharingButton from './ScreenSharingButton';
/**
@@ -41,11 +40,6 @@ import ScreenSharingButton from './ScreenSharingButton';
*/
interface IProps {
/**
* Custom Toolbar buttons.
*/
_customToolbarButtons?: Array<{ backgroundColor?: string; icon: string; id: string; text: string; }>;
/**
* True if breakout rooms feature is available, false otherwise.
*/
@@ -66,6 +60,16 @@ interface IProps {
*/
_isSpeakerStatsDisabled?: boolean;
/**
* Toolbar buttons.
*/
_mainMenuButtons?: Array<IToolboxNativeButton>;
/**
* Overflow menu buttons.
*/
_overflowMenuButtons?: Array<IToolboxNativeButton>;
/**
* Whether the recoding button should be enabled or not.
*/
@@ -76,11 +80,6 @@ interface IProps {
*/
_shouldDisplayReactionsButtons: boolean;
/**
* The width of the screen.
*/
_width: number;
/**
* Used for hiding the dialog when the selection was completed.
*/
@@ -128,11 +127,8 @@ class OverflowMenu extends PureComponent<IProps, IState> {
_isBreakoutRoomsSupported,
_isSpeakerStatsDisabled,
_isSharedVideoEnabled,
_shouldDisplayReactionsButtons,
_width,
dispatch
} = this.props;
const toolbarButtons = getMovableButtons(_width);
const buttonProps = {
afterClick: this._onCancel,
@@ -156,18 +152,12 @@ class OverflowMenu extends PureComponent<IProps, IState> {
return (
<BottomSheet
renderFooter = { _shouldDisplayReactionsButtons && !toolbarButtons.has('raisehand')
? this._renderReactionMenu
: undefined }>
{ this._renderCustomOverflowMenuButtons(topButtonProps) }
renderFooter = { this._renderReactionMenu }>
<Divider style = { styles.divider as ViewStyle } />
<OpenCarmodeButton { ...topButtonProps } />
<AudioOnlyButton { ...buttonProps } />
{
!_shouldDisplayReactionsButtons && !toolbarButtons.has('raisehand')
&& <RaiseHandButton { ...buttonProps } />
}
{ this._renderRaiseHandButton(buttonProps) }
{/* @ts-ignore */}
<Divider style = { styles.divider as ViewStyle } />
<SecurityDialogButton { ...buttonProps } />
<RecordButton { ...buttonProps } />
<LiveStreamButton { ...buttonProps } />
@@ -176,9 +166,8 @@ class OverflowMenu extends PureComponent<IProps, IState> {
{/* @ts-ignore */}
<Divider style = { styles.divider as ViewStyle } />
{_isSharedVideoEnabled && <SharedVideoButton { ...buttonProps } />}
{!toolbarButtons.has('screensharing') && <ScreenSharingButton { ...buttonProps } />}
{ this._renderOverflowMenuButtons(topButtonProps) }
{!_isSpeakerStatsDisabled && <SpeakerStatsButton { ...buttonProps } />}
{!toolbarButtons.has('tileview') && <TileViewButton { ...buttonProps } />}
{_isBreakoutRoomsSupported && <BreakoutRoomsButton { ...buttonProps } />}
{/* @ts-ignore */}
<Divider style = { styles.divider as ViewStyle } />
@@ -205,42 +194,72 @@ class OverflowMenu extends PureComponent<IProps, IState> {
* @returns {React.ReactElement}
*/
_renderReactionMenu() {
return (
<ReactionMenu
onCancel = { this._onCancel }
overflowMenu = { true } />
);
const { _mainMenuButtons, _shouldDisplayReactionsButtons } = this.props;
// @ts-ignore
const isRaiseHandInMainMenu = _mainMenuButtons?.some(item => item.key === 'raisehand');
if (_shouldDisplayReactionsButtons && !isRaiseHandInMainMenu) {
return (
<ReactionMenu
onCancel = { this._onCancel }
overflowMenu = { true } />
);
}
}
/**
* Function to render the reaction menu as the footer of the bottom sheet.
*
* @param {Object} buttonProps - Styling button properties.
* @returns {React.ReactElement}
*/
_renderRaiseHandButton(buttonProps: Object) {
const { _mainMenuButtons, _shouldDisplayReactionsButtons } = this.props;
// @ts-ignore
const isRaiseHandInMainMenu = _mainMenuButtons?.some(item => item.key === 'raisehand');
if (!_shouldDisplayReactionsButtons && !isRaiseHandInMainMenu) {
return (
<RaiseHandButton { ...buttonProps } />
);
}
}
/**
* Function to render the custom buttons for the overflow menu.
*
* @param {Object} topButtonProps - Button properties.
* @param {Object} topButtonProps - Styling button properties.
* @returns {React.ReactElement}
*/
_renderCustomOverflowMenuButtons(topButtonProps: Object) {
const { _customToolbarButtons, dispatch } = this.props;
_renderOverflowMenuButtons(topButtonProps: Object) {
const { _overflowMenuButtons, dispatch } = this.props;
if (!_customToolbarButtons?.length) {
if (!_overflowMenuButtons?.length) {
return;
}
return (
<>
{
_customToolbarButtons.map(({ id, text, icon, backgroundColor }) => (
<CustomOptionButton
{ ...topButtonProps }
backgroundColor = { backgroundColor }
/* eslint-disable react/jsx-no-bind */
handleClick = { () =>
dispatch(customButtonPressed(id, text))
}
icon = { icon }
isToolboxButton = { false }
key = { id }
text = { text } />
))
_overflowMenuButtons?.map(({ Content, key, text, ...rest }: IToolboxNativeButton) => {
if (key === 'raisehand') {
return null;
}
return (
<Content
{ ...topButtonProps }
{ ...rest }
/* eslint-disable react/jsx-no-bind */
handleClick = { () => dispatch(customButtonPressed(key, text)) }
isToolboxButton = { false }
key = { key }
text = { text } />
);
})
}
<Divider style = { styles.divider as ViewStyle } />
</>
@@ -257,16 +276,38 @@ class OverflowMenu extends PureComponent<IProps, IState> {
*/
function _mapStateToProps(state: IReduxState) {
const { conference } = state['features/base/conference'];
const { customToolbarButtons } = state['features/base/config'];
return {
_customToolbarButtons: customToolbarButtons,
_isBreakoutRoomsSupported: conference?.getBreakoutRooms()?.isSupported(),
_isSharedVideoEnabled: isSharedVideoEnabled(state),
_isSpeakerStatsDisabled: isSpeakerStatsDisabled(state),
_shouldDisplayReactionsButtons: shouldDisplayReactionsButtons(state),
_width: state['features/base/responsive-ui'].clientWidth
_shouldDisplayReactionsButtons: shouldDisplayReactionsButtons(state)
};
}
export default connect(_mapStateToProps)(OverflowMenu);
export default connect(_mapStateToProps)(props => {
const { clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
const { customToolbarButtons } = useSelector((state: IReduxState) => state['features/base/config']);
const {
mainToolbarButtonsThresholds,
toolbarButtons
} = useSelector((state: IReduxState) => state['features/toolbox']);
const allButtons = useNativeToolboxButtons(customToolbarButtons);
const { mainMenuButtons, overflowMenuButtons } = getVisibleNativeButtons({
allButtons,
clientWidth,
mainToolbarButtonsThresholds,
toolbarButtons
});
return (
<OverflowMenu
// @ts-ignore
{ ... props }
_mainMenuButtons = { mainMenuButtons }
_overflowMenuButtons = { overflowMenuButtons } />
);
});

View File

@@ -25,6 +25,8 @@ class OverflowMenuButton extends AbstractButton<AbstractButtonProps> {
* @returns {void}
*/
_handleClick() {
// @ts-ignore
this.props.dispatch(openSheet(OverflowMenu));
}
}

View File

@@ -1,55 +1,29 @@
import React from 'react';
import { View, ViewStyle } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { connect } from 'react-redux';
import { connect, useSelector } from 'react-redux';
import { IReduxState, IStore } from '../../../app/types';
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
import Platform from '../../../base/react/Platform.native';
import ChatButton from '../../../chat/components/native/ChatButton';
import ReactionsMenuButton from '../../../reactions/components/native/ReactionsMenuButton';
import { shouldDisplayReactionsButtons } from '../../../reactions/functions.any';
import TileViewButton from '../../../video-layout/components/TileViewButton';
import { iAmVisitor } from '../../../visitors/functions';
import { customButtonPressed } from '../../actions.native';
import { getMovableButtons, isToolboxVisible } from '../../functions.native';
import HangupButton from '../HangupButton';
import { getVisibleNativeButtons, isToolboxVisible } from '../../functions.native';
import { useNativeToolboxButtons } from '../../hooks.native';
import { IToolboxNativeButton } from '../../types';
import AudioMuteButton from './AudioMuteButton';
import CustomOptionButton from './CustomOptionButton';
import HangupMenuButton from './HangupMenuButton';
import OverflowMenuButton from './OverflowMenuButton';
import RaiseHandButton from './RaiseHandButton';
import ScreenSharingButton from './ScreenSharingButton';
import VideoMuteButton from './VideoMuteButton';
import styles from './styles';
/**
* The type of {@link Toolbox}'s React {@code Component} props.
*/
interface IProps {
/**
* Custom Toolbar buttons.
*/
_customToolbarButtons?: Array<{ backgroundColor?: string; icon: string; id: string; text: string; }>;
/**
* Whether the end conference feature is supported.
*/
_endConferenceSupported: boolean;
/**
* Whether we are in visitors mode.
*/
_iAmVisitor: boolean;
/**
* Whether or not any reactions buttons should be visible.
*/
_shouldDisplayReactionsButtons: boolean;
/**
* The color-schemed stylesheet of the feature.
*/
@@ -60,11 +34,6 @@ interface IProps {
*/
_visible: boolean;
/**
* The width of the screen.
*/
_width: number;
/**
* Redux store dispatch method.
*/
@@ -79,13 +48,9 @@ interface IProps {
*/
function Toolbox(props: IProps) {
const {
_customToolbarButtons,
_endConferenceSupported,
_iAmVisitor,
_shouldDisplayReactionsButtons,
_styles,
_visible,
_width,
dispatch
} = props;
@@ -93,41 +58,47 @@ function Toolbox(props: IProps) {
return null;
}
const { clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
const { customToolbarButtons } = useSelector((state: IReduxState) => state['features/base/config']);
const {
mainToolbarButtonsThresholds,
toolbarButtons
} = useSelector((state: IReduxState) => state['features/toolbox']);
const allButtons = useNativeToolboxButtons(customToolbarButtons);
const { mainMenuButtons } = getVisibleNativeButtons({
allButtons,
clientWidth,
mainToolbarButtonsThresholds,
toolbarButtons
});
const bottomEdge = Platform.OS === 'ios' && _visible;
const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
const additionalButtons = getMovableButtons(_width);
const backgroundToggledStyle = {
...toggledButtonStyles,
style: [
toggledButtonStyles.style,
_styles.backgroundToggle
]
};
const { buttonStylesBorderless, hangupButtonStyles } = _styles;
const style = { ...styles.toolbox };
// we have only hangup and raisehand button in _iAmVisitor mode
// We have only hangup and raisehand button in _iAmVisitor mode
if (_iAmVisitor) {
additionalButtons.add('raisehand');
style.justifyContent = 'center';
}
const renderCustomToolboxButtons = () => {
if (!_customToolbarButtons?.length) {
const renderToolboxButtons = () => {
if (!mainMenuButtons?.length) {
return;
}
return (
<>
{
_customToolbarButtons.map(({ backgroundColor, id, text, icon }) => (
<CustomOptionButton
backgroundColor = { backgroundColor }
mainMenuButtons?.map(({ Content, key, text, ...rest }: IToolboxNativeButton) => (
<Content
{ ...rest }
/* eslint-disable react/jsx-no-bind */
handleClick = { () => dispatch(customButtonPressed(id, text)) }
icon = { icon }
handleClick = { () => dispatch(customButtonPressed(key, text)) }
isToolboxButton = { true }
key = { id } />
key = { key }
styles = { key === 'hangup' ? hangupButtonStyles : buttonStylesBorderless } />
))
}
</>
@@ -144,44 +115,7 @@ function Toolbox(props: IProps) {
edges = { [ bottomEdge && 'bottom' ].filter(Boolean) }
pointerEvents = 'box-none'
style = { style as ViewStyle }>
{
_customToolbarButtons
? <>
{ renderCustomToolboxButtons() }
{ !_iAmVisitor && <OverflowMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } /> }
</>
: <>
{!_iAmVisitor && <AudioMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />}
{!_iAmVisitor && <VideoMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />}
{additionalButtons.has('chat')
&& <ChatButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
{!_iAmVisitor && additionalButtons.has('screensharing')
&& <ScreenSharingButton styles = { buttonStylesBorderless } />}
{additionalButtons.has('raisehand') && (_shouldDisplayReactionsButtons
? <ReactionsMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />
: <RaiseHandButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />)}
{additionalButtons.has('tileview')
&& <TileViewButton styles = { buttonStylesBorderless } />}
{!_iAmVisitor && <OverflowMenuButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />}
{ _endConferenceSupported
? <HangupMenuButton />
: <HangupButton styles = { hangupButtonStyles } />}
</>
}
{ renderToolboxButtons() }
</SafeAreaView>
</View>
);
@@ -197,17 +131,10 @@ function Toolbox(props: IProps) {
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState) {
const { conference } = state['features/base/conference'];
const endConferenceSupported = conference?.isEndConferenceSupported();
return {
_customToolbarButtons: state['features/base/config']?.customToolbarButtons,
_endConferenceSupported: Boolean(endConferenceSupported),
_iAmVisitor: iAmVisitor(state),
_styles: ColorSchemeRegistry.get(state, 'Toolbox'),
_visible: isToolboxVisible(state),
_iAmVisitor: iAmVisitor(state),
_width: state['features/base/responsive-ui'].clientWidth,
_shouldDisplayReactionsButtons: shouldDisplayReactionsButtons(state)
};
}

View File

@@ -1,4 +1,4 @@
import { ToolbarButton } from './types';
import { NativeToolbarButton, ToolbarButton } from './types';
/**
* Thresholds for displaying toolbox buttons.
@@ -34,6 +34,32 @@ export const THRESHOLDS = [
}
];
/**
* Thresholds for displaying native toolbox buttons.
*/
export const NATIVE_THRESHOLDS = [
{
width: 560,
order: [ 'microphone', 'camera', 'chat', 'screensharing', 'raisehand', 'tileview', 'overflowmenu', 'hangup' ]
},
{
width: 500,
order: [ 'microphone', 'camera', 'chat', 'raisehand', 'tileview', 'overflowmenu', 'hangup' ]
},
{
width: 440,
order: [ 'microphone', 'camera', 'chat', 'raisehand', 'overflowmenu', 'hangup' ]
},
{
width: 380,
order: [ 'microphone', 'camera', 'chat', 'overflowmenu', 'hangup' ]
},
{
width: 320,
order: [ 'microphone', 'camera', 'overflowmenu', 'hangup' ]
}
];
/**
* Main toolbar buttons priority used to determine which button should be picked to fill empty spaces for disabled
* buttons.
@@ -47,6 +73,8 @@ export const MAIN_TOOLBAR_BUTTONS_PRIORITY = [
'reactions',
'participants-pane',
'tileview',
'overflowmenu',
'hangup',
'invite',
'toggle-camera',
'videoquality',
@@ -128,17 +156,34 @@ export const TOOLBAR_BUTTONS: ToolbarButton[] = [
'whiteboard'
];
/**
* The list of all possible native buttons.
*
* @protected
* @type Array<string>
*/
export const NATIVE_TOOLBAR_BUTTONS: NativeToolbarButton[] = [
'camera',
'chat',
'hangup',
'microphone',
'overflowmenu',
'raisehand',
'screensharing',
'tileview'
];
/**
* The toolbar buttons to show when in visitors mode.
*/
export const VISITORS_MODE_BUTTONS: ToolbarButton[] = [
'chat',
'closedcaptions',
'fullscreen',
'hangup',
'raisehand',
'settings',
'tileview',
'fullscreen',
'stats',
'tileview',
'videoquality'
];

View File

@@ -1,9 +1,13 @@
import { IReduxState } from '../app/types';
import { IStateful } from '../base/app/types';
import { isJwtFeatureEnabledStateless } from '../base/jwt/functions';
import { IGUMPendingState } from '../base/media/types';
import { IParticipantFeatures } from '../base/participants/types';
import { toState } from '../base/redux/functions';
import { iAmVisitor } from '../visitors/functions';
import { VISITORS_MODE_BUTTONS } from './constants';
/**
* Indicates if the audio mute button is disabled or not.
*
@@ -57,3 +61,41 @@ export function getJwtDisabledButtons(
return acc;
}
/**
* Returns the list of enabled toolbar buttons.
*
* @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
* @param {string[]} definedToolbarButtons - The list of all possible buttons.
*
* @returns {Array<string>} - The list of enabled toolbar buttons.
*/
export function getToolbarButtons(stateful: IStateful, definedToolbarButtons: string[]): Array<string> {
const state = toState(stateful);
const { toolbarButtons, customToolbarButtons } = state['features/base/config'];
const customButtons = customToolbarButtons?.map(({ id }) => id);
let buttons = Array.isArray(toolbarButtons) ? toolbarButtons : definedToolbarButtons;
if (iAmVisitor(state)) {
buttons = VISITORS_MODE_BUTTONS.filter(button => buttons.indexOf(button) > -1);
}
if (customButtons) {
return [ ...buttons, ...customButtons ];
}
return buttons;
}
/**
* Checks if the specified button is enabled.
*
* @param {string} buttonName - The name of the button. See {@link interfaceConfig}.
* @param {Object|Array<string>} state - The redux state or the array with the enabled buttons.
* @returns {boolean} - True if the button is enabled and false otherwise.
*/
export function isButtonEnabled(buttonName: string, state: IReduxState | Array<string>) {
const buttons = Array.isArray(state) ? state : state['features/toolbox'].toolbarButtons || [];
return buttons.includes(buttonName);
}

View File

@@ -7,53 +7,12 @@ import { getParticipantCountWithFake } from '../base/participants/functions';
import { toState } from '../base/redux/functions';
import { isLocalVideoTrackDesktop } from '../base/tracks/functions.native';
import { MAIN_TOOLBAR_BUTTONS_PRIORITY } from './constants';
import { isButtonEnabled } from './functions.any';
import { IGetVisibleNativeButtonsParams, IToolboxNativeButton } from './types';
export * from './functions.any';
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: string[] = [];
switch (true) {
case width >= WIDTH.FIT_9_ICONS: {
buttons = [ 'chat', 'togglecamera', 'screensharing', 'raisehand', 'tileview' ];
break;
}
case width >= WIDTH.FIT_8_ICONS: {
buttons = [ 'chat', 'togglecamera', 'raisehand', 'tileview' ];
break;
}
case width >= WIDTH.FIT_7_ICONS: {
buttons = [ 'chat', 'togglecamera', 'raisehand' ];
break;
}
case width >= WIDTH.FIT_6_ICONS: {
buttons = [ 'chat', 'togglecamera' ];
break;
}
default: {
buttons = [ 'chat' ];
}
}
return new Set(buttons);
}
/**
* Indicates if the desktop share button is disabled or not.
*
@@ -99,3 +58,64 @@ export function isVideoMuteButtonDisabled(state: IReduxState) {
return !hasAvailableDevices(state, 'videoInput')
|| (unmuteBlocked && Boolean(muted));
}
/**
* Returns all buttons that need to be rendered.
*
* @param {IGetVisibleButtonsParams} params - The parameters needed to extract the visible buttons.
* @returns {Object} - The visible buttons arrays .
*/
export function getVisibleNativeButtons({ allButtons, clientWidth, mainToolbarButtonsThresholds, toolbarButtons
}: IGetVisibleNativeButtonsParams) {
const filteredButtons = Object.keys(allButtons).filter(key =>
typeof key !== 'undefined' // filter invalid buttons that may be coming from config.mainToolbarButtons override
&& isButtonEnabled(key, toolbarButtons));
const { order } = mainToolbarButtonsThresholds.find(({ width }) => clientWidth > width)
|| mainToolbarButtonsThresholds[mainToolbarButtonsThresholds.length - 1];
const mainToolbarButtonKeysOrder = [
...order.filter(key => filteredButtons.includes(key)),
...MAIN_TOOLBAR_BUTTONS_PRIORITY.filter(key => !order.includes(key) && filteredButtons.includes(key)),
...filteredButtons.filter(key => !order.includes(key) && !MAIN_TOOLBAR_BUTTONS_PRIORITY.includes(key))
];
const mainButtonsKeys = mainToolbarButtonKeysOrder.slice(0, order.length);
const overflowMenuButtons = filteredButtons.reduce((acc, key) => {
if (!mainButtonsKeys.includes(key)) {
acc.push(allButtons[key]);
}
return acc;
}, [] as IToolboxNativeButton[]);
// if we have 1 button in the overflow menu it is better to directly display it in the main toolbar by replacing
// the "More" menu button with it.
if (overflowMenuButtons.length === 1) {
const button = overflowMenuButtons.shift()?.key;
button && mainButtonsKeys.push(button);
}
const mainMenuButtons
= mainButtonsKeys.map(key => allButtons[key]).sort((a, b) => {
// Native toolbox includes hangup and overflowmenu button keys, too
// hangup goes last, overflowmenu goes second-to-last
if (a.key === 'hangup' || a.key === 'overflowmenu') {
return 1;
}
if (b.key === 'hangup' || b.key === 'overflowmenu') {
return -1;
}
return 0; // other buttons are sorted by priority
});
return {
mainMenuButtons,
overflowMenuButtons
};
}

View File

@@ -1,5 +1,5 @@
import { IReduxState } from '../app/types';
import { hasAvailableDevices } from '../base/devices/functions';
import { hasAvailableDevices } from '../base/devices/functions.web';
import { MEET_FEATURES } from '../base/jwt/constants';
import { isJwtFeatureEnabled } from '../base/jwt/functions';
import { IGUMPendingState } from '../base/media/types';
@@ -7,7 +7,8 @@ import { isScreenMediaShared } from '../screen-share/functions';
import { isWhiteboardVisible } from '../whiteboard/functions';
import { MAIN_TOOLBAR_BUTTONS_PRIORITY, TOOLBAR_TIMEOUT } from './constants';
import { IMainToolbarButtonThresholds, IToolboxButton, NOTIFY_CLICK_MODE } from './types';
import { isButtonEnabled } from './functions.any';
import { IGetVisibleButtonsParams, IToolboxButton, NOTIFY_CLICK_MODE } from './types';
export * from './functions.any';
@@ -22,19 +23,6 @@ export function getToolboxHeight() {
return toolbox?.clientHeight || 0;
}
/**
* Checks if the specified button is enabled.
*
* @param {string} buttonName - The name of the button. See {@link interfaceConfig}.
* @param {Object|Array<string>} state - The redux state or the array with the enabled buttons.
* @returns {boolean} - True if the button is enabled and false otherwise.
*/
export function isButtonEnabled(buttonName: string, state: IReduxState | Array<string>) {
const buttons = Array.isArray(state) ? state : state['features/toolbox'].toolbarButtons || [];
return buttons.includes(buttonName);
}
/**
* Indicates if the toolbox is visible or not.
*
@@ -125,26 +113,6 @@ export function showOverflowDrawer(state: IReduxState) {
return state['features/toolbox'].overflowDrawer;
}
/**
* Returns true if the overflow menu button is displayed and false otherwise.
*
* @param {IReduxState} state - The state from the Redux store.
* @returns {boolean} - True if the overflow menu button is displayed and false otherwise.
*/
export function showOverflowMenu(state: IReduxState) {
return state['features/toolbox'].overflowMenuVisible;
}
/**
* Indicates whether the toolbox is enabled or not.
*
* @param {IReduxState} state - The state from the Redux store.
* @returns {boolean}
*/
export function isToolboxEnabled(state: IReduxState) {
return state['features/toolbox'].enabled;
}
/**
* Returns the toolbar timeout from config or the default value.
*
@@ -176,15 +144,6 @@ function setButtonsNotifyClickMode(buttons: Object, buttonsWithNotifyClick: Map<
});
}
interface IGetVisibleButtonsParams {
allButtons: { [key: string]: IToolboxButton; };
buttonsWithNotifyClick: Map<string, NOTIFY_CLICK_MODE>;
clientWidth: number;
jwtDisabledButtons: string[];
mainToolbarButtonsThresholds: IMainToolbarButtonThresholds;
toolbarButtons: string[];
}
/**
* Returns all buttons that need to be rendered.
*
@@ -234,8 +193,10 @@ export function getVisibleButtons({
button && mainButtonsKeys.push(button);
}
const mainMenuButtons = mainButtonsKeys.map(key => allButtons[key]);
return {
mainMenuButtons: mainButtonsKeys.map(key => allButtons[key]),
mainMenuButtons,
overflowMenuButtons
};
}

View File

@@ -0,0 +1,193 @@
import { useSelector } from 'react-redux';
import ChatButton from '../chat/components/native/ChatButton';
import RaiseHandContainerButtons from '../reactions/components/native/RaiseHandContainerButtons';
import TileViewButton from '../video-layout/components/TileViewButton';
import { iAmVisitor } from '../visitors/functions';
import AudioMuteButton from './components/native/AudioMuteButton';
import CustomOptionButton from './components/native/CustomOptionButton';
import HangupContainerButtons from './components/native/HangupContainerButtons';
import OverflowMenuButton from './components/native/OverflowMenuButton';
import ScreenSharingButton from './components/native/ScreenSharingButton';
import VideoMuteButton from './components/native/VideoMuteButton';
import { isDesktopShareButtonDisabled } from './functions.native';
import { ICustomToolbarButton, IToolboxNativeButton, NativeToolbarButton } from './types';
const microphone = {
key: 'microphone',
Content: AudioMuteButton,
group: 0
};
const camera = {
key: 'camera',
Content: VideoMuteButton,
group: 0
};
const chat = {
key: 'chat',
Content: ChatButton,
group: 1
};
const screensharing = {
key: 'screensharing',
Content: ScreenSharingButton,
group: 1
};
const raisehand = {
key: 'raisehand',
Content: RaiseHandContainerButtons,
group: 2
};
const tileview = {
key: 'tileview',
Content: TileViewButton,
group: 2
};
const overflowmenu = {
key: 'overflowmenu',
Content: OverflowMenuButton,
group: 3
};
const hangup = {
key: 'hangup',
Content: HangupContainerButtons,
group: 3
};
/**
* A hook that returns the audio mute button.
*
* @returns {Object | undefined}
*/
function getAudioMuteButton() {
const _iAmVisitor = useSelector(iAmVisitor);
if (!_iAmVisitor) {
return microphone;
}
}
/**
* A hook that returns the video mute button.
*
* @returns {Object | undefined}
*/
function getVideoMuteButton() {
const _iAmVisitor = useSelector(iAmVisitor);
if (!_iAmVisitor) {
return camera;
}
}
/**
* A hook that returns the chat button.
*
* @returns {Object | undefined}
*/
function getChatButton() {
const _iAmVisitor = useSelector(iAmVisitor);
if (!_iAmVisitor) {
return chat;
}
}
/**
* A hook that returns the screen sharing button.
*
* @returns {Object | undefined}
*/
function getScreenSharingButton() {
const _iAmVisitor = useSelector(iAmVisitor);
const _isScreenShareButtonDisabled = useSelector(isDesktopShareButtonDisabled);
if (!_isScreenShareButtonDisabled && !_iAmVisitor) {
return screensharing;
}
}
/**
* A hook that returns the tile view button.
*
* @returns {Object | undefined}
*/
function getTileViewButton() {
const _iAmVisitor = useSelector(iAmVisitor);
if (!_iAmVisitor) {
return tileview;
}
}
/**
* A hook that returns the overflow menu button.
*
* @returns {Object | undefined}
*/
function getOverflowMenuButton() {
const _iAmVisitor = useSelector(iAmVisitor);
if (!_iAmVisitor) {
return overflowmenu;
}
}
/**
* Returns all buttons that could be rendered.
*
* @param {Object} _customToolbarButtons - An array containing custom buttons objects.
* @returns {Object} The button maps mainMenuButtons and overflowMenuButtons.
*/
export function useNativeToolboxButtons(
_customToolbarButtons?: ICustomToolbarButton[]): { [key: string]: IToolboxNativeButton; } {
const audioMuteButton = getAudioMuteButton();
const videoMuteButton = getVideoMuteButton();
const chatButton = getChatButton();
const screenSharingButton = getScreenSharingButton();
const tileViewButton = getTileViewButton();
const overflowMenuButton = getOverflowMenuButton();
const buttons: { [key in NativeToolbarButton]?: IToolboxNativeButton; } = {
microphone: audioMuteButton,
camera: videoMuteButton,
chat: chatButton,
screensharing: screenSharingButton,
raisehand,
tileview: tileViewButton,
overflowmenu: overflowMenuButton,
hangup
};
const buttonKeys = Object.keys(buttons) as NativeToolbarButton[];
buttonKeys.forEach(
key => typeof buttons[key] === 'undefined' && delete buttons[key]);
const customButtons = _customToolbarButtons?.reduce((prev, { backgroundColor, icon, id, text }) => {
prev[id] = {
backgroundColor,
key: id,
id,
Content: CustomOptionButton,
group: 4,
icon,
text
};
return prev;
}, {} as { [key: string]: ICustomToolbarButton; });
return {
...buttons,
...customButtons
};
}

View File

@@ -270,7 +270,7 @@ function useHelpButton() {
*/
export function useToolboxButtons(
_customToolbarButtons?: ICustomToolbarButton[]): { [key: string]: IToolboxButton; } {
const dekstopSharing = getDesktopSharingButton();
const desktopSharing = getDesktopSharingButton();
const toggleCameraButton = useToggleCameraButton();
const _fullscreen = getFullscreenButton();
const security = useSecurityDialogButton();
@@ -297,7 +297,7 @@ export function useToolboxButtons(
microphone,
camera,
profile,
desktop: dekstopSharing,
desktop: desktopSharing,
chat,
raisehand,
reactions,

View File

@@ -0,0 +1,46 @@
import { OVERWRITE_CONFIG, SET_CONFIG, UPDATE_CONFIG } from '../base/config/actionTypes';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { I_AM_VISITOR_MODE } from '../visitors/actionTypes';
import { SET_TOOLBAR_BUTTONS } from './actionTypes';
import { setMainToolbarThresholds } from './actions.native';
import { NATIVE_THRESHOLDS, NATIVE_TOOLBAR_BUTTONS } from './constants';
import { getToolbarButtons } from './functions.native';
/**
* Middleware which intercepts Toolbox actions to handle changes to the
* visibility timeout of the Toolbox.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case UPDATE_CONFIG:
case OVERWRITE_CONFIG:
case I_AM_VISITOR_MODE:
case SET_CONFIG: {
const result = next(action);
const { dispatch } = store;
const state = store.getState();
const toolbarButtons = getToolbarButtons(state, NATIVE_TOOLBAR_BUTTONS);
if (action.type !== I_AM_VISITOR_MODE) {
dispatch(setMainToolbarThresholds(NATIVE_THRESHOLDS));
}
dispatch({
type: SET_TOOLBAR_BUTTONS,
toolbarButtons
});
return result;
}
}
return next(action);
});

View File

@@ -1,12 +1,10 @@
import { batch } from 'react-redux';
import { AnyAction } from 'redux';
import { IReduxState } from '../app/types';
import { OVERWRITE_CONFIG, SET_CONFIG, UPDATE_CONFIG } from '../base/config/actionTypes';
import { NotifyClickButton } from '../base/config/configType';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { I_AM_VISITOR_MODE } from '../visitors/actionTypes';
import { iAmVisitor } from '../visitors/functions';
import {
CLEAR_TOOLBOX_TIMEOUT,
@@ -17,7 +15,8 @@ import {
SET_TOOLBOX_TIMEOUT
} from './actionTypes';
import { setMainToolbarThresholds } from './actions.web';
import { TOOLBAR_BUTTONS, VISITORS_MODE_BUTTONS } from './constants';
import { THRESHOLDS, TOOLBAR_BUTTONS } from './constants';
import { getToolbarButtons } from './functions.web';
import { NOTIFY_CLICK_MODE } from './types';
import './subscriber.web';
@@ -55,7 +54,7 @@ MiddlewareRegistry.register(store => next => action => {
batch(() => {
if (action.type !== I_AM_VISITOR_MODE) {
dispatch(setMainToolbarThresholds());
dispatch(setMainToolbarThresholds(THRESHOLDS));
}
dispatch({
type: SET_BUTTONS_WITH_NOTIFY_CLICK,
@@ -69,7 +68,7 @@ MiddlewareRegistry.register(store => next => action => {
});
}
const toolbarButtons = _getToolbarButtons(state);
const toolbarButtons = getToolbarButtons(state, TOOLBAR_BUTTONS);
dispatch({
type: SET_TOOLBAR_BUTTONS,
@@ -171,25 +170,3 @@ function _buildButtonsArray(
return new Map([ ...customButtonsWithNotifyClick, ...buttons ]);
}
/**
* Returns the list of enabled toolbar buttons.
*
* @param {Object} state - The redux state.
* @returns {Array<string>} - The list of enabled toolbar buttons.
*/
function _getToolbarButtons(state: IReduxState): Array<string> {
const { toolbarButtons, customToolbarButtons } = state['features/base/config'];
const customButtons = customToolbarButtons?.map(({ id }) => id);
let buttons = Array.isArray(toolbarButtons) ? toolbarButtons : TOOLBAR_BUTTONS;
if (iAmVisitor(state)) {
buttons = VISITORS_MODE_BUTTONS.filter(button => buttons.indexOf(button) > -1);
}
if (customButtons) {
return [ ...buttons, ...customButtons ];
}
return buttons;
}

View File

@@ -18,7 +18,7 @@ import {
SET_TOOLBOX_VISIBLE,
TOGGLE_TOOLBOX_VISIBLE
} from './actionTypes';
import { THRESHOLDS } from './constants';
import { NATIVE_THRESHOLDS, THRESHOLDS } from './constants';
import { IMainToolbarButtonThresholds, NOTIFY_CLICK_MODE } from './types';
/**
@@ -52,7 +52,7 @@ const INITIAL_STATE = {
/**
* The thresholds for screen size and visible main toolbar buttons.
*/
mainToolbarButtonsThresholds: THRESHOLDS,
mainToolbarButtonsThresholds: navigator.product === 'ReactNative' ? NATIVE_THRESHOLDS : THRESHOLDS,
participantMenuButtonsWithNotifyClick: new Map(),

View File

@@ -1,13 +1,21 @@
import { ComponentType } from 'react';
import { CustomOptionButton } from './components';
export interface IToolboxButton {
Content: ComponentType<any>;
group: number;
key: string;
}
export interface IToolboxNativeButton {
Content: ComponentType<any>;
backgroundColor?: string;
group: number;
icon?: string;
id?: string;
key: string;
text?: string;
}
export type ToolbarButton = 'camera' |
'chat' |
'closedcaptions' |
@@ -28,6 +36,7 @@ export type ToolbarButton = 'camera' |
'mute-everyone' |
'mute-video-everyone' |
'noisesuppression' |
'overflowmenu' |
'participants-pane' |
'profile' |
'raisehand' |
@@ -52,12 +61,12 @@ export enum NOTIFY_CLICK_MODE {
}
export type IMainToolbarButtonThresholds = Array<{
order: Array<ToolbarButton | string>;
order: Array<ToolbarButton | NativeToolbarButton | string>;
width: number;
}>;
export interface ICustomToolbarButton {
Content?: typeof CustomOptionButton;
Content?: ComponentType<any>;
backgroundColor?: string;
group?: number;
icon: string;
@@ -65,3 +74,28 @@ export interface ICustomToolbarButton {
key?: string;
text: string;
}
export type NativeToolbarButton = 'camera' |
'chat' |
'microphone' |
'raisehand' |
'screensharing' |
'tileview' |
'overflowmenu' |
'hangup';
export interface IGetVisibleNativeButtonsParams {
allButtons: { [key: string]: IToolboxNativeButton; };
clientWidth: number;
mainToolbarButtonsThresholds: IMainToolbarButtonThresholds;
toolbarButtons: string[];
}
export interface IGetVisibleButtonsParams {
allButtons: { [key: string]: IToolboxButton; };
buttonsWithNotifyClick: Map<string, NOTIFY_CLICK_MODE>;
clientWidth: number;
jwtDisabledButtons: string[];
mainToolbarButtonsThresholds: IMainToolbarButtonThresholds;
toolbarButtons: string[];
}