@@ -201,6 +201,12 @@ class NativeViewGestureHandler : GestureHandler() {
201201 private fun tryIntercept (view : View , event : MotionEvent ) = view is ViewGroup && view.onInterceptTouchEvent(event)
202202
203203 private val defaultHook = object : NativeViewGestureHandlerHook {}
204+
205+ enum class ScrollDirection (val value : Int ) {
206+ UP (- 1 ),
207+ DOWN (1 ),
208+ NONE (0 ),
209+ }
204210 }
205211
206212 interface NativeViewGestureHandlerHook {
@@ -304,6 +310,7 @@ class NativeViewGestureHandler : GestureHandler() {
304310 private val handler : NativeViewGestureHandler ,
305311 private val swipeRefreshLayout : ReactSwipeRefreshLayout ,
306312 ) : NativeViewGestureHandlerHook {
313+ private var lastY: Float? = null
307314 override fun wantsToHandleEventBeforeActivation () = true
308315
309316 override fun handleEventBeforeActivation (event : MotionEvent ) {
@@ -323,11 +330,32 @@ class NativeViewGestureHandler : GestureHandler() {
323330 it is NativeViewGestureHandler
324331 }
325332
326- // If handler was found, it's active and the ScrollView is not at the top, fail the RefreshControl
327- if (scrollHandler != null && scrollHandler.state == STATE_ACTIVE && scroll.scrollY > 0 ) {
333+ // In old API ScrollView was detecting scroll even if RefreshControl hasn't been cancelled yet.
334+ // This doesn't work on new API, therefore we check scroll direction. This shouldn't affect old APIs.
335+ // To determine scroll direction, we will compare current event with previous one.
336+ // Note: Scrolling up is handled by `canScrollVertically` method.
337+ val scrollDirection = lastY?.let {
338+ val dy = it - event.y
339+
340+ when {
341+ dy < 0 -> ScrollDirection .UP
342+ dy > 0 -> ScrollDirection .DOWN
343+ else -> ScrollDirection .NONE
344+ }
345+ } ? : ScrollDirection .NONE
346+
347+ // We want to fail RefreshControl if we find active ScrollView handler and we either:
348+ // 1. scroll down,
349+ // 2. scroll up when we are not at the top of the list.
350+ if (scrollHandler != null &&
351+ scrollHandler.state == STATE_ACTIVE &&
352+ (scrollDirection == ScrollDirection .DOWN || scroll.canScrollVertically(ScrollDirection .UP .value))
353+ ) {
328354 handler.fail()
329355 }
330356
357+ lastY = event.y
358+
331359 // The drawback is that the smooth transition from scrolling to refreshing in a single swipe
332360 // is impossible this way and two swipes are required:
333361 // - one to go back to top
0 commit comments