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:
parent
667a6eac80
commit
366dc8d11b
|
@ -6,6 +6,7 @@ import React from 'react';
|
||||||
import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
|
import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
|
||||||
import { getConferenceNameForTitle } from '../../../base/conference';
|
import { getConferenceNameForTitle } from '../../../base/conference';
|
||||||
import { connect, disconnect } from '../../../base/connection';
|
import { connect, disconnect } from '../../../base/connection';
|
||||||
|
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { connect as reactReduxConnect } from '../../../base/redux';
|
import { connect as reactReduxConnect } from '../../../base/redux';
|
||||||
import { setColorAlpha } from '../../../base/util';
|
import { setColorAlpha } from '../../../base/util';
|
||||||
|
@ -18,6 +19,7 @@ import { getIsLobbyVisible } from '../../../lobby/functions';
|
||||||
import { ParticipantsPane } from '../../../participants-pane/components/web';
|
import { ParticipantsPane } from '../../../participants-pane/components/web';
|
||||||
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
|
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
|
||||||
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
|
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
|
||||||
|
import { toggleToolboxVisible } from '../../../toolbox/actions.any';
|
||||||
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
|
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
|
||||||
import { JitsiPortal, Toolbox } from '../../../toolbox/components/web';
|
import { JitsiPortal, Toolbox } from '../../../toolbox/components/web';
|
||||||
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
||||||
|
@ -119,6 +121,7 @@ class Conference extends AbstractConference<Props, *> {
|
||||||
_onMouseLeave: Function;
|
_onMouseLeave: Function;
|
||||||
_onMouseMove: Function;
|
_onMouseMove: Function;
|
||||||
_onShowToolbar: Function;
|
_onShowToolbar: Function;
|
||||||
|
_onVidespaceTouchStart: Function;
|
||||||
_originalOnMouseMove: Function;
|
_originalOnMouseMove: Function;
|
||||||
_originalOnShowToolbar: Function;
|
_originalOnShowToolbar: Function;
|
||||||
_setBackground: Function;
|
_setBackground: Function;
|
||||||
|
@ -157,6 +160,7 @@ class Conference extends AbstractConference<Props, *> {
|
||||||
|
|
||||||
// Bind event handler so it is only bound once for every instance.
|
// Bind event handler so it is only bound once for every instance.
|
||||||
this._onFullScreenChange = this._onFullScreenChange.bind(this);
|
this._onFullScreenChange = this._onFullScreenChange.bind(this);
|
||||||
|
this._onVidespaceTouchStart = this._onVidespaceTouchStart.bind(this);
|
||||||
this._setBackground = this._setBackground.bind(this);
|
this._setBackground = this._setBackground.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,12 +233,14 @@ class Conference extends AbstractConference<Props, *> {
|
||||||
<div
|
<div
|
||||||
className = { _layoutClassName }
|
className = { _layoutClassName }
|
||||||
id = 'videoconference_page'
|
id = 'videoconference_page'
|
||||||
onMouseMove = { this._onShowToolbar }
|
onMouseMove = { isMobileBrowser() ? undefined : this._onShowToolbar }
|
||||||
ref = { this._setBackground }>
|
ref = { this._setBackground }>
|
||||||
<ConferenceInfo />
|
<ConferenceInfo />
|
||||||
|
|
||||||
<Notice />
|
<Notice />
|
||||||
<div id = 'videospace'>
|
<div
|
||||||
|
id = 'videospace'
|
||||||
|
onTouchStart = { this._onVidespaceTouchStart }>
|
||||||
<LargeVideo />
|
<LargeVideo />
|
||||||
{!_isParticipantsPaneVisible
|
{!_isParticipantsPaneVisible
|
||||||
&& <div id = 'notification-participant-list'>
|
&& <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
|
* Updates the Redux state when full screen mode has been enabled or
|
||||||
* disabled.
|
* disabled.
|
||||||
|
|
|
@ -155,6 +155,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
this._listItemKey = this._listItemKey.bind(this);
|
this._listItemKey = this._listItemKey.bind(this);
|
||||||
this._onGridItemsRendered = this._onGridItemsRendered.bind(this);
|
this._onGridItemsRendered = this._onGridItemsRendered.bind(this);
|
||||||
this._onListItemsRendered = this._onListItemsRendered.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();
|
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
|
* Creates a React Element for changing the visibility of the filmstrip when
|
||||||
* clicked.
|
* clicked.
|
||||||
|
@ -520,6 +535,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
id = 'toggleFilmstripButton'
|
id = 'toggleFilmstripButton'
|
||||||
onClick = { this._onToolbarToggleFilmstrip }
|
onClick = { this._onToolbarToggleFilmstrip }
|
||||||
onFocus = { this._onTabIn }
|
onFocus = { this._onTabIn }
|
||||||
|
onTouchStart = { this._onTouchStart }
|
||||||
tabIndex = { 0 }>
|
tabIndex = { 0 }>
|
||||||
<Icon
|
<Icon
|
||||||
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }
|
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }
|
||||||
|
|
|
@ -258,6 +258,12 @@ class Thumbnail extends Component<Props, State> {
|
||||||
*/
|
*/
|
||||||
videoMenuTriggerRef: Object;
|
videoMenuTriggerRef: Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout used to detect double tapping.
|
||||||
|
* It is active while user has tapped once.
|
||||||
|
*/
|
||||||
|
_firstTap: ?TimeoutID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new Thumbnail instance.
|
* Initializes a new Thumbnail instance.
|
||||||
*
|
*
|
||||||
|
@ -281,6 +287,7 @@ class Thumbnail extends Component<Props, State> {
|
||||||
this.timeoutHandle = null;
|
this.timeoutHandle = null;
|
||||||
this.videoMenuTriggerRef = null;
|
this.videoMenuTriggerRef = null;
|
||||||
|
|
||||||
|
this._clearDoubleClickTimeout = this._clearDoubleClickTimeout.bind(this);
|
||||||
this._setInstance = this._setInstance.bind(this);
|
this._setInstance = this._setInstance.bind(this);
|
||||||
this._updateAudioLevel = this._updateAudioLevel.bind(this);
|
this._updateAudioLevel = this._updateAudioLevel.bind(this);
|
||||||
this._onCanPlay = this._onCanPlay.bind(this);
|
this._onCanPlay = this._onCanPlay.bind(this);
|
||||||
|
@ -437,6 +444,18 @@ class Thumbnail extends Component<Props, State> {
|
||||||
this._stopListeningForAudioUpdates(this.props._audioTrack);
|
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.
|
* Starts listening for audio level updates from the library.
|
||||||
*
|
*
|
||||||
|
@ -580,12 +599,21 @@ class Thumbnail extends Component<Props, State> {
|
||||||
_onTouchStart: () => void;
|
_onTouchStart: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set showing popover context menu after x miliseconds.
|
* Handler for touch start.
|
||||||
*
|
*
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onTouchStart() {
|
_onTouchStart() {
|
||||||
this.timeoutHandle = setTimeout(this._showPopupMenu, SHOW_TOOLBAR_CONTEXT_MENU_AFTER);
|
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;
|
_onTouchEnd: () => void;
|
||||||
|
@ -790,7 +818,6 @@ class Thumbnail extends Component<Props, State> {
|
||||||
<span
|
<span
|
||||||
className = { containerClassName }
|
className = { containerClassName }
|
||||||
id = 'localVideoContainer'
|
id = 'localVideoContainer'
|
||||||
onClick = { this._onClick }
|
|
||||||
{ ...(_isMobile
|
{ ...(_isMobile
|
||||||
? {
|
? {
|
||||||
onTouchEnd: this._onTouchEnd,
|
onTouchEnd: this._onTouchEnd,
|
||||||
|
@ -798,6 +825,7 @@ class Thumbnail extends Component<Props, State> {
|
||||||
onTouchStart: this._onTouchStart
|
onTouchStart: this._onTouchStart
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
|
onClick: this._onClick,
|
||||||
onMouseEnter: this._onMouseEnter,
|
onMouseEnter: this._onMouseEnter,
|
||||||
onMouseLeave: this._onMouseLeave
|
onMouseLeave: this._onMouseLeave
|
||||||
}
|
}
|
||||||
|
@ -932,7 +960,7 @@ class Thumbnail extends Component<Props, State> {
|
||||||
<span
|
<span
|
||||||
className = { containerClassName }
|
className = { containerClassName }
|
||||||
id = { `participant_${id}` }
|
id = { `participant_${id}` }
|
||||||
onClick = { this._onClick }
|
onClick = { _isMobile ? undefined : this._onClick }
|
||||||
{ ...(_isMobile
|
{ ...(_isMobile
|
||||||
? {
|
? {
|
||||||
onTouchEnd: this._onTouchEnd,
|
onTouchEnd: this._onTouchEnd,
|
||||||
|
|
|
@ -4,7 +4,8 @@ import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SET_TOOLBOX_ENABLED,
|
SET_TOOLBOX_ENABLED,
|
||||||
SET_TOOLBOX_VISIBLE
|
SET_TOOLBOX_VISIBLE,
|
||||||
|
TOGGLE_TOOLBOX_VISIBLE
|
||||||
} from './actionTypes';
|
} 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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,28 +1,2 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import type { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
import { TOGGLE_TOOLBOX_VISIBLE } from './actionTypes';
|
|
||||||
|
|
||||||
export * from './actions.any';
|
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
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { overwriteConfig } from '../base/config';
|
import { overwriteConfig } from '../base/config';
|
||||||
|
import { isMobileBrowser } from '../base/environment/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CLEAR_TOOLBOX_TIMEOUT,
|
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 {Function} handler - Function to be invoked after the timeout.
|
||||||
* @param {number} timeoutMS - Delay.
|
* @param {number} timeoutMS - Delay.
|
||||||
|
@ -231,10 +233,15 @@ export function setToolbarHovered(hovered: boolean): Object {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function setToolboxTimeout(handler: Function, timeoutMS: number): Object {
|
export function setToolboxTimeout(handler: Function, timeoutMS: number): Object {
|
||||||
return {
|
return function(dispatch) {
|
||||||
type: SET_TOOLBOX_TIMEOUT,
|
if (isMobileBrowser()) {
|
||||||
handler,
|
return;
|
||||||
timeoutMS
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: SET_TOOLBOX_TIMEOUT,
|
||||||
|
handler,
|
||||||
|
timeoutMS
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue