diff --git a/css/_toolbars.scss b/css/_toolbars.scss index 18d8314e72..e771f3d67f 100644 --- a/css/_toolbars.scss +++ b/css/_toolbars.scss @@ -32,6 +32,14 @@ pointer-events: none; z-index: $toolbarZ + 2; + &.shift-up { + bottom: calc(((#{$newToolbarSize} + 30px) * 2) * -1); + + .toolbox-content { + margin-bottom: 46px; + } + } + &.visible { bottom: 0; } diff --git a/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx b/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx index 2dcded8faa..289ca8dc39 100644 --- a/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx +++ b/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx @@ -61,7 +61,7 @@ const ThumbnailBottomIndicators = ({ showStatusIndicators = true, thumbnailType }: IProps) => { - const { classes: styles } = useStyles(); + const { classes: styles, cx } = useStyles(); const _allowEditing = !useSelector(isNameReadOnly); const _defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME; const _showDisplayName = useSelector(isDisplayNameVisible); @@ -69,7 +69,7 @@ const ThumbnailBottomIndicators = ({ (state: IReduxState) => isScreenShareParticipantById(state, participantId) ); - return (
+ return (
{ showStatusIndicators && { return (
{ this._renderToolboxContent() }
@@ -1614,6 +1620,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) { _reactionsButtonEnabled: isReactionsButtonEnabled(state), _reactionsEnabled, _screenSharing: isScreenVideoShared(state), + _shiftUp: state['features/toolbox'].shiftUp, _shortcutsEnabled: areKeyboardShortcutsEnabled(state), _tileViewEnabled: shouldDisplayTileView(state), _toolbarButtons: toolbarButtons, diff --git a/react/features/toolbox/reducer.ts b/react/features/toolbox/reducer.ts index ed85409b95..2b106458e4 100644 --- a/react/features/toolbox/reducer.ts +++ b/react/features/toolbox/reducer.ts @@ -9,6 +9,7 @@ import { SET_OVERFLOW_MENU_VISIBLE, SET_TOOLBAR_HOVERED, SET_TOOLBOX_ENABLED, + SET_TOOLBOX_SHIFT_UP, SET_TOOLBOX_TIMEOUT, SET_TOOLBOX_VISIBLE, TOGGLE_TOOLBOX_VISIBLE @@ -55,6 +56,11 @@ const INITIAL_STATE = { */ overflowMenuVisible: false, + /** + * Whether to shift the toolbar up (in case it overlaps the tiles names). + */ + shiftUp: false, + /** * A number, non-zero value which identifies the timer created by a call * to setTimeout(). @@ -79,6 +85,7 @@ export interface IToolboxState { hovered: boolean; overflowDrawer: boolean; overflowMenuVisible: boolean; + shiftUp: boolean; timeoutID?: number | null; visible: boolean; } @@ -135,6 +142,12 @@ ReducerRegistry.register( timeoutID: action.timeoutID }; + case SET_TOOLBOX_SHIFT_UP: + return { + ...state, + shiftUp: action.shiftUp + }; + case SET_TOOLBOX_VISIBLE: return set(state, 'visible', action.visible); diff --git a/react/features/toolbox/subscriber.ts b/react/features/toolbox/subscriber.ts index 18504638d3..95c0f7b028 100644 --- a/react/features/toolbox/subscriber.ts +++ b/react/features/toolbox/subscriber.ts @@ -1,6 +1,12 @@ +import { throttle } from 'lodash'; + import { IReduxState, IStore } from '../app/types'; +import { getParticipantCount } from '../base/participants/functions'; import StateListenerRegistry from '../base/redux/StateListenerRegistry'; +import { DEFAULT_MAX_COLUMNS } from '../filmstrip/constants'; +import { isLayoutTileView } from '../video-layout/functions.any'; +import { setShiftUp } from './actions.any'; import { isAudioMuteButtonDisabled } from './functions.any'; /** @@ -18,3 +24,84 @@ StateListenerRegistry.register( } } ); + + +const checkToolboxOverlap = (clientHeight: number, store: IStore) => { + let toolboxRect = document.querySelector('.toolbox-content-items')?.getBoundingClientRect(); + + if (!toolboxRect) { + return; + } + const tiles = document.querySelectorAll('span.videocontainer'); + + if (!tiles.length) { + return; + } + + const toolboxHeight = 48 + 12; // height + padding + const bottomMargin = 16; + + // Set top and bottom manually to avoid wrong coordinates + // caused by the hiding/ showing of the toolbox. + toolboxRect = { + ...toolboxRect, + top: clientHeight - toolboxHeight - bottomMargin, + bottom: clientHeight - bottomMargin, + left: toolboxRect.left, + right: toolboxRect.right + }; + let isIntersecting = false; + + const rows = store.getState()['features/filmstrip'].tileViewDimensions?.gridDimensions?.rows; + const noOfTilesToCheck = rows === 1 ? tiles.length : DEFAULT_MAX_COLUMNS - 1; + + for (let i = 1; i < Math.max(noOfTilesToCheck, tiles.length); i++) { + const tile = tiles[tiles.length - i]; + const indicatorsRect = tile?.querySelector('.bottom-indicators')?.getBoundingClientRect(); + + if (!indicatorsRect) { + // eslint-disable-next-line no-continue + continue; + } + + if (indicatorsRect.top <= toolboxRect.bottom + && indicatorsRect.right >= toolboxRect.left + && indicatorsRect.bottom >= toolboxRect.top + && indicatorsRect.left <= toolboxRect.right + ) { + isIntersecting = true; + break; + } + } + + store.dispatch(setShiftUp(isIntersecting)); +}; + +const throttledCheckOverlap = throttle(checkToolboxOverlap, 100, { + leading: false, + trailing: true +}); + +/** + * Listens for changes in the selected layout to calculate the dimensions of the tile view grid and horizontal view. + */ +StateListenerRegistry.register( + /* selector */ state => { + const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + + return { + participantCount: getParticipantCount(state), + clientHeight, + clientWidth, + isTileView: isLayoutTileView(state) + }; + }, + /* listener */({ clientHeight, isTileView }, store) => { + if (!isTileView) { + return; + } + throttledCheckOverlap(clientHeight, store); + + }, { + deepEquals: true + });