diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 5552f15320f3..878b4eef9539 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -37,15 +37,6 @@ const propTypes = { transactionID: PropTypes.string, }).isRequired, - /** Whether there is only one element in the attachment carousel */ - isSingleItem: PropTypes.bool.isRequired, - - /** The index of the carousel item */ - index: PropTypes.number.isRequired, - - /** The index of the currently active carousel item */ - activeIndex: PropTypes.number.isRequired, - /** onPress callback */ onPress: PropTypes.func, }; @@ -54,7 +45,7 @@ const defaultProps = { onPress: undefined, }; -function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { +function CarouselItem({item, onPress}) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isAttachmentHidden} = useContext(ReportAttachmentsContext); @@ -104,10 +95,6 @@ function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { source={item.source} file={item.file} isAuthTokenRequired={item.isAuthTokenRequired} - isUsedInCarousel - isSingleCarouselItem={isSingleItem} - carouselItemIndex={index} - carouselActiveItemIndex={activeIndex} onPress={onPress} transactionID={item.transactionID} /> diff --git a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts index 270e0b04909c..fd9b57511cc4 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts +++ b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts @@ -3,7 +3,24 @@ import {createContext} from 'react'; import type PagerView from 'react-native-pager-view'; import type {SharedValue} from 'react-native-reanimated'; +/** The pager items array is used within the pager to render and navigate between the images */ +type AttachmentCarouselPagerItems = { + /** The source of the image is used to identify each attachment/page in the pager */ + source: string; + + /** The index of the pager item determines the order of the images in the pager */ + index: number; + + /** The active state of the pager item determines whether the image is currently transformable with pinch, pan and tap gestures */ + isActive: boolean; +}; + type AttachmentCarouselPagerContextValue = { + /** The list of items that are shown in the pager */ + pagerItems: AttachmentCarouselPagerItems[]; + + /** The index of the active page */ + activePage: number; pagerRef: ForwardedRef; isPagerScrolling: SharedValue; isScrollEnabled: SharedValue; diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx index 490afb6614ac..8704584c3e18 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx @@ -6,6 +6,7 @@ import {createNativeWrapper} from 'react-native-gesture-handler'; import type {PagerViewProps} from 'react-native-pager-view'; import PagerView from 'react-native-pager-view'; import Animated, {useAnimatedProps, useSharedValue} from 'react-native-reanimated'; +import CarouselItem from '@components/Attachments/AttachmentCarousel/CarouselItem'; import useThemeStyles from '@hooks/useThemeStyles'; import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext'; import usePageScrollHandler from './usePageScrollHandler'; @@ -19,21 +20,31 @@ type AttachmentCarouselPagerHandle = { setPage: (selectedPage: number) => void; }; -type PagerItem = { - key: string; - url: string; +type Attachment = { source: string; }; type AttachmentCarouselPagerProps = { - items: PagerItem[]; - renderItem: (props: {item: PagerItem; index: number; isActive: boolean}) => React.ReactNode; - initialIndex: number; + /** The attachments to be rendered in the pager. */ + items: Attachment[]; + + /** The source (URL) of the currently active attachment. */ + activeSource: string; + + /** The index of the initial page to be rendered. */ + initialPage: number; + + /** A callback to be called when the page is changed. */ onPageSelected: () => void; + + /** + * A callback that can be used to toggle the attachment carousel arrows, when the scale of the image changes. + * @param showArrows If set, it will show/hide the arrows. If not set, it will toggle the arrows. + */ onRequestToggleArrows: (showArrows?: boolean) => void; }; -function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelected, onRequestToggleArrows}: AttachmentCarouselPagerProps, ref: ForwardedRef) { +function AttachmentCarouselPager({items, activeSource, initialPage, onPageSelected, onRequestToggleArrows}: AttachmentCarouselPagerProps, ref: ForwardedRef) { const styles = useThemeStyles(); const pagerRef = useRef(null); @@ -41,8 +52,8 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte const isPagerScrolling = useSharedValue(false); const isScrollEnabled = useSharedValue(true); - const activePage = useSharedValue(initialIndex); - const [activePageState, setActivePageState] = useState(initialIndex); + const activePage = useSharedValue(initialPage); + const [activePageIndex, setActivePageIndex] = useState(initialPage); const pageScrollHandler = usePageScrollHandler((e) => { 'worklet'; @@ -52,9 +63,12 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte }, []); useEffect(() => { - setActivePageState(initialIndex); - activePage.value = initialIndex; - }, [activePage, initialIndex]); + setActivePageIndex(initialPage); + activePage.value = initialPage; + }, [activePage, initialPage]); + + /** The `pagerItems` object that passed down to the context. Later used to detect current page, whether it's a single image gallery etc. */ + const pagerItems = useMemo(() => items.map((item, index) => ({source: item.source, index, isActive: index === activePageIndex})), [activePageIndex, items]); /** * This callback is passed to the MultiGestureCanvas/Lightbox through the AttachmentCarouselPagerContext. @@ -94,13 +108,15 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte const contextValue = useMemo( () => ({ - pagerRef, + pagerItems, + activePage: activePageIndex, isPagerScrolling, isScrollEnabled, + pagerRef, onTap: handleTap, onScaleChanged: handleScaleChange, }), - [isPagerScrolling, isScrollEnabled, handleTap, handleScaleChange], + [pagerItems, activePageIndex, isPagerScrolling, isScrollEnabled, handleTap, handleScaleChange], ); const animatedProps = useAnimatedProps(() => ({ @@ -121,6 +137,21 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte [], ); + const carouselItems = items.map((item, index) => ( + + + + )); + return ( - {items.map((item, index) => ( - - {renderItem({item, index, isActive: index === activePageState})} - - ))} + {carouselItems} ); diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js index fa24ccd0ef53..228f0d597a32 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.js +++ b/src/components/Attachments/AttachmentCarousel/index.native.js @@ -13,7 +13,6 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './attachmentCarouselPropTypes'; import CarouselButtons from './CarouselButtons'; -import CarouselItem from './CarouselItem'; import extractAttachmentsFromReport from './extractAttachmentsFromReport'; import AttachmentCarouselPager from './Pager'; import useCarouselArrows from './useCarouselArrows'; @@ -103,24 +102,6 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, [setShouldShowArrows], ); - /** - * Defines how a single attachment should be rendered - * @param {{ reportActionID: String, isAuthTokenRequired: Boolean, source: String, file: { name: String }, hasBeenFlagged: Boolean }} item - * @returns {JSX.Element} - */ - const renderItem = useCallback( - ({item, index, isActive}) => ( - - ), - [activeSource, attachments.length, page], - ); - return ( {page == null ? ( @@ -148,8 +129,8 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, updatePage(newPage)} ref={pagerRef} diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js index 14c60458b044..67f87b1733d3 100755 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js @@ -12,21 +12,7 @@ const propTypes = { ...withLocalizePropTypes, }; -function AttachmentViewImage({ - url, - file, - isAuthTokenRequired, - isUsedInCarousel, - isSingleCarouselItem, - carouselItemIndex, - carouselActiveItemIndex, - isFocused, - loadComplete, - onPress, - onError, - isImage, - translate, -}) { +function AttachmentViewImage({url, file, isAuthTokenRequired, isFocused, loadComplete, onPress, onError, isImage, translate}) { const styles = useThemeStyles(); const children = ( ); diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 33eab13f3851..fafdbd27d844 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -79,9 +79,6 @@ function AttachmentView({ translate, isFocused, isUsedInCarousel, - isSingleCarouselItem, - carouselItemIndex, - carouselActiveItemIndex, isUsedInAttachmentModal, isWorkspaceAvatar, maybeIcon, @@ -147,8 +144,6 @@ function AttachmentView({ isFocused={isFocused} isAuthTokenRequired={isAuthTokenRequired} encryptedSourceUrl={encryptedSourceUrl} - carouselItemIndex={carouselItemIndex} - carouselActiveItemIndex={carouselActiveItemIndex} onPress={onPress} onToggleKeyboard={onToggleKeyboard} onLoadComplete={() => !loadComplete && setLoadComplete(true)} @@ -177,9 +172,6 @@ function AttachmentView({ loadComplete={loadComplete} isFocused={isFocused} isUsedInCarousel={isUsedInCarousel} - isSingleCarouselItem={isSingleCarouselItem} - carouselItemIndex={carouselItemIndex} - carouselActiveItemIndex={carouselActiveItemIndex} isImage={isImage} onPress={onPress} onError={() => { diff --git a/src/components/Attachments/AttachmentView/propTypes.js b/src/components/Attachments/AttachmentView/propTypes.js index d78bed8526b8..0a0d654912d3 100644 --- a/src/components/Attachments/AttachmentView/propTypes.js +++ b/src/components/Attachments/AttachmentView/propTypes.js @@ -14,18 +14,6 @@ const attachmentViewPropTypes = { /** Whether this AttachmentView is shown as part of a AttachmentCarousel */ isUsedInCarousel: PropTypes.bool, - /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */ - isSingleCarouselItem: PropTypes.bool, - - /** Whether this AttachmentView is shown as part of an AttachmentModal */ - isUsedInAttachmentModal: PropTypes.bool, - - /** The index of the carousel item */ - carouselItemIndex: PropTypes.number, - - /** The index of the currently active carousel item */ - carouselActiveItemIndex: PropTypes.number, - /** Function for handle on press */ onPress: PropTypes.func, @@ -39,11 +27,8 @@ const attachmentViewDefaultProps = { name: '', }, isFocused: false, - isUsedInCarousel: false, - isSingleCarouselItem: false, - carouselItemIndex: 0, - carouselActiveItemIndex: 0, isSingleElement: false, + isUsedInCarousel: false, isUsedInAttachmentModal: false, onPress: undefined, onScaleChanged: () => {}, diff --git a/src/components/ImageView/index.native.tsx b/src/components/ImageView/index.native.tsx index 8de1946ef554..24f7ffb1dac3 100644 --- a/src/components/ImageView/index.native.tsx +++ b/src/components/ImageView/index.native.tsx @@ -3,28 +3,13 @@ import Lightbox from '@components/Lightbox'; import {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {ImageViewProps} from './types'; -function ImageView({ - isAuthTokenRequired = false, - url, - style, - zoomRange = DEFAULT_ZOOM_RANGE, - onError, - isUsedInCarousel = false, - isSingleCarouselItem = false, - carouselItemIndex = 0, - carouselActiveItemIndex = 0, -}: ImageViewProps) { - const hasSiblingCarouselItems = isUsedInCarousel && !isSingleCarouselItem; - +function ImageView({isAuthTokenRequired = false, url, style, zoomRange = DEFAULT_ZOOM_RANGE, onError}: ImageViewProps) { return ( ); diff --git a/src/components/ImageView/types.ts b/src/components/ImageView/types.ts index b85115874a5a..bb63373324cb 100644 --- a/src/components/ImageView/types.ts +++ b/src/components/ImageView/types.ts @@ -14,18 +14,6 @@ type ImageViewProps = { /** Handles errors while displaying the image */ onError?: () => void; - /** Whether this AttachmentView is shown as part of a AttachmentCarousel */ - isUsedInCarousel?: boolean; - - /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */ - isSingleCarouselItem?: boolean; - - /** The index of the carousel item */ - carouselItemIndex?: number; - - /** The index of the currently active carousel item */ - carouselActiveItemIndex?: number; - /** Additional styles to add to the component */ style?: StyleProp; diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index aeec1876eb93..36cb175e3c45 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -1,6 +1,8 @@ -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; import type {LayoutChangeEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native'; +import {useSharedValue} from 'react-native-reanimated'; +import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import Image from '@components/Image'; import MultiGestureCanvas, {DEFAULT_ZOOM_RANGE} from '@components/MultiGestureCanvas'; import type {CanvasSize, ContentSize, OnScaleChangedCallback, ZoomRange} from '@components/MultiGestureCanvas/types'; @@ -31,15 +33,6 @@ type LightboxProps = { /** Additional styles to add to the component */ style?: StyleProp; - /** The index of the carousel item */ - index?: number; - - /** The index of the currently active carousel item */ - activeIndex?: number; - - /** Whether the Lightbox is used within a carousel component and there are other sibling elements */ - hasSiblingCarouselItems?: boolean; - /** Range of zoom that can be applied to the content by pinching or double tapping. */ zoomRange?: Partial; }; @@ -47,19 +40,53 @@ type LightboxProps = { /** * On the native layer, we use a image library to handle zoom functionality */ -function Lightbox({ - isAuthTokenRequired = false, - uri, - onScaleChanged, - onError, - style, - index = 0, - activeIndex = 0, - hasSiblingCarouselItems = false, - zoomRange = DEFAULT_ZOOM_RANGE, -}: LightboxProps) { +function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChangedProp, onError, style, zoomRange = DEFAULT_ZOOM_RANGE}: LightboxProps) { const StyleUtils = useStyleUtils(); + /** + * React hooks must be used in the render function of the component at top-level and unconditionally. + * Therefore, in order to provide a default value for "isPagerScrolling" if the "AttachmentCarouselPagerContext" is not available, + * we need to create a shared value that can be used in the render function. + */ + const isPagerScrollingFallback = useSharedValue(false); + + const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); + const { + isUsedInCarousel, + isSingleCarouselItem, + isPagerScrolling, + page, + activePage, + onTap, + onScaleChanged: onScaleChangedContext, + pagerRef, + } = useMemo(() => { + if (attachmentCarouselPagerContext === null) { + return { + isUsedInCarousel: false, + isSingleCarouselItem: true, + isPagerScrolling: isPagerScrollingFallback, + page: 0, + activePage: 0, + onTap: () => {}, + onScaleChanged: () => {}, + pagerRef: undefined, + }; + } + + const foundPage = attachmentCarouselPagerContext.pagerItems.findIndex((item) => item.source === uri); + return { + ...attachmentCarouselPagerContext, + isUsedInCarousel: true, + isSingleCarouselItem: attachmentCarouselPagerContext.pagerItems.length === 1, + page: foundPage, + }; + }, [attachmentCarouselPagerContext, isPagerScrollingFallback, uri]); + + /** Whether the Lightbox is used within an attachment carousel and there are more than one page in the carousel */ + const hasSiblingCarouselItems = isUsedInCarousel && !isSingleCarouselItem; + const isActive = page === activePage; + const [canvasSize, setCanvasSize] = useState(); const isCanvasLoading = canvasSize === undefined; const updateCanvasSize = useCallback( @@ -100,9 +127,9 @@ function Lightbox({ } const indexCanvasOffset = Math.floor((NUMBER_OF_CONCURRENT_LIGHTBOXES - 1) / 2) || 0; - const indexOutOfRange = index > activeIndex + indexCanvasOffset || index < activeIndex - indexCanvasOffset; + const indexOutOfRange = page > activePage + indexCanvasOffset || page < activePage - indexCanvasOffset; return !indexOutOfRange; - }, [activeIndex, hasSiblingCarouselItems, index]); + }, [activePage, hasSiblingCarouselItems, page]); const [isLightboxImageLoaded, setLightboxImageLoaded] = useState(false); const [isFallbackVisible, setFallbackVisible] = useState(!isLightboxVisible); @@ -126,7 +153,6 @@ function Lightbox({ // because it's only going to be rendered after the fallback image is hidden. const shouldShowLightbox = isLightboxImageLoaded && !isFallbackVisible; - const isActive = index === activeIndex; const isFallbackStillLoading = isFallbackVisible && !isFallbackImageLoaded; const isLightboxStillLoading = isLightboxVisible && !isLightboxImageLoaded; const isLoading = isActive && (isCanvasLoading || isFallbackStillLoading || isLightboxStillLoading); @@ -160,6 +186,14 @@ function Lightbox({ } }, [hasSiblingCarouselItems, isActive, isFallbackVisible, isLightboxImageLoaded, isLightboxVisible]); + const scaleChange = useCallback( + (scale: number) => { + onScaleChangedProp?.(scale); + onScaleChangedContext?.(scale); + }, + [onScaleChangedContext, onScaleChangedProp], + ); + return ( ; + /** A shared value of type boolean, that indicates disabled the transformation gestures (pinch, pan, double tap) */ + shouldDisableTransformationGestures?: SharedValue; + + /** If there is a pager wrapping the canvas, we need to disable the pan gesture in case the pager is swiping */ + pagerRef?: ForwardedRef; // TODO: For TS migration: Exclude + /** Handles scale changed event */ onScaleChanged?: OnScaleChangedCallback; + + /** Handles scale changed event */ + onTap?: OnTapCallback; }; function MultiGestureCanvas({ @@ -44,41 +55,16 @@ function MultiGestureCanvas({ zoomRange: zoomRangeProp, isActive = true, children, - onScaleChanged: onScaleChangedProp, + pagerRef, + shouldDisableTransformationGestures: shouldDisableTransformationGesturesProp, + onTap, + onScaleChanged, }: MultiGestureCanvasProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const isSwipingInPagerFallback = useSharedValue(false); - - // If the MultiGestureCanvas used inside a AttachmentCarouselPager, we need to adapt the behaviour based on the pager state - const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); - const { - onTap, - onScaleChanged: onScaleChangedContext, - isPagerScrolling: isPagerSwiping, - pagerRef, - } = useMemo( - () => - attachmentCarouselPagerContext ?? { - onTap: () => {}, - onScaleChanged: () => {}, - pagerRef: undefined, - isPagerScrolling: isSwipingInPagerFallback, - }, - [attachmentCarouselPagerContext, isSwipingInPagerFallback], - ); - - /** - * Calls the onScaleChanged callback from the both props and the pager context - */ - const onScaleChanged = useCallback( - (newScale: number) => { - onScaleChangedProp?.(newScale); - onScaleChangedContext(newScale); - }, - [onScaleChangedContext, onScaleChangedProp], - ); + const shouldDisableTransformationGesturesFallback = useSharedValue(false); + const shouldDisableTransformationGestures = shouldDisableTransformationGesturesProp ?? shouldDisableTransformationGesturesFallback; const zoomRange = useMemo( () => ({ @@ -126,8 +112,6 @@ function MultiGestureCanvas({ const reset = useWorkletCallback((animated: boolean, callback?: () => void) => { stopAnimation(); - pinchScale.value = 1; - if (animated) { offsetX.value = withSpring(0, SPRING_CONFIG); offsetY.value = withSpring(0, SPRING_CONFIG); @@ -155,7 +139,7 @@ function MultiGestureCanvas({ callback(); }); - const {singleTapGesture: basicSingleTapGesture, doubleTapGesture} = useTapGestures({ + const {singleTapGesture: baseSingleTapGesture, doubleTapGesture} = useTapGestures({ canvasSize, contentSize, minContentScale, @@ -168,8 +152,9 @@ function MultiGestureCanvas({ stopAnimation, onScaleChanged, onTap, + shouldDisableTransformationGestures, }); - const singleTapGesture = basicSingleTapGesture.requireExternalGestureToFail(doubleTapGesture, panGestureRef); + const singleTapGesture = baseSingleTapGesture.requireExternalGestureToFail(doubleTapGesture, panGestureRef); const panGestureSimultaneousList = useMemo( () => (pagerRef === undefined ? [singleTapGesture, doubleTapGesture] : [pagerRef as unknown as Exclude, singleTapGesture, doubleTapGesture]), @@ -185,8 +170,8 @@ function MultiGestureCanvas({ offsetY, panTranslateX, panTranslateY, - isPagerSwiping, stopAnimation, + shouldDisableTransformationGestures, }) .simultaneousWithExternalGesture(...panGestureSimultaneousList) .withRef(panGestureRef); @@ -200,9 +185,9 @@ function MultiGestureCanvas({ pinchTranslateX, pinchTranslateY, pinchScale, - isPagerSwiping, stopAnimation, onScaleChanged, + shouldDisableTransformationGestures, }).simultaneousWithExternalGesture(panGesture, singleTapGesture, doubleTapGesture); // Trigger a reset when the canvas gets inactive, but only if it was already mounted before diff --git a/src/components/MultiGestureCanvas/types.ts b/src/components/MultiGestureCanvas/types.ts index bbd8f69e6947..40fcc1462a09 100644 --- a/src/components/MultiGestureCanvas/types.ts +++ b/src/components/MultiGestureCanvas/types.ts @@ -31,7 +31,7 @@ type MultiGestureCanvasVariables = { zoomRange: ZoomRange; minContentScale: number; maxContentScale: number; - isPagerSwiping: SharedValue; + shouldDisableTransformationGestures: SharedValue; zoomScale: SharedValue; totalScale: SharedValue; pinchScale: SharedValue; @@ -43,8 +43,8 @@ type MultiGestureCanvasVariables = { pinchTranslateY: SharedValue; stopAnimation: () => void; reset: (animated: boolean, callback: () => void) => void; - onTap: OnTapCallback; + onTap: OnTapCallback | undefined; onScaleChanged: OnScaleChangedCallback | undefined; }; -export type {CanvasSize, ContentSize, ZoomRange, OnScaleChangedCallback, MultiGestureCanvasVariables}; +export type {CanvasSize, ContentSize, ZoomRange, OnScaleChangedCallback, OnTapCallback, MultiGestureCanvasVariables}; diff --git a/src/components/MultiGestureCanvas/usePanGesture.ts b/src/components/MultiGestureCanvas/usePanGesture.ts index 8a646446fad4..a3f9c7d62df0 100644 --- a/src/components/MultiGestureCanvas/usePanGesture.ts +++ b/src/components/MultiGestureCanvas/usePanGesture.ts @@ -13,10 +13,21 @@ const PAN_DECAY_DECELARATION = 0.9915; type UsePanGestureProps = Pick< MultiGestureCanvasVariables, - 'canvasSize' | 'contentSize' | 'zoomScale' | 'totalScale' | 'offsetX' | 'offsetY' | 'panTranslateX' | 'panTranslateY' | 'isPagerSwiping' | 'stopAnimation' + 'canvasSize' | 'contentSize' | 'zoomScale' | 'totalScale' | 'offsetX' | 'offsetY' | 'panTranslateX' | 'panTranslateY' | 'shouldDisableTransformationGestures' | 'stopAnimation' >; -const usePanGesture = ({canvasSize, contentSize, zoomScale, totalScale, offsetX, offsetY, panTranslateX, panTranslateY, isPagerSwiping, stopAnimation}: UsePanGestureProps): PanGesture => { +const usePanGesture = ({ + canvasSize, + contentSize, + zoomScale, + totalScale, + offsetX, + offsetY, + panTranslateX, + panTranslateY, + shouldDisableTransformationGestures, + stopAnimation, +}: UsePanGestureProps): PanGesture => { // The content size after fitting it to the canvas and zooming const zoomedContentWidth = useDerivedValue(() => contentSize.width * totalScale.value, [contentSize.width]); const zoomedContentHeight = useDerivedValue(() => contentSize.height * totalScale.value, [contentSize.height]); @@ -117,7 +128,7 @@ const usePanGesture = ({canvasSize, contentSize, zoomScale, totalScale, offsetX, // eslint-disable-next-line @typescript-eslint/naming-convention .onTouchesMove((_evt, state) => { // We only allow panning when the content is zoomed in - if (zoomScale.value <= 1 || isPagerSwiping.value) { + if (zoomScale.value <= 1 || shouldDisableTransformationGestures.value) { return; } @@ -147,7 +158,7 @@ const usePanGesture = ({canvasSize, contentSize, zoomScale, totalScale, offsetX, panTranslateY.value = 0; // If we are swiping (in the pager), we don't want to return to boundaries - if (isPagerSwiping.value) { + if (shouldDisableTransformationGestures.value) { return; } diff --git a/src/components/MultiGestureCanvas/usePinchGesture.ts b/src/components/MultiGestureCanvas/usePinchGesture.ts index 2ff375dc7edd..74a2326748a1 100644 --- a/src/components/MultiGestureCanvas/usePinchGesture.ts +++ b/src/components/MultiGestureCanvas/usePinchGesture.ts @@ -8,7 +8,17 @@ import type {MultiGestureCanvasVariables} from './types'; type UsePinchGestureProps = Pick< MultiGestureCanvasVariables, - 'canvasSize' | 'zoomScale' | 'zoomRange' | 'offsetX' | 'offsetY' | 'pinchTranslateX' | 'pinchTranslateY' | 'pinchScale' | 'isPagerSwiping' | 'stopAnimation' | 'onScaleChanged' + | 'canvasSize' + | 'zoomScale' + | 'zoomRange' + | 'offsetX' + | 'offsetY' + | 'pinchTranslateX' + | 'pinchTranslateY' + | 'pinchScale' + | 'shouldDisableTransformationGestures' + | 'stopAnimation' + | 'onScaleChanged' >; const usePinchGesture = ({ @@ -20,7 +30,7 @@ const usePinchGesture = ({ pinchTranslateX: totalPinchTranslateX, pinchTranslateY: totalPinchTranslateY, pinchScale, - isPagerSwiping, + shouldDisableTransformationGestures, stopAnimation, onScaleChanged, }: UsePinchGestureProps): PinchGesture => { @@ -87,10 +97,11 @@ const usePinchGesture = ({ const pinchGesture = Gesture.Pinch() .enabled(pinchEnabled) + // The first argument is not used, but must be defined // eslint-disable-next-line @typescript-eslint/naming-convention .onTouchesDown((_evt, state) => { // We don't want to activate pinch gesture when we are swiping in the pager - if (!isPagerSwiping.value) { + if (!shouldDisableTransformationGestures.value) { return; } diff --git a/src/components/MultiGestureCanvas/useTapGestures.ts b/src/components/MultiGestureCanvas/useTapGestures.ts index ce67f11a91c8..a28333725d6e 100644 --- a/src/components/MultiGestureCanvas/useTapGestures.ts +++ b/src/components/MultiGestureCanvas/useTapGestures.ts @@ -9,7 +9,19 @@ import * as MultiGestureCanvasUtils from './utils'; type UseTapGesturesProps = Pick< MultiGestureCanvasVariables, - 'canvasSize' | 'contentSize' | 'minContentScale' | 'maxContentScale' | 'offsetX' | 'offsetY' | 'pinchScale' | 'zoomScale' | 'reset' | 'stopAnimation' | 'onScaleChanged' | 'onTap' + | 'canvasSize' + | 'contentSize' + | 'minContentScale' + | 'maxContentScale' + | 'offsetX' + | 'offsetY' + | 'pinchScale' + | 'zoomScale' + | 'shouldDisableTransformationGestures' + | 'reset' + | 'stopAnimation' + | 'onScaleChanged' + | 'onTap' >; const useTapGestures = ({ @@ -23,6 +35,7 @@ const useTapGestures = ({ zoomScale, reset, stopAnimation, + shouldDisableTransformationGestures, onScaleChanged, onTap, }: UseTapGesturesProps): {singleTapGesture: TapGesture; doubleTapGesture: TapGesture} => { @@ -107,6 +120,15 @@ const useTapGestures = ({ ); const doubleTapGesture = Gesture.Tap() + // The first argument is not used, but must be defined + // eslint-disable-next-line @typescript-eslint/naming-convention + .onTouchesDown((_evt, state) => { + if (!shouldDisableTransformationGestures.value) { + return; + } + + state.fail(); + }) .numberOfTaps(2) .maxDelay(150) .maxDistance(20)