From 20f45f293f2a3188721140c2f46810775d8778b0 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 14 Dec 2023 23:21:57 +0100 Subject: [PATCH 1/3] fix: ignore duplicated events --- .../events/KeyboardTransitionEvent.kt | 24 ++++--- .../listeners/KeyboardAnimationCallback.kt | 72 +++++++++---------- .../Examples/InteractiveKeyboard/index.tsx | 22 +++++- 3 files changed, 65 insertions(+), 53 deletions(-) diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt b/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt index 192db3aaa..0504491ee 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt @@ -4,25 +4,29 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event +data class KeyboardTransitionEventData( + val event: String, + val height: Double, + val progress: Double, + val duration: Int, + val target: Int, +) + @Suppress("detekt:LongParameterList") class KeyboardTransitionEvent( surfaceId: Int, viewId: Int, - private val event: String, - private val height: Double, - private val progress: Double, - private val duration: Int, - private val target: Int, + private val data: KeyboardTransitionEventData, ) : Event(surfaceId, viewId) { - override fun getEventName() = event + override fun getEventName() = data.event // All events for a given view can be coalesced? override fun getCoalescingKey(): Short = 0 override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putDouble("progress", progress) - putDouble("height", height) - putInt("duration", duration) - putInt("target", target) + putDouble("progress", data.progress) + putDouble("height", data.height) + putInt("duration", data.duration) + putInt("target", data.target) } } diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt index 7451cb5e5..93d9bd8ee 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt @@ -20,6 +20,7 @@ import com.facebook.react.views.textinput.ReactEditText import com.facebook.react.views.view.ReactViewGroup import com.reactnativekeyboardcontroller.InteractiveKeyboardProvider import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent +import com.reactnativekeyboardcontroller.events.KeyboardTransitionEventData import com.reactnativekeyboardcontroller.extensions.dispatchEvent import com.reactnativekeyboardcontroller.extensions.dp import com.reactnativekeyboardcontroller.extensions.isKeyboardAnimation @@ -43,6 +44,7 @@ class KeyboardAnimationCallback( private var duration = 0 private var viewTagFocused = -1 private var animation: ValueAnimator? = null + private var lastEventDispatched: KeyboardTransitionEventData? = null // listeners private val focusListener = OnGlobalFocusChangeListener { oldFocus, newFocus -> @@ -56,11 +58,8 @@ class KeyboardAnimationCallback( // 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be // 100% included in onStart/onMove/onEnd lifecycles, but triggering onStart/onEnd several time // can bring breaking changes - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveStart", this.persistentKeyboardHeight, 1.0, @@ -68,11 +67,8 @@ class KeyboardAnimationCallback( viewTagFocused, ), ) - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveEnd", this.persistentKeyboardHeight, 1.0, @@ -164,11 +160,8 @@ class KeyboardAnimationCallback( ) Log.i(TAG, "HEIGHT:: $keyboardHeight TAG:: $viewTagFocused") - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveStart", keyboardHeight, if (!isKeyboardVisible) 0.0 else 1.0, @@ -215,11 +208,8 @@ class KeyboardAnimationCallback( ) val event = if (InteractiveKeyboardProvider.isInteractive) "topKeyboardMoveInteractive" else "topKeyboardMove" - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( event, height, progress, @@ -259,11 +249,8 @@ class KeyboardAnimationCallback( "KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", getEventParams(keyboardHeight), ) - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveEnd", keyboardHeight, if (!isKeyboardVisible) 0.0 else 1.0, @@ -300,11 +287,8 @@ class KeyboardAnimationCallback( } this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight)) - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveStart", keyboardHeight, 1.0, @@ -317,11 +301,8 @@ class KeyboardAnimationCallback( ValueAnimator.ofFloat(this.persistentKeyboardHeight.toFloat(), keyboardHeight.toFloat()) animation.addUpdateListener { animator -> val toValue = animator.animatedValue as Float - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMove", toValue.toDouble(), toValue.toDouble() / keyboardHeight, @@ -332,11 +313,8 @@ class KeyboardAnimationCallback( } animation.doOnEnd { this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight)) - context.dispatchEvent( - view.id, - KeyboardTransitionEvent( - surfaceId, - view.id, + this.dispatchEventToJS( + KeyboardTransitionEventData( "topKeyboardMoveEnd", keyboardHeight, 1.0, @@ -384,6 +362,20 @@ class KeyboardAnimationCallback( return params } + private fun dispatchEventToJS(event: KeyboardTransitionEventData) { + if (event != lastEventDispatched) { + lastEventDispatched = event + context.dispatchEvent( + view.id, + KeyboardTransitionEvent( + surfaceId, + view.id, + data = event, + ), + ) + } + } + companion object { private const val DEFAULT_ANIMATION_TIME = 250 } diff --git a/example/src/screens/Examples/InteractiveKeyboard/index.tsx b/example/src/screens/Examples/InteractiveKeyboard/index.tsx index 3332cf43a..49fa47193 100644 --- a/example/src/screens/Examples/InteractiveKeyboard/index.tsx +++ b/example/src/screens/Examples/InteractiveKeyboard/index.tsx @@ -6,6 +6,9 @@ import { useKeyboardHandler, } from 'react-native-keyboard-controller'; import Reanimated, { + scrollTo, + useAnimatedRef, + useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, } from 'react-native-reanimated'; @@ -17,7 +20,7 @@ import styles from './styles'; const AnimatedTextInput = Reanimated.createAnimatedComponent(TextInput); -const useKeyboardAnimation = () => { +const useKeyboardAnimation = ({ref, scroll}) => { const progress = useSharedValue(0); const height = useSharedValue(0); useKeyboardHandler({ @@ -30,6 +33,8 @@ const useKeyboardAnimation = () => { onInteractive: (e) => { 'worklet'; + scrollTo(ref, 0, scroll.value, false); + progress.value = e.progress; height.value = e.height; }, @@ -41,8 +46,16 @@ const useKeyboardAnimation = () => { type Props = StackScreenProps; function InteractiveKeyboard({ navigation }: Props) { + const aRef = useAnimatedRef(); + const scroll = useSharedValue(0); const [interpolator, setInterpolator] = useState<'ios' | 'linear'>('linear'); - const { height } = useKeyboardAnimation(); + const { height } = useKeyboardAnimation({ref: aRef, scroll: scroll}); + + const onScroll = useAnimatedScrollHandler({ + onScroll: (e) => { + scroll.value = e.contentOffset.y; + }, + }) useEffect(() => { navigation.setOptions({ @@ -76,7 +89,8 @@ function InteractiveKeyboard({ navigation }: Props) { ); const fakeView = useAnimatedStyle( () => ({ - height: height.value, + // TODO: don't update when onInteractive is fired + // height: height.value, }), [] ); @@ -89,6 +103,8 @@ function InteractiveKeyboard({ navigation }: Props) { showOnSwipeUp > From d1581c17f9c0598606b55b527e621f2d4a2ca0f1 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Sun, 17 Dec 2023 12:17:20 +0100 Subject: [PATCH 2/3] fix: CI --- .../extensions/ThemedReactContext.kt | 10 ++++++++++ .../listeners/KeyboardAnimationCallback.kt | 19 +++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt index 8c0b18882..3f4eb4bc8 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt @@ -1,6 +1,10 @@ package com.reactnativekeyboardcontroller.extensions +import android.util.Log import android.view.View + +import com.facebook.react.bridge.WritableMap +import com.facebook.react.modules.core.DeviceEventManagerModule import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.UIManagerHelper import com.facebook.react.uimanager.events.Event @@ -14,3 +18,9 @@ fun ThemedReactContext?.dispatchEvent(viewId: Int, event: Event<*>) { UIManagerHelper.getEventDispatcherForReactTag(this, viewId) eventDispatcher?.dispatchEvent(event) } + +fun ThemedReactContext?.emitEvent(event: String, params: WritableMap) { + this?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit(event, params) + + Log.i("ThemedReactContext", event) +} diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt index 93d9bd8ee..2bf1656c4 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt @@ -23,6 +23,7 @@ import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent import com.reactnativekeyboardcontroller.events.KeyboardTransitionEventData import com.reactnativekeyboardcontroller.extensions.dispatchEvent import com.reactnativekeyboardcontroller.extensions.dp +import com.reactnativekeyboardcontroller.extensions.emitEvent import com.reactnativekeyboardcontroller.extensions.isKeyboardAnimation import kotlin.math.abs @@ -76,8 +77,8 @@ class KeyboardAnimationCallback( viewTagFocused, ), ) - this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight)) - this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight)) + context.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight)) + context.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight)) } } } @@ -154,7 +155,7 @@ class KeyboardAnimationCallback( } layoutObserver?.syncUpLayout() - this.emitEvent( + context.emitEvent( "KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow", getEventParams(keyboardHeight), ) @@ -245,7 +246,7 @@ class KeyboardAnimationCallback( } isKeyboardVisible = isKeyboardVisible || isKeyboardShown - this.emitEvent( + context.emitEvent( "KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", getEventParams(keyboardHeight), ) @@ -286,7 +287,7 @@ class KeyboardAnimationCallback( this.animation?.cancel() } - this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight)) + context.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight)) this.dispatchEventToJS( KeyboardTransitionEventData( "topKeyboardMoveStart", @@ -312,7 +313,7 @@ class KeyboardAnimationCallback( ) } animation.doOnEnd { - this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight)) + context.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight)) this.dispatchEventToJS( KeyboardTransitionEventData( "topKeyboardMoveEnd", @@ -346,12 +347,6 @@ class KeyboardAnimationCallback( return (keyboardHeight - navigationBar).toFloat().dp.coerceAtLeast(0.0) } - private fun emitEvent(event: String, params: WritableMap) { - context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit(event, params) - - Log.i(TAG, event) - } - private fun getEventParams(height: Double): WritableMap { val params: WritableMap = Arguments.createMap() params.putDouble("height", height) From 96b041af8be9ed80b1ec16f859e7f884b517609d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Sun, 17 Dec 2023 12:21:02 +0100 Subject: [PATCH 3/3] fix: CI --- .../extensions/ThemedReactContext.kt | 1 - .../listeners/KeyboardAnimationCallback.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt index 3f4eb4bc8..a4622f037 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt @@ -2,7 +2,6 @@ package com.reactnativekeyboardcontroller.extensions import android.util.Log import android.view.View - import com.facebook.react.bridge.WritableMap import com.facebook.react.modules.core.DeviceEventManagerModule import com.facebook.react.uimanager.ThemedReactContext diff --git a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt index 2bf1656c4..80fc72702 100644 --- a/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +++ b/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt @@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap -import com.facebook.react.modules.core.DeviceEventManagerModule import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.UIManagerHelper import com.facebook.react.views.textinput.ReactEditText