fix(app): avoid draggable-list gesture livelock
Manual activation now runs once while the recognizer is waiting; reactivating or failing an already-active recognizer could leave the gesture pipeline stuck.
This commit is contained in:
@@ -58,7 +58,7 @@ index 2abb4a2..8ca967f 100644
|
|||||||
onAnimValInit?: (animVals: ReturnType<typeof useAnimatedValues>) => void;
|
onAnimValInit?: (animVals: ReturnType<typeof useAnimatedValues>) => void;
|
||||||
itemEnteringAnimation?: AnimateProps<Animated.View>["entering"];
|
itemEnteringAnimation?: AnimateProps<Animated.View>["entering"];
|
||||||
diff --git a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
diff --git a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
||||||
index 7c88afc..b2f103e 100644
|
index 7c88afc..6d5719c 100644
|
||||||
--- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
--- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
||||||
+++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
+++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
||||||
@@ -19,6 +19,7 @@ import {
|
@@ -19,6 +19,7 @@ import {
|
||||||
@@ -237,17 +237,19 @@ index 7c88afc..b2f103e 100644
|
|||||||
useAnimatedReaction(
|
useAnimatedReaction(
|
||||||
() => {
|
() => {
|
||||||
return spacerIndexAnim.value;
|
return spacerIndexAnim.value;
|
||||||
@@ -271,11 +349,19 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -271,11 +349,21 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const gestureDisabled = useSharedValue(false);
|
const gestureDisabled = useSharedValue(false);
|
||||||
+ const touchStartX = useSharedValue(0);
|
+ const touchStartX = useSharedValue(0);
|
||||||
+ const touchStartY = useSharedValue(0);
|
+ const touchStartY = useSharedValue(0);
|
||||||
|
+ const manualPanActivated = useSharedValue(false);
|
||||||
+ const coordinatesDragWithExternalGesture = Boolean(props.simultaneousHandlers);
|
+ const coordinatesDragWithExternalGesture = Boolean(props.simultaneousHandlers);
|
||||||
|
|
||||||
- const panGesture = Gesture.Pan()
|
- const panGesture = Gesture.Pan()
|
||||||
+ let panGesture = Gesture.Pan()
|
+ let panGesture = Gesture.Pan()
|
||||||
.onBegin((evt) => {
|
.onBegin((evt) => {
|
||||||
|
+ manualPanActivated.value = false;
|
||||||
gestureDisabled.value = disabled.value;
|
gestureDisabled.value = disabled.value;
|
||||||
if (gestureDisabled.value) return;
|
if (gestureDisabled.value) return;
|
||||||
+ const sessionId = gestureSessionId.value + 1;
|
+ const sessionId = gestureSessionId.value + 1;
|
||||||
@@ -258,7 +260,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
panGestureState.value = evt.state;
|
panGestureState.value = evt.state;
|
||||||
})
|
})
|
||||||
.onUpdate((evt) => {
|
.onUpdate((evt) => {
|
||||||
@@ -286,8 +372,15 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -286,8 +374,15 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
: evt.translationY;
|
: evt.translationY;
|
||||||
touchTranslate.value = translation;
|
touchTranslate.value = translation;
|
||||||
})
|
})
|
||||||
@@ -275,7 +277,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
// Set touch val to current translate val
|
// Set touch val to current translate val
|
||||||
isTouchActiveNative.value = false;
|
isTouchActiveNative.value = false;
|
||||||
const translation = horizontalAnim.value
|
const translation = horizontalAnim.value
|
||||||
@@ -299,6 +392,7 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -299,6 +394,7 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
|
|
||||||
// Only call onDragEnd if actually dragging a cell
|
// Only call onDragEnd if actually dragging a cell
|
||||||
if (activeIndexAnim.value === -1 || disabled.value) return;
|
if (activeIndexAnim.value === -1 || disabled.value) return;
|
||||||
@@ -283,7 +285,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
disabled.value = true;
|
disabled.value = true;
|
||||||
runOnJS(onRelease)(activeIndexAnim.value);
|
runOnJS(onRelease)(activeIndexAnim.value);
|
||||||
const springTo = placeholderOffset.value - activeCellOffset.value;
|
const springTo = placeholderOffset.value - activeCellOffset.value;
|
||||||
@@ -309,14 +403,58 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -309,14 +405,64 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
runOnJS(onDragEnd)({
|
runOnJS(onDragEnd)({
|
||||||
from: activeIndexAnim.value,
|
from: activeIndexAnim.value,
|
||||||
to: spacerIndexAnim.value,
|
to: spacerIndexAnim.value,
|
||||||
@@ -295,6 +297,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
})
|
})
|
||||||
- .onTouchesDown(() => {
|
- .onTouchesDown(() => {
|
||||||
+ .onFinalize((evt) => {
|
+ .onFinalize((evt) => {
|
||||||
|
+ manualPanActivated.value = false;
|
||||||
+ panGestureState.value = evt.state;
|
+ panGestureState.value = evt.state;
|
||||||
+ runOnJS(onContainerTouchEnd)();
|
+ runOnJS(onContainerTouchEnd)();
|
||||||
+ if (gestureDisabled.value) return;
|
+ if (gestureDisabled.value) return;
|
||||||
@@ -316,6 +319,10 @@ index 7c88afc..b2f103e 100644
|
|||||||
+ .onTouchesMove((event, stateManager) => {
|
+ .onTouchesMove((event, stateManager) => {
|
||||||
+ if (!coordinatesDragWithExternalGesture) return;
|
+ if (!coordinatesDragWithExternalGesture) return;
|
||||||
+
|
+
|
||||||
|
+ // Manual activation arbitrates only while the pan is waiting. Failing or
|
||||||
|
+ // reactivating an active recognizer can leave RNGH/Reanimated livelocked.
|
||||||
|
+ if (manualPanActivated.value) return;
|
||||||
|
+
|
||||||
+ const touch = event.changedTouches[0];
|
+ const touch = event.changedTouches[0];
|
||||||
+ if (!touch || event.numberOfTouches !== 1) {
|
+ if (!touch || event.numberOfTouches !== 1) {
|
||||||
+ stateManager.fail();
|
+ stateManager.fail();
|
||||||
@@ -337,13 +344,14 @@ index 7c88afc..b2f103e 100644
|
|||||||
+ if (absDeltaX > 10 && absDeltaX > absDeltaY) {
|
+ if (absDeltaX > 10 && absDeltaX > absDeltaY) {
|
||||||
+ stateManager.fail();
|
+ stateManager.fail();
|
||||||
+ } else if (absDeltaY >= activationDistanceProp && absDeltaY > absDeltaX) {
|
+ } else if (absDeltaY >= activationDistanceProp && absDeltaY > absDeltaX) {
|
||||||
|
+ manualPanActivated.value = true;
|
||||||
+ stateManager.activate();
|
+ stateManager.activate();
|
||||||
+ }
|
+ }
|
||||||
+ })
|
+ })
|
||||||
.onTouchesUp(() => {
|
.onTouchesUp(() => {
|
||||||
// Turning this into a worklet causes timing issues. We want it to run
|
// Turning this into a worklet causes timing issues. We want it to run
|
||||||
// just after the finger lifts.
|
// just after the finger lifts.
|
||||||
@@ -324,7 +462,9 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -324,7 +470,9 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dragHitSlop) panGesture.hitSlop(dragHitSlop);
|
if (dragHitSlop) panGesture.hitSlop(dragHitSlop);
|
||||||
@@ -354,7 +362,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
const activeOffset = [-activationDistanceProp, activationDistanceProp];
|
const activeOffset = [-activationDistanceProp, activationDistanceProp];
|
||||||
if (props.horizontal) {
|
if (props.horizontal) {
|
||||||
panGesture.activeOffsetX(activeOffset);
|
panGesture.activeOffsetX(activeOffset);
|
||||||
@@ -332,6 +472,21 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -332,6 +480,21 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
panGesture.activeOffsetY(activeOffset);
|
panGesture.activeOffsetY(activeOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +384,7 @@ index 7c88afc..b2f103e 100644
|
|||||||
|
|
||||||
const onScroll = useStableCallback((scrollOffset: number) => {
|
const onScroll = useStableCallback((scrollOffset: number) => {
|
||||||
props.onScrollOffsetChange?.(scrollOffset);
|
props.onScrollOffsetChange?.(scrollOffset);
|
||||||
@@ -373,8 +528,9 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
@@ -373,8 +536,9 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
||||||
horizontal={!!props.horizontal}
|
horizontal={!!props.horizontal}
|
||||||
layoutAnimationDisabled={layoutAnimationDisabled}
|
layoutAnimationDisabled={layoutAnimationDisabled}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user