Skip to content

Commit 20f45f2

Browse files
committed
fix: ignore duplicated events
1 parent a425e7c commit 20f45f2

File tree

3 files changed

+65
-53
lines changed

3 files changed

+65
-53
lines changed

android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@ import com.facebook.react.bridge.Arguments
44
import com.facebook.react.bridge.WritableMap
55
import com.facebook.react.uimanager.events.Event
66

7+
data class KeyboardTransitionEventData(
8+
val event: String,
9+
val height: Double,
10+
val progress: Double,
11+
val duration: Int,
12+
val target: Int,
13+
)
14+
715
@Suppress("detekt:LongParameterList")
816
class KeyboardTransitionEvent(
917
surfaceId: Int,
1018
viewId: Int,
11-
private val event: String,
12-
private val height: Double,
13-
private val progress: Double,
14-
private val duration: Int,
15-
private val target: Int,
19+
private val data: KeyboardTransitionEventData,
1620
) : Event<KeyboardTransitionEvent>(surfaceId, viewId) {
17-
override fun getEventName() = event
21+
override fun getEventName() = data.event
1822

1923
// All events for a given view can be coalesced?
2024
override fun getCoalescingKey(): Short = 0
2125

2226
override fun getEventData(): WritableMap? = Arguments.createMap().apply {
23-
putDouble("progress", progress)
24-
putDouble("height", height)
25-
putInt("duration", duration)
26-
putInt("target", target)
27+
putDouble("progress", data.progress)
28+
putDouble("height", data.height)
29+
putInt("duration", data.duration)
30+
putInt("target", data.target)
2731
}
2832
}

android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.facebook.react.views.textinput.ReactEditText
2020
import com.facebook.react.views.view.ReactViewGroup
2121
import com.reactnativekeyboardcontroller.InteractiveKeyboardProvider
2222
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent
23+
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEventData
2324
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
2425
import com.reactnativekeyboardcontroller.extensions.dp
2526
import com.reactnativekeyboardcontroller.extensions.isKeyboardAnimation
@@ -43,6 +44,7 @@ class KeyboardAnimationCallback(
4344
private var duration = 0
4445
private var viewTagFocused = -1
4546
private var animation: ValueAnimator? = null
47+
private var lastEventDispatched: KeyboardTransitionEventData? = null
4648

4749
// listeners
4850
private val focusListener = OnGlobalFocusChangeListener { oldFocus, newFocus ->
@@ -56,23 +58,17 @@ class KeyboardAnimationCallback(
5658
// 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be
5759
// 100% included in onStart/onMove/onEnd lifecycles, but triggering onStart/onEnd several time
5860
// can bring breaking changes
59-
context.dispatchEvent(
60-
view.id,
61-
KeyboardTransitionEvent(
62-
surfaceId,
63-
view.id,
61+
this.dispatchEventToJS(
62+
KeyboardTransitionEventData(
6463
"topKeyboardMoveStart",
6564
this.persistentKeyboardHeight,
6665
1.0,
6766
0,
6867
viewTagFocused,
6968
),
7069
)
71-
context.dispatchEvent(
72-
view.id,
73-
KeyboardTransitionEvent(
74-
surfaceId,
75-
view.id,
70+
this.dispatchEventToJS(
71+
KeyboardTransitionEventData(
7672
"topKeyboardMoveEnd",
7773
this.persistentKeyboardHeight,
7874
1.0,
@@ -164,11 +160,8 @@ class KeyboardAnimationCallback(
164160
)
165161

166162
Log.i(TAG, "HEIGHT:: $keyboardHeight TAG:: $viewTagFocused")
167-
context.dispatchEvent(
168-
view.id,
169-
KeyboardTransitionEvent(
170-
surfaceId,
171-
view.id,
163+
this.dispatchEventToJS(
164+
KeyboardTransitionEventData(
172165
"topKeyboardMoveStart",
173166
keyboardHeight,
174167
if (!isKeyboardVisible) 0.0 else 1.0,
@@ -215,11 +208,8 @@ class KeyboardAnimationCallback(
215208
)
216209

217210
val event = if (InteractiveKeyboardProvider.isInteractive) "topKeyboardMoveInteractive" else "topKeyboardMove"
218-
context.dispatchEvent(
219-
view.id,
220-
KeyboardTransitionEvent(
221-
surfaceId,
222-
view.id,
211+
this.dispatchEventToJS(
212+
KeyboardTransitionEventData(
223213
event,
224214
height,
225215
progress,
@@ -259,11 +249,8 @@ class KeyboardAnimationCallback(
259249
"KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow",
260250
getEventParams(keyboardHeight),
261251
)
262-
context.dispatchEvent(
263-
view.id,
264-
KeyboardTransitionEvent(
265-
surfaceId,
266-
view.id,
252+
this.dispatchEventToJS(
253+
KeyboardTransitionEventData(
267254
"topKeyboardMoveEnd",
268255
keyboardHeight,
269256
if (!isKeyboardVisible) 0.0 else 1.0,
@@ -300,11 +287,8 @@ class KeyboardAnimationCallback(
300287
}
301288

302289
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
303-
context.dispatchEvent(
304-
view.id,
305-
KeyboardTransitionEvent(
306-
surfaceId,
307-
view.id,
290+
this.dispatchEventToJS(
291+
KeyboardTransitionEventData(
308292
"topKeyboardMoveStart",
309293
keyboardHeight,
310294
1.0,
@@ -317,11 +301,8 @@ class KeyboardAnimationCallback(
317301
ValueAnimator.ofFloat(this.persistentKeyboardHeight.toFloat(), keyboardHeight.toFloat())
318302
animation.addUpdateListener { animator ->
319303
val toValue = animator.animatedValue as Float
320-
context.dispatchEvent(
321-
view.id,
322-
KeyboardTransitionEvent(
323-
surfaceId,
324-
view.id,
304+
this.dispatchEventToJS(
305+
KeyboardTransitionEventData(
325306
"topKeyboardMove",
326307
toValue.toDouble(),
327308
toValue.toDouble() / keyboardHeight,
@@ -332,11 +313,8 @@ class KeyboardAnimationCallback(
332313
}
333314
animation.doOnEnd {
334315
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
335-
context.dispatchEvent(
336-
view.id,
337-
KeyboardTransitionEvent(
338-
surfaceId,
339-
view.id,
316+
this.dispatchEventToJS(
317+
KeyboardTransitionEventData(
340318
"topKeyboardMoveEnd",
341319
keyboardHeight,
342320
1.0,
@@ -384,6 +362,20 @@ class KeyboardAnimationCallback(
384362
return params
385363
}
386364

365+
private fun dispatchEventToJS(event: KeyboardTransitionEventData) {
366+
if (event != lastEventDispatched) {
367+
lastEventDispatched = event
368+
context.dispatchEvent(
369+
view.id,
370+
KeyboardTransitionEvent(
371+
surfaceId,
372+
view.id,
373+
data = event,
374+
),
375+
)
376+
}
377+
}
378+
387379
companion object {
388380
private const val DEFAULT_ANIMATION_TIME = 250
389381
}

example/src/screens/Examples/InteractiveKeyboard/index.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import {
66
useKeyboardHandler,
77
} from 'react-native-keyboard-controller';
88
import Reanimated, {
9+
scrollTo,
10+
useAnimatedRef,
11+
useAnimatedScrollHandler,
912
useAnimatedStyle,
1013
useSharedValue,
1114
} from 'react-native-reanimated';
@@ -17,7 +20,7 @@ import styles from './styles';
1720

1821
const AnimatedTextInput = Reanimated.createAnimatedComponent(TextInput);
1922

20-
const useKeyboardAnimation = () => {
23+
const useKeyboardAnimation = ({ref, scroll}) => {
2124
const progress = useSharedValue(0);
2225
const height = useSharedValue(0);
2326
useKeyboardHandler({
@@ -30,6 +33,8 @@ const useKeyboardAnimation = () => {
3033
onInteractive: (e) => {
3134
'worklet';
3235

36+
scrollTo(ref, 0, scroll.value, false);
37+
3338
progress.value = e.progress;
3439
height.value = e.height;
3540
},
@@ -41,8 +46,16 @@ const useKeyboardAnimation = () => {
4146
type Props = StackScreenProps<ExamplesStackParamList>;
4247

4348
function InteractiveKeyboard({ navigation }: Props) {
49+
const aRef = useAnimatedRef();
50+
const scroll = useSharedValue(0);
4451
const [interpolator, setInterpolator] = useState<'ios' | 'linear'>('linear');
45-
const { height } = useKeyboardAnimation();
52+
const { height } = useKeyboardAnimation({ref: aRef, scroll: scroll});
53+
54+
const onScroll = useAnimatedScrollHandler({
55+
onScroll: (e) => {
56+
scroll.value = e.contentOffset.y;
57+
},
58+
})
4659

4760
useEffect(() => {
4861
navigation.setOptions({
@@ -76,7 +89,8 @@ function InteractiveKeyboard({ navigation }: Props) {
7689
);
7790
const fakeView = useAnimatedStyle(
7891
() => ({
79-
height: height.value,
92+
// TODO: don't update when onInteractive is fired
93+
// height: height.value,
8094
}),
8195
[]
8296
);
@@ -89,6 +103,8 @@ function InteractiveKeyboard({ navigation }: Props) {
89103
showOnSwipeUp
90104
>
91105
<Reanimated.ScrollView
106+
ref={aRef}
107+
onScroll={onScroll}
92108
showsVerticalScrollIndicator={false}
93109
style={scrollViewStyle}
94110
>

0 commit comments

Comments
 (0)