-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix image moves permanently when swipe down #51921
Changes from 17 commits
99a4e93
4835a6a
9b8c5de
27374bc
7306c7d
79a760d
4011c97
f95fd08
0e4bee6
69c5001
67438d0
7fbe39d
b8f1135
ca558c1
7e32422
06be95e
4e53b33
d55ddee
5ecd45e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi | |
const [attachments, setAttachments] = useState<Attachment[]>([]); | ||
const [activeSource, setActiveSource] = useState<AttachmentSource | null>(source); | ||
const {shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows} = useCarouselArrows(); | ||
const {handleTap, handleScaleChange, scale} = useCarouselContextEvents(setShouldShowArrows); | ||
const {handleTap, handleScaleChange, isScrollEnabled} = useCarouselContextEvents(setShouldShowArrows); | ||
|
||
useEffect(() => { | ||
if (!canUseTouchScreen) { | ||
|
@@ -201,12 +201,12 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi | |
activePage: 0, | ||
pagerRef, | ||
isPagerScrolling: nope, | ||
isScrollEnabled: nope, | ||
isScrollEnabled, | ||
onTap: handleTap, | ||
onScaleChanged: handleScaleChange, | ||
onSwipeDown: onClose, | ||
}), | ||
[source, nope, handleTap, handleScaleChange, onClose], | ||
[source, nope, isScrollEnabled, handleTap, handleScaleChange, onClose], | ||
); | ||
|
||
/** Defines how a single attachment should be rendered */ | ||
|
@@ -229,14 +229,14 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi | |
Gesture.Pan() | ||
.enabled(canUseTouchScreen) | ||
.onUpdate(({translationX}) => { | ||
if (scale.current !== 1) { | ||
if (!isScrollEnabled.value) { | ||
return; | ||
} | ||
|
||
scrollTo(scrollRef, page * cellWidth - translationX, 0, false); | ||
}) | ||
.onEnd(({translationX, velocityX}) => { | ||
if (scale.current !== 1) { | ||
if (!isScrollEnabled.value) { | ||
return; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. The scroll stuck because we swipe down while scrolling, making |
||
|
||
|
@@ -257,7 +257,7 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi | |
}) | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
.withRef(pagerRef as MutableRefObject<GestureType | undefined>), | ||
[attachments.length, canUseTouchScreen, cellWidth, page, scale, scrollRef], | ||
[attachments.length, canUseTouchScreen, cellWidth, page, isScrollEnabled, scrollRef], | ||
); | ||
|
||
return ( | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,12 +1,12 @@ | ||||||
import type {ForwardedRef} from 'react'; | ||||||
import React, {useEffect, useMemo, useRef} from 'react'; | ||||||
import React, {useCallback, useEffect, useMemo, useRef} from 'react'; | ||||||
import {View} from 'react-native'; | ||||||
import type {GestureType} from 'react-native-gesture-handler'; | ||||||
import {Gesture, GestureDetector} from 'react-native-gesture-handler'; | ||||||
import type {GestureRef} from 'react-native-gesture-handler/lib/typescript/handlers/gestures/gesture'; | ||||||
import type PagerView from 'react-native-pager-view'; | ||||||
import type {SharedValue} from 'react-native-reanimated'; | ||||||
import Animated, {cancelAnimation, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue, useWorkletCallback, withSpring} from 'react-native-reanimated'; | ||||||
import Animated, {cancelAnimation, runOnUI, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring} from 'react-native-reanimated'; | ||||||
import useStyleUtils from '@hooks/useStyleUtils'; | ||||||
import useThemeStyles from '@hooks/useThemeStyles'; | ||||||
import type ChildrenProps from '@src/types/utils/ChildrenProps'; | ||||||
|
@@ -40,9 +40,15 @@ type MultiGestureCanvasProps = ChildrenProps & { | |||||
/** A shared value of type boolean, that indicates disabled the transformation gestures (pinch, pan, double tap) */ | ||||||
shouldDisableTransformationGestures?: SharedValue<boolean>; | ||||||
|
||||||
/** A shared value to enable/disable the pager scroll */ | ||||||
isPagerScrollEnabled: SharedValue<boolean>; | ||||||
|
||||||
/** If there is a pager wrapping the canvas, we need to disable the pan gesture in case the pager is swiping */ | ||||||
pagerRef?: ForwardedRef<PagerView | GestureType>; // TODO: For TS migration: Exclude<GestureRef, number> | ||||||
|
||||||
/** Whether the component is being used inside a carousel */ | ||||||
isUsedInCarousel: boolean; | ||||||
|
||||||
/** Handles scale changed event */ | ||||||
onScaleChanged?: OnScaleChangedCallback; | ||||||
|
||||||
|
@@ -62,7 +68,9 @@ function MultiGestureCanvas({ | |||||
isActive = true, | ||||||
children, | ||||||
pagerRef, | ||||||
isUsedInCarousel, | ||||||
shouldDisableTransformationGestures: shouldDisableTransformationGesturesProp, | ||||||
isPagerScrollEnabled, | ||||||
onTap, | ||||||
onScaleChanged, | ||||||
onSwipeDown, | ||||||
|
@@ -107,47 +115,65 @@ function MultiGestureCanvas({ | |||||
const offsetX = useSharedValue(0); | ||||||
const offsetY = useSharedValue(0); | ||||||
|
||||||
useAnimatedReaction( | ||||||
() => isSwipingDownToClose.value, | ||||||
(current) => { | ||||||
if (!isUsedInCarousel) { | ||||||
return; | ||||||
} | ||||||
// eslint-disable-next-line react-compiler/react-compiler, no-param-reassign | ||||||
isPagerScrollEnabled.value = !current; | ||||||
}, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. In non-carousel, changing isScrollEnabled, changes the App/src/components/AttachmentModal.tsx Lines 474 to 475 in 64cb4ee
which sets |
||||||
); | ||||||
|
||||||
/** | ||||||
* Stops any currently running decay animation from panning | ||||||
*/ | ||||||
const stopAnimation = useWorkletCallback(() => { | ||||||
const stopAnimation = useCallback(() => { | ||||||
'worklet'; | ||||||
|
||||||
cancelAnimation(offsetX); | ||||||
cancelAnimation(offsetY); | ||||||
}); | ||||||
}, [offsetX, offsetY]); | ||||||
|
||||||
/** | ||||||
* Resets the canvas to the initial state and animates back smoothly | ||||||
*/ | ||||||
const reset = useWorkletCallback((animated: boolean, callback?: () => void) => { | ||||||
stopAnimation(); | ||||||
|
||||||
// eslint-disable-next-line react-compiler/react-compiler | ||||||
offsetX.value = 0; | ||||||
offsetY.value = 0; | ||||||
pinchScale.value = 1; | ||||||
|
||||||
if (animated) { | ||||||
panTranslateX.value = withSpring(0, SPRING_CONFIG); | ||||||
panTranslateY.value = withSpring(0, SPRING_CONFIG); | ||||||
pinchTranslateX.value = withSpring(0, SPRING_CONFIG); | ||||||
pinchTranslateY.value = withSpring(0, SPRING_CONFIG); | ||||||
zoomScale.value = withSpring(1, SPRING_CONFIG, callback); | ||||||
|
||||||
return; | ||||||
} | ||||||
|
||||||
panTranslateX.value = 0; | ||||||
panTranslateY.value = 0; | ||||||
pinchTranslateX.value = 0; | ||||||
pinchTranslateY.value = 0; | ||||||
zoomScale.value = 1; | ||||||
|
||||||
if (callback === undefined) { | ||||||
return; | ||||||
} | ||||||
|
||||||
callback(); | ||||||
}); | ||||||
const reset = useCallback( | ||||||
(animated: boolean, callback?: () => void) => { | ||||||
'worklet'; | ||||||
|
||||||
stopAnimation(); | ||||||
|
||||||
// eslint-disable-next-line react-compiler/react-compiler | ||||||
offsetX.value = 0; | ||||||
offsetY.value = 0; | ||||||
pinchScale.value = 1; | ||||||
|
||||||
if (animated) { | ||||||
panTranslateX.value = withSpring(0, SPRING_CONFIG); | ||||||
panTranslateY.value = withSpring(0, SPRING_CONFIG); | ||||||
pinchTranslateX.value = withSpring(0, SPRING_CONFIG); | ||||||
pinchTranslateY.value = withSpring(0, SPRING_CONFIG); | ||||||
zoomScale.value = withSpring(1, SPRING_CONFIG, callback); | ||||||
|
||||||
return; | ||||||
} | ||||||
|
||||||
panTranslateX.value = 0; | ||||||
panTranslateY.value = 0; | ||||||
pinchTranslateX.value = 0; | ||||||
pinchTranslateY.value = 0; | ||||||
zoomScale.value = 1; | ||||||
|
||||||
if (callback === undefined) { | ||||||
return; | ||||||
} | ||||||
|
||||||
callback(); | ||||||
}, | ||||||
[stopAnimation, offsetX, offsetY, pinchScale, panTranslateX, panTranslateY, pinchTranslateX, pinchTranslateY, zoomScale], | ||||||
); | ||||||
|
||||||
const {singleTapGesture: baseSingleTapGesture, doubleTapGesture} = useTapGestures({ | ||||||
canvasSize, | ||||||
|
@@ -164,6 +190,7 @@ function MultiGestureCanvas({ | |||||
onTap, | ||||||
shouldDisableTransformationGestures, | ||||||
}); | ||||||
// eslint-disable-next-line react-compiler/react-compiler | ||||||
const singleTapGesture = baseSingleTapGesture.requireExternalGestureToFail(doubleTapGesture, panGestureRef); | ||||||
|
||||||
const panGestureSimultaneousList = useMemo( | ||||||
|
@@ -186,6 +213,7 @@ function MultiGestureCanvas({ | |||||
onSwipeDown, | ||||||
}) | ||||||
.simultaneousWithExternalGesture(...panGestureSimultaneousList) | ||||||
// eslint-disable-next-line react-compiler/react-compiler | ||||||
.withRef(panGestureRef); | ||||||
|
||||||
const pinchGesture = usePinchGesture({ | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Same with the previous issue.