feat(narrow-layout) Use drawer menus on desktop narrow mode (#12799)
This commit is contained in:
parent
cf7e692186
commit
3e58cd8af3
|
@ -52,3 +52,15 @@ export const SET_REDUCED_UI = 'SET_REDUCED_UI';
|
|||
*/
|
||||
export const SET_CONTEXT_MENU_OPEN = 'SET_CONTEXT_MENU_OPEN';
|
||||
|
||||
/**
|
||||
* The type of redux action which signals whether we are in narrow layout.
|
||||
*
|
||||
* {
|
||||
* type: SET_NARROW_LAYOUT,
|
||||
* isNarrow: boolean
|
||||
* }
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SET_NARROW_LAYOUT = 'SET_NARROW_LAYOUT';
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_NARROW_LAYOUT,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from './constants';
|
||||
|
@ -143,3 +144,19 @@ export function setSafeAreaInsets(insets: Object) {
|
|||
insets
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets narrow layout.
|
||||
*
|
||||
* @param {boolean} isNarrow - Whether is narrow layout.
|
||||
* @returns {{
|
||||
* type: SET_NARROW_LAYOUT,
|
||||
* isNarrow: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setNarrowLayout(isNarrow: boolean) {
|
||||
return {
|
||||
type: SET_NARROW_LAYOUT,
|
||||
isNarrow
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_NARROW_LAYOUT,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW } from './constants';
|
||||
|
@ -22,6 +23,7 @@ const DEFAULT_STATE = {
|
|||
aspectRatio: ASPECT_RATIO_NARROW,
|
||||
clientHeight: innerHeight,
|
||||
clientWidth: innerWidth,
|
||||
isNarrowLayout: false,
|
||||
reducedUI: false,
|
||||
contextMenuOpened: false
|
||||
};
|
||||
|
@ -31,6 +33,7 @@ export interface IResponsiveUIState {
|
|||
clientHeight: number;
|
||||
clientWidth: number;
|
||||
contextMenuOpened: boolean;
|
||||
isNarrowLayout: boolean;
|
||||
reducedUI: boolean;
|
||||
safeAreaInsets?: {
|
||||
bottom: number;
|
||||
|
@ -65,6 +68,9 @@ ReducerRegistry.register<IResponsiveUIState>('features/base/responsive-ui',
|
|||
|
||||
case SET_CONTEXT_MENU_OPEN:
|
||||
return set(state, 'contextMenuOpened', action.isOpen);
|
||||
|
||||
case SET_NARROW_LAYOUT:
|
||||
return set(state, 'isNarrowLayout', action.isNarrow);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -81,6 +81,11 @@ type Props = AbstractProps & {
|
|||
*/
|
||||
_enableSaveLogs: boolean,
|
||||
|
||||
/**
|
||||
* Whether is narrow layout or not.
|
||||
*/
|
||||
_isNarrowLayout: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the displays stats are for local video.
|
||||
*/
|
||||
|
@ -193,6 +198,7 @@ class ConnectionIndicatorContent extends AbstractConnectionIndicator<Props, Stat
|
|||
enableSaveLogs = { this.props._enableSaveLogs }
|
||||
framerate = { framerate }
|
||||
isLocalVideo = { this.props._isLocalVideo }
|
||||
isNarrowLayout = { this.props._isNarrowLayout }
|
||||
isVirtualScreenshareParticipant = { this.props._isVirtualScreenshareParticipant }
|
||||
maxEnabledResolution = { maxEnabledResolution }
|
||||
onSaveLogs = { this.props._onSaveLogs }
|
||||
|
@ -312,6 +318,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
const conference = state['features/base/conference'].conference;
|
||||
const participant
|
||||
= participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
const audioTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantId);
|
||||
let videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
|
@ -330,6 +337,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_isE2EEVerified: participant?.e2eeVerified,
|
||||
_isNarrowLayout: isNarrowLayout,
|
||||
_isVirtualScreenshareParticipant: isScreenShareParticipant(participant),
|
||||
_isLocalVideo: participant?.local,
|
||||
_region: participant?.region,
|
||||
|
|
|
@ -98,6 +98,11 @@ interface IProps extends WithTranslation {
|
|||
*/
|
||||
isLocalVideo: boolean;
|
||||
|
||||
/**
|
||||
* Whether we are in narrow layout mode or not.
|
||||
*/
|
||||
isNarrowLayout: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the statistics are for screen share.
|
||||
*/
|
||||
|
@ -261,9 +266,10 @@ class ConnectionStatsTable extends Component<IProps> {
|
|||
disableShowMoreStats,
|
||||
enableSaveLogs,
|
||||
isVirtualScreenshareParticipant,
|
||||
isLocalVideo
|
||||
isLocalVideo,
|
||||
isNarrowLayout
|
||||
} = this.props;
|
||||
const className = clsx(classes.connectionStatsTable, { [classes.mobile]: isMobileBrowser() });
|
||||
const className = clsx(classes.connectionStatsTable, { [classes.mobile]: isMobileBrowser() || isNarrowLayout });
|
||||
|
||||
if (isVirtualScreenshareParticipant) {
|
||||
return this._renderScreenShareStatus();
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
import { pinParticipant } from '../base/participants/actions';
|
||||
import { getParticipantCountWithFake } from '../base/participants/functions';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { clientResized } from '../base/responsive-ui/actions';
|
||||
import { clientResized, setNarrowLayout } from '../base/responsive-ui/actions';
|
||||
import { shouldHideSelfView } from '../base/settings/functions.web';
|
||||
import { selectParticipantInLargeVideo } from '../large-video/actions.any';
|
||||
import { getParticipantsPaneOpen } from '../participants-pane/functions';
|
||||
|
@ -136,9 +135,8 @@ StateListenerRegistry.register(
|
|||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/responsive-ui'].clientWidth < DISPLAY_DRAWER_THRESHOLD,
|
||||
/* listener */ (widthBelowThreshold, store) => {
|
||||
if (isMobileBrowser()) {
|
||||
store.dispatch(setOverflowDrawer(widthBelowThreshold));
|
||||
}
|
||||
store.dispatch(setOverflowDrawer(widthBelowThreshold));
|
||||
store.dispatch(setNarrowLayout(widthBelowThreshold));
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { isMobileBrowser } from '../../../../../base/environment/utils';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants';
|
||||
import { equals } from '../../../../../base/redux';
|
||||
import useContextMenu from '../../../../../base/ui/hooks/useContextMenu.web';
|
||||
|
@ -40,10 +41,11 @@ export const RoomList = ({ searchString }: Props) => {
|
|||
const isLocalModerator = useSelector(isLocalParticipantModerator);
|
||||
const showAutoAssign = useSelector(isAutoAssignParticipantsVisible);
|
||||
const { hideJoinRoomButton } = useSelector(getBreakoutRoomsConfig);
|
||||
const _overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu();
|
||||
const [ lowerParticipantMenu, raiseParticipantMenu, toggleParticipantMenu,
|
||||
participantMenuEnter, participantMenuLeave, raiseParticipantContext ] = useContextMenu();
|
||||
const hideMenu = useCallback(() => !overflowDrawer && lowerMenu(), [ overflowDrawer, lowerMenu ]);
|
||||
const onRaiseMenu = useCallback(room => target => raiseMenu(room, target), [ raiseMenu ]);
|
||||
|
||||
return (
|
||||
|
@ -55,14 +57,14 @@ export const RoomList = ({ searchString }: Props) => {
|
|||
<React.Fragment key = { room.id }>
|
||||
<CollapsibleRoom
|
||||
isHighlighted = { raiseContext.entity === room }
|
||||
onLeave = { lowerMenu }
|
||||
onLeave = { hideMenu }
|
||||
onRaiseMenu = { onRaiseMenu(room) }
|
||||
participantContextEntity = { raiseParticipantContext.entity }
|
||||
raiseParticipantContextMenu = { raiseParticipantMenu }
|
||||
room = { room }
|
||||
searchString = { searchString }
|
||||
toggleParticipantMenu = { toggleParticipantMenu }>
|
||||
{!_overflowDrawer && <>
|
||||
{!isMobileBrowser() && <>
|
||||
{!hideJoinRoomButton && <JoinActionButton room = { room } />}
|
||||
{isLocalModerator && !room.isMainRoom
|
||||
&& <RoomActionEllipsis onClick = { toggleMenu(room) } />}
|
||||
|
|
|
@ -236,10 +236,11 @@ class ReactionsMenu extends Component<IProps> {
|
|||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_localParticipantID: localParticipant?.id,
|
||||
_isMobile: isMobileBrowser(),
|
||||
_isMobile: isMobileBrowser() || isNarrowLayout,
|
||||
_isGifEnabled: isGifEnabled(state),
|
||||
_isGifMenuVisible: isGifsMenuOpen(state),
|
||||
_raisedHand: hasRaisedHand(localParticipant)
|
||||
|
|
|
@ -43,9 +43,9 @@ interface IProps extends WithTranslation {
|
|||
handleClick: Function;
|
||||
|
||||
/**
|
||||
* Whether or not it's a mobile browser.
|
||||
* Whether or not it's narrow mode or mobile browser.
|
||||
*/
|
||||
isMobile: boolean;
|
||||
isNarrow: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the reactions menu is open.
|
||||
|
@ -75,7 +75,7 @@ function ReactionsMenuButton({
|
|||
dispatch,
|
||||
handleClick,
|
||||
isOpen,
|
||||
isMobile,
|
||||
isNarrow,
|
||||
notifyMode,
|
||||
reactionsQueue,
|
||||
t
|
||||
|
@ -95,7 +95,7 @@ function ReactionsMenuButton({
|
|||
|
||||
return (
|
||||
<div className = 'reactions-menu-popup-container'>
|
||||
{!_reactionsEnabled || isMobile ? (
|
||||
{!_reactionsEnabled || isNarrow ? (
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
|
@ -135,10 +135,12 @@ function ReactionsMenuButton({
|
|||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
isOpen: getReactionsMenuVisibility(state),
|
||||
isMobile: isMobileBrowser(),
|
||||
isNarrow: isMobileBrowser() || isNarrowLayout,
|
||||
reactionsQueue: getReactionsQueue(state)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -155,12 +155,13 @@ class AudioSettingsButton extends Component<Props> {
|
|||
*/
|
||||
function mapStateToProps(state) {
|
||||
const { permissions = {} } = state['features/base/devices'];
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
hasPermissions: permissions.audio,
|
||||
isDisabled: isAudioSettingsButtonDisabled(state),
|
||||
isOpen: getAudioSettingsVisibility(state),
|
||||
visible: !isMobileBrowser()
|
||||
visible: !isMobileBrowser() && !isNarrowLayout
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ import { makeStyles } from 'tss-react/mui';
|
|||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
// @ts-ignore
|
||||
import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
|
||||
import { REACTIONS_MENU_HEIGHT } from '../../../reactions/constants';
|
||||
import { getReactionsQueue } from '../../../reactions/functions.any';
|
||||
import { DRAWER_MAX_HEIGHT } from '../../constants';
|
||||
import { showOverflowDrawer } from '../../functions.web';
|
||||
|
||||
// @ts-ignore
|
||||
import Drawer from './Drawer';
|
||||
|
@ -67,7 +67,7 @@ const OverflowMenuButton = ({
|
|||
showMobileReactions
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const overflowDrawer = useSelector((state: IReduxState) => state['features/toolbox'].overflowDrawer);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const reactionsQueue = useSelector(getReactionsQueue);
|
||||
|
||||
const onCloseDialog = useCallback(() => {
|
||||
|
|
|
@ -231,6 +231,11 @@ interface IProps extends WithTranslation {
|
|||
*/
|
||||
_isMobile: boolean;
|
||||
|
||||
/**
|
||||
* Whether we are in narrow layout mode.
|
||||
*/
|
||||
_isNarrowLayout: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the profile is disabled.
|
||||
*/
|
||||
|
@ -710,8 +715,10 @@ class Toolbox extends Component<IProps> {
|
|||
_hasSalesforce,
|
||||
_isIosMobile,
|
||||
_isMobile,
|
||||
_isNarrowLayout,
|
||||
_isSpeakerStatsDisabled,
|
||||
_multiStreamModeEnabled,
|
||||
_reactionsEnabled,
|
||||
_screenSharing,
|
||||
_whiteboardEnabled
|
||||
} = this.props;
|
||||
|
@ -748,7 +755,7 @@ class Toolbox extends Component<IProps> {
|
|||
group: 2
|
||||
};
|
||||
|
||||
const raisehand = {
|
||||
const raisehand = (!_reactionsEnabled || (!_isNarrowLayout && !_isMobile)) && {
|
||||
key: 'raisehand',
|
||||
Content: ReactionsMenuButton,
|
||||
handleClick: this._onToolbarToggleRaiseHand,
|
||||
|
@ -1387,6 +1394,7 @@ class Toolbox extends Component<IProps> {
|
|||
_endConferenceSupported,
|
||||
_hangupMenuVisible,
|
||||
_isMobile,
|
||||
_isNarrowLayout,
|
||||
_overflowDrawer,
|
||||
_overflowMenuVisible,
|
||||
_reactionsEnabled,
|
||||
|
@ -1396,7 +1404,7 @@ class Toolbox extends Component<IProps> {
|
|||
} = this.props;
|
||||
|
||||
const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
|
||||
const containerClassName = `toolbox-content${_isMobile ? ' toolbox-content-mobile' : ''}`;
|
||||
const containerClassName = `toolbox-content${_isMobile || _isNarrowLayout ? ' toolbox-content-mobile' : ''}`;
|
||||
|
||||
const { mainMenuButtons, overflowMenuButtons } = this._getVisibleButtons();
|
||||
|
||||
|
@ -1424,7 +1432,7 @@ class Toolbox extends Component<IProps> {
|
|||
key = 'overflow-menu'
|
||||
onVisibilityChange = { this._onSetOverflowVisible }
|
||||
showMobileReactions = {
|
||||
_reactionsEnabled && overflowMenuButtons.find(({ key }) => key === 'raisehand')
|
||||
_reactionsEnabled && (_isMobile || _isNarrowLayout)
|
||||
}>
|
||||
<ContextMenu
|
||||
accessibilityLabel = { t(toolbarAccLabel) }
|
||||
|
@ -1509,6 +1517,7 @@ class Toolbox extends Component<IProps> {
|
|||
*/
|
||||
function _mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
const endConferenceSupported = conference?.isEndConferenceSupported();
|
||||
|
||||
const {
|
||||
|
@ -1551,6 +1560,7 @@ function _mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
|||
_jwtDisabledButons: getJwtDisabledButtons(state),
|
||||
_hasSalesforce: isSalesforceEnabled(state),
|
||||
_hangupMenuVisible: hangupMenuVisible,
|
||||
_isNarrowLayout: isNarrowLayout,
|
||||
_localParticipantID: localParticipant?.id,
|
||||
_localVideo: localVideo,
|
||||
_multiStreamModeEnabled: getMultipleVideoSendingSupportFeatureFlag(state),
|
||||
|
|
|
@ -170,13 +170,14 @@ class VideoSettingsButton extends Component<Props> {
|
|||
*/
|
||||
function mapStateToProps(state) {
|
||||
const { permissions = {} } = state['features/base/devices'];
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
hasPermissions: permissions.video,
|
||||
hasVideoTrack: Boolean(getLocalJitsiVideoTrack(state)),
|
||||
isDisabled: isVideoSettingsButtonDisabled(state),
|
||||
isOpen: getVideoSettingsVisibility(state),
|
||||
visible: !isMobileBrowser()
|
||||
visible: !isMobileBrowser() && !isNarrowLayout
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@ class LocalVideoMenuTriggerButton extends Component<IProps> {
|
|||
overflowDrawer = { _overflowDrawer }
|
||||
position = { _menuPosition }
|
||||
visible = { popoverVisible }>
|
||||
{!_overflowDrawer && buttonVisible && !isMobileBrowser() && (
|
||||
{buttonVisible && !isMobileBrowser() && (
|
||||
<Button
|
||||
accessibilityLabel = { t('dialog.localUserControls') }
|
||||
className = { classes.triggerButton }
|
||||
|
|
|
@ -192,9 +192,10 @@ class RemoteVideoMenuTriggerButton extends Component<IProps> {
|
|||
id = 'remote-video-menu-trigger'
|
||||
onPopoverClose = { this._onPopoverClose }
|
||||
onPopoverOpen = { this._onPopoverOpen }
|
||||
overflowDrawer = { _overflowDrawer }
|
||||
position = { this.props._menuPosition }
|
||||
visible = { popoverVisible }>
|
||||
{!_overflowDrawer && buttonVisible && !_disabled && (
|
||||
{ buttonVisible && !_disabled && (
|
||||
!isMobileBrowser() && <Button
|
||||
accessibilityLabel = { this.props.t('dialog.remoteUserControls', { username }) }
|
||||
className = { classes.triggerButton }
|
||||
|
|
Loading…
Reference in New Issue