diff --git a/package-lock.json b/package-lock.json index 64ca24ef3..4d7bdf9db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14873,11 +14873,6 @@ } } }, - "react-native-swipe-gestures": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.4.tgz", - "integrity": "sha512-C/vz0KPHNyqHk3uF4Cz/jzd/0N8z34ZgsjAZUh/RsXPH2FtJJf3Fw73pQDWJSoCMtvVadlztb8xQ+/aEQrll7w==" - }, "react-native-swipeout": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz", diff --git a/package.json b/package.json index 4d6908fac..ca37a0afb 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "react-native-sound": "0.11.0", "react-native-svg": "9.7.1", "react-native-svg-transformer": "0.13.0", - "react-native-swipe-gestures": "1.0.4", "react-native-swipeout": "2.3.6", "react-native-watch-connectivity": "0.2.0", "react-native-webrtc": "1.75.2", diff --git a/react/features/base/dialog/components/native/BottomSheet.js b/react/features/base/dialog/components/native/BottomSheet.js index 6c4c29259..bdd2f29a1 100644 --- a/react/features/base/dialog/components/native/BottomSheet.js +++ b/react/features/base/dialog/components/native/BottomSheet.js @@ -1,7 +1,7 @@ // @flow import React, { PureComponent, type Node } from 'react'; -import { SafeAreaView, ScrollView, View } from 'react-native'; +import { PanResponder, SafeAreaView, ScrollView, View } from 'react-native'; import { ColorSchemeRegistry } from '../../../color-scheme'; import { SlidingView } from '../../../react'; @@ -10,6 +10,16 @@ import { StyleType } from '../../../styles'; import { bottomSheetStyles as styles } from './styles'; +/** + * Minimal distance that needs to be moved by the finger to consider it a swipe. + */ +const GESTURE_DISTANCE_THRESHOLD = 5; + +/** + * The minimal speed needed to be achieved by the finger to consider it as a swipe. + */ +const GESTURE_SPEED_THRESHOLD = 0.2; + /** * The type of {@code BottomSheet}'s React {@code Component} prop types. */ @@ -31,6 +41,11 @@ type Props = { */ onCancel: ?Function, + /** + * Callback to be attached to the custom swipe event of the BottomSheet. + */ + onSwipe?: Function, + /** * Function to render a bottom sheet header element, if necessary. */ @@ -41,6 +56,23 @@ type Props = { * A component emulating Android's BottomSheet. */ class BottomSheet extends PureComponent { + panResponder: Object; + + /** + * Instantiates a new component. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this.panResponder = PanResponder.create({ + onStartShouldSetPanResponder: this._onShouldSetResponder.bind(this), + onMoveShouldSetPanResponder: this._onShouldSetResponder.bind(this), + onPanResponderRelease: this._onGestureEnd.bind(this) + }); + } + /** * Implements React's {@link Component#render()}. * @@ -66,7 +98,8 @@ class BottomSheet extends PureComponent { style = { [ styles.sheetItemContainer, _styles.sheet - ] }> + ] } + { ...this.panResponder.panHandlers }> { ); } + + /** + * Callback to handle a gesture end event. + * + * @param {Object} evt - The native gesture event. + * @param {Object} gestureState - The gesture state. + * @returns {void} + */ + _onGestureEnd(evt, gestureState) { + const verticalSwipe = Math.abs(gestureState.vy) > Math.abs(gestureState.vx) + && Math.abs(gestureState.vy) > GESTURE_SPEED_THRESHOLD; + + if (verticalSwipe) { + const direction = gestureState.vy > 0 ? 'down' : 'up'; + const { onCancel, onSwipe } = this.props; + let isSwipeHandled = false; + + if (onSwipe) { + isSwipeHandled = onSwipe(direction); + } + + if (direction === 'down' && !isSwipeHandled) { + // Swipe down is a special gesture that can be used to close the + // BottomSheet, so if the swipe is not handled by the parent + // component, we consider it as a request to close. + onCancel && onCancel(); + } + } + } + + /** + * Returns true if the pan responder should activate, false otherwise. + * + * @param {Object} evt - The native gesture event. + * @param {Object} gestureState - The gesture state. + * @returns {boolean} + */ + _onShouldSetResponder({ nativeEvent }, gestureState) { + return nativeEvent.touches.length === 1 + && Math.abs(gestureState.dx) > GESTURE_DISTANCE_THRESHOLD + && Math.abs(gestureState.dy) > GESTURE_DISTANCE_THRESHOLD; + } } /** diff --git a/react/features/toolbox/components/native/OverflowMenu.js b/react/features/toolbox/components/native/OverflowMenu.js index 4f8f7c08f..8e575446c 100644 --- a/react/features/toolbox/components/native/OverflowMenu.js +++ b/react/features/toolbox/components/native/OverflowMenu.js @@ -1,9 +1,8 @@ // @flow import React, { PureComponent } from 'react'; -import { Platform, TouchableOpacity } from 'react-native'; +import { Platform, TouchableOpacity, View } from 'react-native'; import Collapsible from 'react-native-collapsible'; -import GestureRecognizer, { swipeDirections } from 'react-native-swipe-gestures'; import { ColorSchemeRegistry } from '../../../base/color-scheme'; import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog'; @@ -59,6 +58,11 @@ type Props = { type State = { + /** + * True if the bottom scheet is scrolled to the top. + */ + scrolledToTop: boolean, + /** * True if the 'more' button set needas to be rendered. */ @@ -88,6 +92,7 @@ class OverflowMenu extends PureComponent { super(props); this.state = { + scrolledToTop: true, showMore: false }; @@ -117,6 +122,7 @@ class OverflowMenu extends PureComponent { return ( @@ -152,12 +158,7 @@ class OverflowMenu extends PureComponent { */ _renderMenuExpandToggle() { return ( - { { /* $FlowFixMeProps */ } - + ); } @@ -188,34 +189,31 @@ class OverflowMenu extends PureComponent { return false; } - _onSwipe: (string) => void; + _onSwipe: string => void; /** - * Callback to be invoked when a swipe gesture is detected on the menu. + * Callback to be invoked when swipe gesture is detected on the menu. Returns true + * if the swipe gesture is handled by the menu, false otherwise. * - * @param {string} gestureName - The name of the swipe gesture. - * @returns {void} + * @param {string} direction - Direction of 'up' or 'down'. + * @returns {boolean} */ - _onSwipe(gestureName) { + _onSwipe(direction) { const { showMore } = this.state; - switch (gestureName) { - case swipeDirections.SWIPE_UP: + switch (direction) { + case 'up': !showMore && this.setState({ showMore: true }); - break; - case swipeDirections.SWIPE_DOWN: - if (showMore) { - // If the menu is expanded, we collapse it. - this.setState({ - showMore: false - }); - } else { - // If the menu is not expanded, we close the menu - this._onCancel(); - } - break; + + return !showMore; + case 'down': + showMore && this.setState({ + showMore: false + }); + + return showMore; } } diff --git a/react/features/toolbox/components/native/styles.js b/react/features/toolbox/components/native/styles.js index a53c959f9..4fc799f06 100644 --- a/react/features/toolbox/components/native/styles.js +++ b/react/features/toolbox/components/native/styles.js @@ -60,6 +60,11 @@ const styles = { flexDirection: 'column' }, + sheetGestureRecognizer: { + alignItems: 'stretch', + flexDirection: 'column' + }, + /** * The style of the toolbar. */