diff --git a/libraries/htmlrenderer/api/src/main/kotlin/io/element/android/libraries/htmlrenderer/api/ui/HtmlMessageContent.kt b/libraries/htmlrenderer/api/src/main/kotlin/io/element/android/libraries/htmlrenderer/api/ui/HtmlMessageContent.kt
index e779b5086f..283aa356be 100644
--- a/libraries/htmlrenderer/api/src/main/kotlin/io/element/android/libraries/htmlrenderer/api/ui/HtmlMessageContent.kt
+++ b/libraries/htmlrenderer/api/src/main/kotlin/io/element/android/libraries/htmlrenderer/api/ui/HtmlMessageContent.kt
@@ -15,6 +15,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -53,8 +55,7 @@ import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.translate
-import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException
-import androidx.compose.ui.input.pointer.changedToUp
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
@@ -565,34 +566,18 @@ private fun Modifier.linkTapHandler(
val (url, urlText) = layoutResult.value?.urlAt(down.position, text) ?: return@awaitEachGesture
down.consume()
- val longPressTimeout = viewConfiguration.longPressTimeoutMillis
-
- // Try detecting a long press by waiting for the long press timeout and checking if we received an up event before that.
- // If we did, it's a tap, otherwise it's a long press.
- // This warning is a false positive.
- @Suppress("KotlinConstantConditions")
- val isLongPress = try {
- withTimeout(longPressTimeout) {
- var receivedUp = false
- while (!receivedUp) {
- val event = awaitPointerEvent()
- val up = event.changes.firstOrNull { it.id == down.id && it.changedToUp() }
- if (up != null) {
- receivedUp = true
- up.consume()
- }
- }
-
- !receivedUp
- }
- } catch (_: PointerEventTimeoutCancellationException) {
- true
- }
-
- if (isLongPress) {
+ // Returns a non-null change if the long press timeout elapsed, or null if the gesture was canceled by movement beyond touch slop or by an up event.
+ if (awaitLongPressOrCancellation(down.id) != null) {
context.onLinkLongClick(url, urlText)
+ // Swallow the rest of the gesture so parents don't react to the eventual release.
+ waitForUpOrCancellation()?.consume()
} else {
- context.onLinkClick(url, urlText)
+ // Either a tap (the pointer was released) or a drag (the user probably wanted to scroll).
+ val up = currentEvent.changes.firstOrNull { it.id == down.id && it.changedToUpIgnoreConsumed() }
+ if (up != null) {
+ up.consume()
+ context.onLinkClick(url, urlText)
+ }
}
}
}