diff --git a/react/features/base/responsive-ui/actionTypes.ts b/react/features/base/responsive-ui/actionTypes.ts index 7d092f525..56f26c132 100644 --- a/react/features/base/responsive-ui/actionTypes.ts +++ b/react/features/base/responsive-ui/actionTypes.ts @@ -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'; + diff --git a/react/features/base/responsive-ui/actions.ts b/react/features/base/responsive-ui/actions.ts index 1232d0bcd..7b89a7516 100644 --- a/react/features/base/responsive-ui/actions.ts +++ b/react/features/base/responsive-ui/actions.ts @@ -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 + }; +} diff --git a/react/features/base/responsive-ui/reducer.ts b/react/features/base/responsive-ui/reducer.ts index b2d7fa136..483803c9a 100644 --- a/react/features/base/responsive-ui/reducer.ts +++ b/react/features/base/responsive-ui/reducer.ts @@ -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('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; diff --git a/react/features/connection-indicator/components/web/ConnectionIndicatorContent.js b/react/features/connection-indicator/components/web/ConnectionIndicatorContent.js index 964deeb8a..0081e2cf0 100644 --- a/react/features/connection-indicator/components/web/ConnectionIndicatorContent.js +++ b/react/features/connection-indicator/components/web/ConnectionIndicatorContent.js @@ -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 { 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(); diff --git a/react/features/filmstrip/subscriber.web.ts b/react/features/filmstrip/subscriber.web.ts index 10cff348f..bfc262b1b 100644 --- a/react/features/filmstrip/subscriber.web.ts +++ b/react/features/filmstrip/subscriber.web.ts @@ -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)); }); /** diff --git a/react/features/participants-pane/components/breakout-rooms/components/web/RoomList.js b/react/features/participants-pane/components/breakout-rooms/components/web/RoomList.js index 02516c9a7..358173ed5 100644 --- a/react/features/participants-pane/components/breakout-rooms/components/web/RoomList.js +++ b/react/features/participants-pane/components/breakout-rooms/components/web/RoomList.js @@ -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) => { - {!_overflowDrawer && <> + {!isMobileBrowser() && <> {!hideJoinRoomButton && } {isLocalModerator && !room.isMainRoom && } diff --git a/react/features/reactions/components/web/ReactionsMenu.tsx b/react/features/reactions/components/web/ReactionsMenu.tsx index 85cca476b..c1b2ffaef 100644 --- a/react/features/reactions/components/web/ReactionsMenu.tsx +++ b/react/features/reactions/components/web/ReactionsMenu.tsx @@ -236,10 +236,11 @@ class ReactionsMenu extends Component { */ 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) diff --git a/react/features/reactions/components/web/ReactionsMenuButton.tsx b/react/features/reactions/components/web/ReactionsMenuButton.tsx index 8732a5785..042833944 100644 --- a/react/features/reactions/components/web/ReactionsMenuButton.tsx +++ b/react/features/reactions/components/web/ReactionsMenuButton.tsx @@ -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 (
- {!_reactionsEnabled || isMobile ? ( + {!_reactionsEnabled || isNarrow ? ( { */ 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 }; } diff --git a/react/features/toolbox/components/web/OverflowMenuButton.tsx b/react/features/toolbox/components/web/OverflowMenuButton.tsx index ad55fb915..0d5a794de 100644 --- a/react/features/toolbox/components/web/OverflowMenuButton.tsx +++ b/react/features/toolbox/components/web/OverflowMenuButton.tsx @@ -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(() => { diff --git a/react/features/toolbox/components/web/Toolbox.tsx b/react/features/toolbox/components/web/Toolbox.tsx index df8d92628..5313ab890 100644 --- a/react/features/toolbox/components/web/Toolbox.tsx +++ b/react/features/toolbox/components/web/Toolbox.tsx @@ -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 { _hasSalesforce, _isIosMobile, _isMobile, + _isNarrowLayout, _isSpeakerStatsDisabled, _multiStreamModeEnabled, + _reactionsEnabled, _screenSharing, _whiteboardEnabled } = this.props; @@ -748,7 +755,7 @@ class Toolbox extends Component { group: 2 }; - const raisehand = { + const raisehand = (!_reactionsEnabled || (!_isNarrowLayout && !_isMobile)) && { key: 'raisehand', Content: ReactionsMenuButton, handleClick: this._onToolbarToggleRaiseHand, @@ -1387,6 +1394,7 @@ class Toolbox extends Component { _endConferenceSupported, _hangupMenuVisible, _isMobile, + _isNarrowLayout, _overflowDrawer, _overflowMenuVisible, _reactionsEnabled, @@ -1396,7 +1404,7 @@ class Toolbox extends Component { } = 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 { key = 'overflow-menu' onVisibilityChange = { this._onSetOverflowVisible } showMobileReactions = { - _reactionsEnabled && overflowMenuButtons.find(({ key }) => key === 'raisehand') + _reactionsEnabled && (_isMobile || _isNarrowLayout) }> { */ function _mapStateToProps(state: IReduxState, ownProps: Partial) { 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) { _jwtDisabledButons: getJwtDisabledButtons(state), _hasSalesforce: isSalesforceEnabled(state), _hangupMenuVisible: hangupMenuVisible, + _isNarrowLayout: isNarrowLayout, _localParticipantID: localParticipant?.id, _localVideo: localVideo, _multiStreamModeEnabled: getMultipleVideoSendingSupportFeatureFlag(state), diff --git a/react/features/toolbox/components/web/VideoSettingsButton.js b/react/features/toolbox/components/web/VideoSettingsButton.js index 191f8934f..1945fce16 100644 --- a/react/features/toolbox/components/web/VideoSettingsButton.js +++ b/react/features/toolbox/components/web/VideoSettingsButton.js @@ -170,13 +170,14 @@ class VideoSettingsButton extends Component { */ 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 }; } diff --git a/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx b/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx index 1ccf58bc9..8d1501d81 100644 --- a/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx +++ b/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx @@ -221,7 +221,7 @@ class LocalVideoMenuTriggerButton extends Component { overflowDrawer = { _overflowDrawer } position = { _menuPosition } visible = { popoverVisible }> - {!_overflowDrawer && buttonVisible && !isMobileBrowser() && ( + {buttonVisible && !isMobileBrowser() && (