fix(toolbar): Hide/Show toolbar on tap on mobile web.

* A tap on video space will toggle the toolbar.
* Double tapping on a tile will pin the participant.
This commit is contained in:
Vlad Piersec 2021-10-25 12:16:03 +03:00 committed by vp8x8
parent 667a6eac80
commit 366dc8d11b
6 changed files with 101 additions and 38 deletions

View File

@ -6,6 +6,7 @@ import React from 'react';
import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
import { getConferenceNameForTitle } from '../../../base/conference';
import { connect, disconnect } from '../../../base/connection';
import { isMobileBrowser } from '../../../base/environment/utils';
import { translate } from '../../../base/i18n';
import { connect as reactReduxConnect } from '../../../base/redux';
import { setColorAlpha } from '../../../base/util';
@ -18,6 +19,7 @@ import { getIsLobbyVisible } from '../../../lobby/functions';
import { ParticipantsPane } from '../../../participants-pane/components/web';
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
import { toggleToolboxVisible } from '../../../toolbox/actions.any';
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
import { JitsiPortal, Toolbox } from '../../../toolbox/components/web';
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
@ -119,6 +121,7 @@ class Conference extends AbstractConference<Props, *> {
_onMouseLeave: Function;
_onMouseMove: Function;
_onShowToolbar: Function;
_onVidespaceTouchStart: Function;
_originalOnMouseMove: Function;
_originalOnShowToolbar: Function;
_setBackground: Function;
@ -157,6 +160,7 @@ class Conference extends AbstractConference<Props, *> {
// Bind event handler so it is only bound once for every instance.
this._onFullScreenChange = this._onFullScreenChange.bind(this);
this._onVidespaceTouchStart = this._onVidespaceTouchStart.bind(this);
this._setBackground = this._setBackground.bind(this);
}
@ -229,12 +233,14 @@ class Conference extends AbstractConference<Props, *> {
<div
className = { _layoutClassName }
id = 'videoconference_page'
onMouseMove = { this._onShowToolbar }
onMouseMove = { isMobileBrowser() ? undefined : this._onShowToolbar }
ref = { this._setBackground }>
<ConferenceInfo />
<Notice />
<div id = 'videospace'>
<div
id = 'videospace'
onTouchStart = { this._onVidespaceTouchStart }>
<LargeVideo />
{!_isParticipantsPaneVisible
&& <div id = 'notification-participant-list'>
@ -292,6 +298,16 @@ class Conference extends AbstractConference<Props, *> {
}
}
/**
* Handler used for touch start on Video container.
*
* @private
* @returns {void}
*/
_onVidespaceTouchStart() {
this.props.dispatch(toggleToolboxVisible());
}
/**
* Updates the Redux state when full screen mode has been enabled or
* disabled.

View File

@ -155,6 +155,7 @@ class Filmstrip extends PureComponent <Props> {
this._listItemKey = this._listItemKey.bind(this);
this._onGridItemsRendered = this._onGridItemsRendered.bind(this);
this._onListItemsRendered = this._onListItemsRendered.bind(this);
this._onTouchStart = this._onTouchStart.bind(this);
}
/**
@ -500,6 +501,20 @@ class Filmstrip extends PureComponent <Props> {
this._doToggleFilmstrip();
}
_onTouchStart: (SyntheticEvent<HTMLButtonElement>) => void;
/**
* Handler for onTouchStart.
*
* @private
* @param {Object} e - The synthetic event.
* @returns {void}
*/
_onTouchStart(e: SyntheticEvent<HTMLButtonElement>) {
// Don't propagate the touchStart event so the toolbar doesn't get toggled.
e.stopPropagation();
}
/**
* Creates a React Element for changing the visibility of the filmstrip when
* clicked.
@ -520,6 +535,7 @@ class Filmstrip extends PureComponent <Props> {
id = 'toggleFilmstripButton'
onClick = { this._onToolbarToggleFilmstrip }
onFocus = { this._onTabIn }
onTouchStart = { this._onTouchStart }
tabIndex = { 0 }>
<Icon
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }

View File

@ -258,6 +258,12 @@ class Thumbnail extends Component<Props, State> {
*/
videoMenuTriggerRef: Object;
/**
* Timeout used to detect double tapping.
* It is active while user has tapped once.
*/
_firstTap: ?TimeoutID;
/**
* Initializes a new Thumbnail instance.
*
@ -281,6 +287,7 @@ class Thumbnail extends Component<Props, State> {
this.timeoutHandle = null;
this.videoMenuTriggerRef = null;
this._clearDoubleClickTimeout = this._clearDoubleClickTimeout.bind(this);
this._setInstance = this._setInstance.bind(this);
this._updateAudioLevel = this._updateAudioLevel.bind(this);
this._onCanPlay = this._onCanPlay.bind(this);
@ -437,6 +444,18 @@ class Thumbnail extends Component<Props, State> {
this._stopListeningForAudioUpdates(this.props._audioTrack);
}
_clearDoubleClickTimeout: () => void
/**
* Clears the first click timeout.
*
* @returns {void}
*/
_clearDoubleClickTimeout() {
clearTimeout(this._firstTap);
this._firstTap = undefined;
}
/**
* Starts listening for audio level updates from the library.
*
@ -580,12 +599,21 @@ class Thumbnail extends Component<Props, State> {
_onTouchStart: () => void;
/**
* Set showing popover context menu after x miliseconds.
* Handler for touch start.
*
* @returns {void}
*/
_onTouchStart() {
this.timeoutHandle = setTimeout(this._showPopupMenu, SHOW_TOOLBAR_CONTEXT_MENU_AFTER);
if (this._firstTap) {
this._clearDoubleClickTimeout();
this._onClick();
return;
}
this._firstTap = setTimeout(this._clearDoubleClickTimeout, 300);
}
_onTouchEnd: () => void;
@ -790,7 +818,6 @@ class Thumbnail extends Component<Props, State> {
<span
className = { containerClassName }
id = 'localVideoContainer'
onClick = { this._onClick }
{ ...(_isMobile
? {
onTouchEnd: this._onTouchEnd,
@ -798,6 +825,7 @@ class Thumbnail extends Component<Props, State> {
onTouchStart: this._onTouchStart
}
: {
onClick: this._onClick,
onMouseEnter: this._onMouseEnter,
onMouseLeave: this._onMouseLeave
}
@ -932,7 +960,7 @@ class Thumbnail extends Component<Props, State> {
<span
className = { containerClassName }
id = { `participant_${id}` }
onClick = { this._onClick }
onClick = { _isMobile ? undefined : this._onClick }
{ ...(_isMobile
? {
onTouchEnd: this._onTouchEnd,

View File

@ -4,7 +4,8 @@ import type { Dispatch } from 'redux';
import {
SET_TOOLBOX_ENABLED,
SET_TOOLBOX_VISIBLE
SET_TOOLBOX_VISIBLE,
TOGGLE_TOOLBOX_VISIBLE
} from './actionTypes';
/**
@ -43,3 +44,24 @@ export function setToolboxVisible(visible: boolean): Object {
});
};
}
/**
* Action to toggle the toolbox visibility.
*
* @returns {Function}
*/
export function toggleToolboxVisible() {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
const { visible } = state['features/toolbox'];
if (visible && alwaysVisible) {
return;
}
dispatch({
type: TOGGLE_TOOLBOX_VISIBLE
});
};
}

View File

@ -1,28 +1,2 @@
// @flow
import type { Dispatch } from 'redux';
import { TOGGLE_TOOLBOX_VISIBLE } from './actionTypes';
export * from './actions.any';
/**
* Action to toggle the toolbox visibility.
*
* @returns {Function}
*/
export function toggleToolboxVisible() {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
const { visible } = state['features/toolbox'];
if (visible && alwaysVisible) {
return;
}
dispatch({
type: TOGGLE_TOOLBOX_VISIBLE
});
};
}

View File

@ -3,6 +3,7 @@
import type { Dispatch } from 'redux';
import { overwriteConfig } from '../base/config';
import { isMobileBrowser } from '../base/environment/utils';
import {
CLEAR_TOOLBOX_TIMEOUT,
@ -220,7 +221,8 @@ export function setToolbarHovered(hovered: boolean): Object {
}
/**
* Dispatches an action which sets new timeout and clears the previous one.
* Dispatches an action which sets new timeout for the toolbox visibility and clears the previous one.
* On mobile browsers the toolbox does not hide on timeout. It is toggled on simple tap.
*
* @param {Function} handler - Function to be invoked after the timeout.
* @param {number} timeoutMS - Delay.
@ -231,10 +233,15 @@ export function setToolbarHovered(hovered: boolean): Object {
* }}
*/
export function setToolboxTimeout(handler: Function, timeoutMS: number): Object {
return {
type: SET_TOOLBOX_TIMEOUT,
handler,
timeoutMS
return function(dispatch) {
if (isMobileBrowser()) {
return;
}
dispatch({
type: SET_TOOLBOX_TIMEOUT,
handler,
timeoutMS
});
};
}