Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent pull-to-refresh from triggering while scrolling in a nested scrollable element on the page #310

Merged
merged 2 commits into from
Feb 24, 2024
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
34 changes: 34 additions & 0 deletions turbo/src/main/assets/js/turbo_bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,37 @@
}
}

// Touch detection, allowing vertically scrollable elements
// to scroll properly without triggering pull-to-refresh.

const elementTouchStart = (event) => {
if (!event.target) return

var element = event.target

while (element) {
const canScroll = element.scrollHeight > element.clientHeight
const overflowY = window.getComputedStyle(element).overflowY

if (canScroll && (overflowY === "scroll" || overflowY === "auto")) {
TurboSession.elementTouchStarted(true)
break
}

element = element.parentElement
}

if (!element) {
TurboSession.elementTouchStarted(false)
}
}

const elementTouchEnd = () => {
TurboSession.elementTouchEnded()
}

// Setup and register adapter

window.turboNative = new TurboNative()

const setup = function() {
Expand All @@ -187,6 +218,9 @@

document.removeEventListener("turbo:load", setup)
document.removeEventListener("turbolinks:load", setup)

document.addEventListener("touchstart", elementTouchStart)
document.addEventListener("touchend", elementTouchEnd)
}

const setupOnLoad = () => {
Expand Down
22 changes: 22 additions & 0 deletions turbo/src/main/kotlin/dev/hotwire/turbo/session/TurboSession.kt
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,28 @@ class TurboSession internal constructor(
callback { it.onReceivedError(-1) }
}

/**
* Called when a touched element event has started.
*
* Warning: This method is public so it can be used as a Javascript Interface.
* You should never call this directly as it could lead to unintended behavior.
*/
@JavascriptInterface
fun elementTouchStarted(scrollable: Boolean) {
webView.elementTouchIsScrollable = scrollable
}

/**
* Called when a touched element event has ended.
*
* Warning: This method is public so it can be used as a Javascript Interface.
* You should never call this directly as it could lead to unintended behavior.
*/
@JavascriptInterface
fun elementTouchEnded() {
webView.elementTouchIsScrollable = false
}

// Private

private fun visitLocation(visit: TurboVisit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dev.hotwire.turbo.views

import android.content.Context
import android.util.AttributeSet
import android.webkit.WebView
import androidx.core.view.children
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout

Expand All @@ -14,8 +13,13 @@ internal class TurboSwipeRefreshLayout @JvmOverloads constructor(context: Contex
}

override fun canChildScrollUp(): Boolean {
val webView = children.firstOrNull() as? WebView
return webView?.scrollY ?: 0 > 0
val webView = children.firstOrNull() as? TurboWebView

return if (webView != null) {
webView.scrollY > 0 || webView.elementTouchIsScrollable
} else {
false
}
}

/**
Expand Down
5 changes: 3 additions & 2 deletions turbo/src/main/kotlin/dev/hotwire/turbo/views/TurboWebView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import android.util.AttributeSet
import android.webkit.WebView
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
import androidx.webkit.WebViewCompat
import com.google.gson.GsonBuilder
import dev.hotwire.turbo.util.contentFromAsset
import dev.hotwire.turbo.util.runOnUiThread
import dev.hotwire.turbo.util.toJson
import dev.hotwire.turbo.visit.TurboVisitOptions
import com.google.gson.GsonBuilder

/**
* A Turbo-specific WebView that configures required settings and exposes some helpful info.
Expand Down Expand Up @@ -72,6 +71,8 @@ open class TurboWebView @JvmOverloads constructor(context: Context, attrs: Attri
}
}

internal var elementTouchIsScrollable = false

private fun WebView.runJavascript(javascript: String, onComplete: (String?) -> Unit = {}) {
context.runOnUiThread {
evaluateJavascript(javascript) {
Expand Down
Loading