Skip to content

Commit 3cd8152

Browse files
M-i-k-e-lethanshar
andcommitted
DO NOT MERGE - Use the new activateAfterLongPress to fix an Android bug (#2110)
* Use the new activateAfterLongPress to fix an Android bug * Set peer react-native-gesture-handler to 2.5.0 Co-authored-by: Ethan Sharabi <[email protected]>
1 parent 5a5eb67 commit 3cd8152

File tree

4 files changed

+64
-168
lines changed

4 files changed

+64
-168
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
"peerDependencies": {
110110
"react": ">=17.0.1",
111111
"react-native": ">=0.64.1",
112-
"react-native-gesture-handler": ">=1.9.0",
112+
"react-native-gesture-handler": ">=2.5.0",
113113
"react-native-reanimated": ">=2.0.0"
114114
},
115115
"jest": {

src/components/sortableGridList/SortableItem.tsx

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ function SortableItem(props: PropsWithChildren<SortableItemProps & ReturnType<ty
3232
const translateX = useSharedValue(0);
3333
const translateY = useSharedValue(0);
3434

35-
const isFloating = useSharedValue(false);
3635
const isDragging = useSharedValue(false);
3736
const tempItemsOrder = useSharedValue(itemsOrder.value);
3837
const tempTranslateX = useSharedValue(0);
@@ -62,13 +61,13 @@ function SortableItem(props: PropsWithChildren<SortableItemProps & ReturnType<ty
6261
if (dataManuallyChanged.value) {
6362
translateX.value = 0;
6463
translateY.value = 0;
65-
/* Reset with an animation when the change id due to user reordering */
64+
/* Reset with an animation when the change id due to user reordering */
6665
} else {
6766
translateX.value = withTiming(0, animationConfig);
6867
translateY.value = withTiming(0, animationConfig);
6968
}
7069
dataManuallyChanged.value = false;
71-
/* Handle an order change, animate item to its new position */
70+
/* Handle an order change, animate item to its new position */
7271
} else if (newOrder !== prevOrder) {
7372
const translation = getTranslationByOrderChange(newOrder, prevOrder);
7473
translateX.value = withTiming(translateX.value + translation.x, animationConfig);
@@ -83,29 +82,10 @@ function SortableItem(props: PropsWithChildren<SortableItemProps & ReturnType<ty
8382
updateItemLayout({width, height});
8483
}, []);
8584

86-
const longPressGesture = Gesture.LongPress()
87-
.onStart(() => {
88-
isFloating.value = true;
89-
})
90-
.onTouchesCancelled(() => {
91-
if (!isDragging.value) {
92-
isFloating.value = false;
93-
}
94-
})
95-
.minDuration(250);
96-
97-
const dragGesture = Gesture.Pan()
98-
.manualActivation(true)
99-
.onTouchesMove((_e, state) => {
100-
if (isFloating.value) {
101-
isDragging.value = true;
102-
state.activate();
103-
} else {
104-
isDragging.value = false;
105-
state.fail();
106-
}
107-
})
85+
const dragOnLongPressGesture = Gesture.Pan()
86+
.activateAfterLongPress(250)
10887
.onStart(() => {
88+
isDragging.value = true;
10989
tempTranslateX.value = translateX.value;
11090
tempTranslateY.value = translateY.value;
11191
tempItemsOrder.value = itemsOrder.value;
@@ -139,19 +119,15 @@ function SortableItem(props: PropsWithChildren<SortableItemProps & ReturnType<ty
139119
.onFinalize(() => {
140120
if (isDragging.value) {
141121
isDragging.value = false;
142-
isFloating.value = false;
143122
if (tempItemsOrder.value.toString() !== itemsOrder.value.toString()) {
144123
runOnJS(onChange)();
145124
}
146125
}
147-
})
148-
.simultaneousWithExternalGesture(longPressGesture);
149-
150-
const gesture = Gesture.Race(dragGesture, longPressGesture);
126+
});
151127

152128
const animatedStyle = useAnimatedStyle(() => {
153-
const scale = withSpring(isFloating.value ? 1.1 : 1);
154-
const zIndex = isFloating.value ? 100 : withTiming(0, animationConfig);
129+
const scale = withSpring(isDragging.value ? 1.1 : 1);
130+
const zIndex = isDragging.value ? 100 : withTiming(0, animationConfig);
155131

156132
return {
157133
zIndex,
@@ -161,7 +137,7 @@ function SortableItem(props: PropsWithChildren<SortableItemProps & ReturnType<ty
161137

162138
return (
163139
<View reanimated style={[style, animatedStyle]} onLayout={onLayout}>
164-
<GestureDetector gesture={gesture}>
140+
<GestureDetector gesture={dragOnLongPressGesture}>
165141
<View>{props.children}</View>
166142
</GestureDetector>
167143
</View>

src/components/sortableList/SortableListItem.tsx

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable react-hooks/exhaustive-deps */
22
import {map} from 'lodash';
3-
import React, {PropsWithChildren, useCallback, useContext} from 'react';
3+
import React, {PropsWithChildren, useContext} from 'react';
44
import {
55
useSharedValue,
66
useAnimatedReaction,
@@ -10,14 +10,13 @@ import {
1010
useAnimatedStyle,
1111
withSpring
1212
} from 'react-native-reanimated';
13-
import {GestureDetector, GestureUpdateEvent, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
13+
import {GestureDetector, Gesture} from 'react-native-gesture-handler';
1414
import View from '../view';
1515
import {Shadows, Colors} from '../../style';
1616
import {useDidUpdate} from 'hooks';
1717
import SortableListContext from './SortableListContext';
1818
import usePresenter from './usePresenter';
19-
import useDragAfterLongPressGesture from './useDragAfterLongPressGesture';
20-
19+
import {HapticService, HapticType} from '../../services';
2120
export interface SortableListItemProps {
2221
index: number;
2322
}
@@ -32,12 +31,13 @@ const animationConfig = {
3231
const SortableListItem = (props: Props) => {
3332
const {children, index} = props;
3433

35-
const {data, itemHeight, onItemLayout, itemsOrder, onChange, enableHaptic} =
36-
useContext(SortableListContext);
34+
const {data, itemHeight, onItemLayout, itemsOrder, onChange, enableHaptic} = useContext(SortableListContext);
3735
const {getTranslationByIndexChange, getItemIndexById, getIndexByPosition, getIdByItemIndex} = usePresenter();
3836
const id: string = data[index].id;
3937
const initialIndex = useSharedValue<number>(map(data, 'id').indexOf(id));
4038
const translateY = useSharedValue<number>(0);
39+
40+
const isDragging = useSharedValue(false);
4141
const tempTranslateY = useSharedValue<number>(0);
4242
const tempItemsOrder = useSharedValue<string[]>(itemsOrder.value);
4343
const dataManuallyChanged = useSharedValue<boolean>(false);
@@ -76,57 +76,58 @@ const SortableListItem = (props: Props) => {
7676
}
7777
});
7878

79-
const onDragStart = useCallback(() => {
80-
'worklet';
81-
tempTranslateY.value = translateY.value;
82-
tempItemsOrder.value = itemsOrder.value;
83-
}, []);
84-
85-
const onDragUpdate = useCallback((event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
86-
'worklet';
87-
translateY.value = tempTranslateY.value + event.translationY;
88-
89-
// Swapping items
90-
const newIndex = getIndexByPosition(translateY.value, itemHeight.value) + initialIndex.value;
91-
const oldIndex = getItemIndexById(itemsOrder.value, id);
92-
93-
if (newIndex !== oldIndex) {
94-
const itemIdToSwap = getIdByItemIndex(itemsOrder.value, newIndex);
95-
96-
if (itemIdToSwap !== undefined) {
97-
const newItemsOrder = [...itemsOrder.value];
98-
newItemsOrder[newIndex] = id;
99-
newItemsOrder[oldIndex] = itemIdToSwap;
100-
itemsOrder.value = newItemsOrder;
79+
const dragOnLongPressGesture = Gesture.Pan()
80+
.activateAfterLongPress(250)
81+
.onStart(() => {
82+
isDragging.value = true;
83+
tempTranslateY.value = translateY.value;
84+
tempItemsOrder.value = itemsOrder.value;
85+
})
86+
.onTouchesMove(() => {
87+
if (enableHaptic && !isDragging.value) {
88+
runOnJS(HapticService.triggerHaptic)(HapticType.selection, 'SortableList');
10189
}
102-
}
103-
}, []);
104-
105-
const onDragEnd = useCallback(() => {
106-
'worklet';
107-
const translation = getTranslationByIndexChange(getItemIndexById(itemsOrder.value, id),
108-
getItemIndexById(tempItemsOrder.value, id),
109-
itemHeight.value);
110-
111-
translateY.value = withTiming(tempTranslateY.value + translation, animationConfig, () => {
112-
if (tempItemsOrder.value.toString() !== itemsOrder.value.toString()) {
113-
runOnJS(onChange)();
90+
})
91+
.onUpdate(event => {
92+
translateY.value = tempTranslateY.value + event.translationY;
93+
94+
// Swapping items
95+
const newIndex = getIndexByPosition(translateY.value, itemHeight.value) + initialIndex.value;
96+
const oldIndex = getItemIndexById(itemsOrder.value, id);
97+
98+
if (newIndex !== oldIndex) {
99+
const itemIdToSwap = getIdByItemIndex(itemsOrder.value, newIndex);
100+
101+
if (itemIdToSwap !== undefined) {
102+
const newItemsOrder = [...itemsOrder.value];
103+
newItemsOrder[newIndex] = id;
104+
newItemsOrder[oldIndex] = itemIdToSwap;
105+
itemsOrder.value = newItemsOrder;
106+
}
107+
}
108+
})
109+
.onEnd(() => {
110+
const translation = getTranslationByIndexChange(getItemIndexById(itemsOrder.value, id),
111+
getItemIndexById(tempItemsOrder.value, id),
112+
itemHeight.value);
113+
114+
translateY.value = withTiming(tempTranslateY.value + translation, animationConfig, () => {
115+
if (tempItemsOrder.value.toString() !== itemsOrder.value.toString()) {
116+
runOnJS(onChange)();
117+
}
118+
});
119+
})
120+
.onFinalize(() => {
121+
if (isDragging.value) {
122+
isDragging.value = false;
114123
}
115124
});
116-
}, []);
117-
118-
const {dragAfterLongPressGesture, isFloating} = useDragAfterLongPressGesture({
119-
onDragStart,
120-
onDragUpdate,
121-
onDragEnd,
122-
hapticComponentName: enableHaptic ? 'SortableList' : null
123-
});
124125

125126
const draggedAnimatedStyle = useAnimatedStyle(() => {
126-
const scaleY = withSpring(isFloating.value ? 1.1 : 1);
127-
const zIndex = isFloating.value ? 100 : withTiming(0, animationConfig);
128-
const opacity = isFloating.value ? 0.95 : 1;
129-
const shadow = isFloating.value
127+
const scaleY = withSpring(isDragging.value ? 1.1 : 1);
128+
const zIndex = isDragging.value ? 100 : withTiming(0, animationConfig);
129+
const opacity = isDragging.value ? 0.95 : 1;
130+
const shadow = isDragging.value
130131
? {
131132
...Shadows.sh30.bottom,
132133
...Shadows.sh30.top
@@ -146,7 +147,7 @@ const SortableListItem = (props: Props) => {
146147
});
147148

148149
return (
149-
<GestureDetector gesture={dragAfterLongPressGesture}>
150+
<GestureDetector gesture={dragOnLongPressGesture}>
150151
<View reanimated style={draggedAnimatedStyle} onLayout={index === 0 ? onItemLayout : undefined}>
151152
{children}
152153
</View>

src/components/sortableList/useDragAfterLongPressGesture.ts

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)