From e2e9633847b2f11e5035fa742c4307677398e2b9 Mon Sep 17 00:00:00 2001 From: Robert Daly Date: Tue, 19 Jul 2022 22:19:01 -0400 Subject: [PATCH] finished with swiping to change images --- src/CONST.js | 1 + src/components/AttachmentCarousel.js | 21 +++-- src/components/SwipeableView/index.native.js | 84 ++++++++++++++++---- 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 3fd74205bb05..826a1d1c35d4 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -468,6 +468,7 @@ const CONST = { EMOJI_PICKER_HEADER_HEIGHT: 38, COMPOSER_MAX_HEIGHT: 125, + MAX_HORIZONTAL_SWIPE: 75, EMAIL: { CONCIERGE: 'concierge@expensify.com', diff --git a/src/components/AttachmentCarousel.js b/src/components/AttachmentCarousel.js index 440960f2ed83..e240323ec854 100644 --- a/src/components/AttachmentCarousel.js +++ b/src/components/AttachmentCarousel.js @@ -7,6 +7,7 @@ import Button from './Button'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; import AttachmentView from './AttachmentView'; +import SwipeableView from './SwipeableView'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import themeColors from '../styles/themes/default'; import reportActionPropTypes from '../pages/home/report/reportActionPropTypes'; @@ -28,8 +29,8 @@ const propTypes = { const defaultProps = { sourceURL: '', - onArrowPress: () => {}, reportActions: {}, + onArrowPress: () => {}, }; class AttachmentCarousel extends React.Component { @@ -62,7 +63,7 @@ class AttachmentCarousel extends React.Component { attachments, sourceURL, file, - showArrows: !canUseTouchScreen(), + showArrows: canUseTouchScreen(), isBackDisabled: page === 0, isForwardDisabled: page === attachments.length - 1, }; @@ -113,6 +114,9 @@ class AttachmentCarousel extends React.Component { * @param {Number} deltaSlide */ cycleThroughAttachments(deltaSlide) { + if ((deltaSlide < 0 && this.state.isBackDisabled) || (deltaSlide > 0 && this.state.isForwardDisabled)) { + return; + } this.setState(({attachments, page}) => { const nextIndex = page + deltaSlide; const {sourceURL, file} = this.getAttachment(attachments[nextIndex]); @@ -133,10 +137,10 @@ class AttachmentCarousel extends React.Component { * @param {Object} e */ handleArrowPress(e) { - if (e.key === 'ArrowLeft' && !this.state.isBackDisabled) { + if (e.key === 'ArrowLeft') { this.cycleThroughAttachments(-1); } - if (e.key === 'ArrowRight' && !this.state.isForwardDisabled) { + if (e.key === 'ArrowRight') { this.cycleThroughAttachments(1); } } @@ -145,7 +149,6 @@ class AttachmentCarousel extends React.Component { return ( canUseTouchScreen() && this.onShowArrow(!this.state.showArrows)} onMouseEnter={() => this.onShowArrow(true)} onMouseLeave={() => this.onShowArrow(false)} > @@ -171,7 +174,13 @@ class AttachmentCarousel extends React.Component { /> )} - + canUseTouchScreen() && this.onShowArrow(!this.state.showArrows)} + onSwipeHorizontal={this.cycleThroughAttachments} + > + + ); diff --git a/src/components/SwipeableView/index.native.js b/src/components/SwipeableView/index.native.js index f0f6167ba3b6..4c7f8be3bb9b 100644 --- a/src/components/SwipeableView/index.native.js +++ b/src/components/SwipeableView/index.native.js @@ -1,39 +1,90 @@ -import React, {PureComponent} from 'react'; -import {PanResponder, View} from 'react-native'; +import React, {Component} from 'react'; +import {PanResponder, View, Animated} from 'react-native'; import PropTypes from 'prop-types'; + import CONST from '../../CONST'; const propTypes = { children: PropTypes.element.isRequired, /** Callback to fire when the user swipes down on the child content */ - onSwipeDown: PropTypes.func.isRequired, + onSwipeDown: PropTypes.func, + + /** Callback to fire when swiping left or right */ + onSwipeHorizontal: PropTypes.func, + + /** Callback to facility an press event */ + onPress: PropTypes.func, + + /** should the movement be animated */ + isAnimated: PropTypes.bool, }; -class SwipeableView extends PureComponent { +const defaultProps = { + onSwipeDown: () => {}, + onSwipeHorizontal: () => {}, + onPress: () => {}, + isAnimated: false, +}; + +class SwipeableView extends Component { constructor(props) { super(props); - const minimumPixelDistance = CONST.COMPOSER_MAX_HEIGHT; - this.oldY = 0; + if (this.props.isAnimated) { + this.pan = new Animated.ValueXY(); + } + this.panResponder = PanResponder.create({ + onMoveShouldSetPanResponderCapture: (_event, gestureState) => { + if (gestureState.dy < CONST.COMPOSER_MAX_HEIGHT) { return; } + return true; + }, - // The PanResponder gets focus only when the y-axis movement is over minimumPixelDistance - // & swip direction is downwards - onMoveShouldSetPanResponderCapture: - (_event, gestureState) => { - if ((gestureState.dy - this.oldY) > 0 && gestureState.dy > minimumPixelDistance) { - return true; - } - this.oldY = gestureState.dy; + onStartShouldSetPanResponder: () => this.props.isAnimated, + onPanResponderMove: (event, gestureState) => { + if (!this.props.isAnimated) { return; } + return Animated.event([null, { + dx: this.pan.x, + dy: this.pan.y, + }], {useNativeDriver: false})(event, gestureState); }, - // Calls the callback when the swipe down is released; after the completion of the gesture - onPanResponderRelease: this.props.onSwipeDown, + onPanResponderRelease: (event, gestureState) => { + if (!this.props.isAnimated) { + return this.props.onSwipeDown(); + } + + // For swiping through images, I needed to catch a single press to hide the arrows + if (gestureState.dx === 0 && gestureState.dy === 0) { + return this.props.onPress(); + } + + if (Math.abs(gestureState.dx) > CONST.MAX_HORIZONTAL_SWIPE) { + const deltaSlide = gestureState.dx > 0 ? -1 : 1; + this.props.onSwipeHorizontal(deltaSlide); + } + + Animated.spring(this.pan, {useNativeDriver: false, toValue: {x: 0, y: 0}}).start(); + }, }); } render() { + if (this.props.isAnimated) { + return ( + + {this.props.children} + + ); + } + return ( // eslint-disable-next-line react/jsx-props-no-spreading @@ -44,5 +95,6 @@ class SwipeableView extends PureComponent { } SwipeableView.propTypes = propTypes; +SwipeableView.defaultProps = defaultProps; export default SwipeableView;