Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ class NativeViewGestureHandler : GestureHandler() {
private fun tryIntercept(view: View, event: MotionEvent) = view is ViewGroup && view.onInterceptTouchEvent(event)

private val defaultHook = object : NativeViewGestureHandlerHook {}

enum class ScrollDirection(val value: Int) {
UP(-1),
DOWN(1),
NONE(0),
}
}

interface NativeViewGestureHandlerHook {
Expand Down Expand Up @@ -304,6 +310,7 @@ class NativeViewGestureHandler : GestureHandler() {
private val handler: NativeViewGestureHandler,
private val swipeRefreshLayout: ReactSwipeRefreshLayout,
) : NativeViewGestureHandlerHook {
private var lastY: Float? = null
override fun wantsToHandleEventBeforeActivation() = true

override fun handleEventBeforeActivation(event: MotionEvent) {
Expand All @@ -323,11 +330,32 @@ class NativeViewGestureHandler : GestureHandler() {
it is NativeViewGestureHandler
}

// If handler was found, it's active and the ScrollView is not at the top, fail the RefreshControl
if (scrollHandler != null && scrollHandler.state == STATE_ACTIVE && scroll.scrollY > 0) {
// In old API ScrollView was detecting scroll even if RefreshControl hasn't been cancelled yet.
// This doesn't work on new API, therefore we check scroll direction. This shouldn't affect old APIs.
// To determine scroll direction, we will compare current event with previous one.
// Note: Scrolling up is handled by `canScrollVertically` method.
val scrollDirection = lastY?.let {
val dy = it - event.y

when {
dy < 0 -> ScrollDirection.UP
dy > 0 -> ScrollDirection.DOWN
else -> ScrollDirection.NONE
}
} ?: ScrollDirection.NONE

// We want to fail RefreshControl if we find active ScrollView handler and we either:
// 1. scroll down,
// 2. scroll up when we are not at the top of the list.
if (scrollHandler != null &&
scrollHandler.state == STATE_ACTIVE &&
(scrollDirection == ScrollDirection.DOWN || scroll.canScrollVertically(ScrollDirection.UP.value))
) {
handler.fail()
}

lastY = event.y

// The drawback is that the smooth transition from scrolling to refreshing in a single swipe
// is impossible this way and two swipes are required:
// - one to go back to top
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export const ScrollView = (
<GHScrollView
{...rest}
ref={props.ref}
updateGesture_CAN_CAUSE_INFINITE_RERENDER={updateGesture}
onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER={updateGesture}
// @ts-ignore we don't pass `refreshing` prop as we only want to override the ref
refreshControl={
refreshControl
Expand Down
Loading