diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-22 18:56:47 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-22 18:56:47 +0000 |
| commit | bedf8a47a8b0601f4e3c253e4d2a7325c6ccbfc6 (patch) | |
| tree | ddfaebfc7d7b2307f7d8658bbfa74cefce27b377 /app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | |
| parent | e804e7a77a6cac048d5f6edeac0a339ca89e4d40 (diff) | |
Fix accidental page-swipe while scrolling; bring linked-article reader to parity
- ReaderScreen: HorizontalPager's full-surface drag detection was treating
ordinary vertical scrolling inside the embedded WebView (a native View,
which doesn't arbitrate with Compose's gesture system the way a
pure-Compose scrollable child would) as an attempted horizontal page-turn
on anything less than a perfectly vertical swipe -- i.e. most real thumb
scrolling. Tried gating it to edge-zone-only drags first, but that
collides with Android's own edge-swipe-back gesture on gesture-nav
devices (reproduced live: an edge swipe triggered system back, not the
pager). Landed on disabling the Pager's built-in gesture and driving it
from a custom detector that works anywhere on screen but only commits to
a page turn once a drag is clearly horizontal-dominant (>2x the vertical
movement) -- ordinary scrolling, even sloppy/diagonal, is left completely
unconsumed for the WebView. Verified live: a deliberately diagonal
mid-screen scroll now stays on the same article; a decisive horizontal
swipe still changes pages.
- LinkReaderScreen: was missing the bookmark/Stash/reader-toggle/menu
actions entirely -- reported after tapping a link left "all my icons...
gone." Brought to parity with the main article reader: save locally and
save to Stash now lazily create a standalone ArticleEntity (feedId=null,
id = hash of the URL, same scheme ShareReceiverActivity uses) on first
use; reader-mode toggle falls back to the live page (JS on) instead of
always forcing extraction; same login/find-another-source menu. Verified
live: all 5 icons present once extraction finishes, bookmarking a linked
article correctly creates a standalone DB row.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | 77 |
1 files changed, 70 insertions, 7 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt index 8c2f64e..9b53f8c 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -3,6 +3,7 @@ package org.terst.feedreader.ui import android.content.Intent import android.net.Uri import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -40,8 +41,11 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -54,6 +58,7 @@ import org.terst.feedreader.readability.ReaderExtractor import org.terst.feedreader.stash.StashClient import org.terst.feedreader.stash.StashSaveResult import java.net.URLEncoder +import kotlin.math.abs // Below this length (of the plain feed-provided HTML), assume it's a teaser/summary, // not the real article body, and auto-fall-back to on-device Readability extraction. @@ -100,14 +105,72 @@ fun ReaderScreen( val safeIds = if (ids.contains(initialArticleId)) ids else listOf(initialArticleId) val initialIndex = safeIds.indexOf(initialArticleId).coerceAtLeast(0) val pagerState = rememberPagerState(initialPage = initialIndex) { safeIds.size } + val scope = rememberCoroutineScope() + val directionLockPx = with(LocalDensity.current) { 16.dp.toPx() } + val swipeThresholdPx = with(LocalDensity.current) { 80.dp.toPx() } - HorizontalPager(state = pagerState, modifier = Modifier.fillMaxSize()) { page -> - ReaderContent( - articleId = safeIds[page], - onBack = onBack, - onOpenLink = onOpenLink, - onOpenBrowser = onOpenBrowser - ) + // HorizontalPager's own full-surface drag detection was grabbing ordinary vertical + // scrolling inside the embedded WebView (a native View, which doesn't arbitrate with + // Compose's gesture system the way a pure-Compose scrollable child would) as an + // attempted horizontal page-turn on anything less than a perfectly vertical swipe -- + // which is most real thumb scrolling. An edge-zone-gated version of this was tried + // first, but on gesture-nav Android the screen edges belong to the OS's own back + // gesture, which wins before the app ever sees the touch (confirmed live: an edge + // swipe triggered system back, not the pager). So instead: disable the Pager's own + // gesture and drive it from a detector that works anywhere on screen but only commits + // to a page turn once the drag is clearly horizontal-dominant (>2x the vertical + // movement) -- ordinary scrolling, even sloppy/diagonal, stays vertical-dominant and + // is left completely unconsumed for the WebView to handle normally. + Box( + modifier = Modifier + .fillMaxSize() + .pointerInput(safeIds.size) { + var totalDx = 0f + var totalDy = 0f + var committedHorizontal = false + detectDragGestures( + onDragStart = { + totalDx = 0f + totalDy = 0f + committedHorizontal = false + }, + onDragCancel = { committedHorizontal = false }, + onDragEnd = { + if (committedHorizontal) { + val direction = when { + totalDx <= -swipeThresholdPx -> 1 + totalDx >= swipeThresholdPx -> -1 + else -> 0 + } + if (direction != 0) { + val target = (pagerState.currentPage + direction).coerceIn(0, safeIds.size - 1) + scope.launch { pagerState.animateScrollToPage(target) } + } + } + committedHorizontal = false + }, + onDrag = { change, dragAmount -> + totalDx += dragAmount.x + totalDy += dragAmount.y + if (!committedHorizontal && + abs(totalDx) > directionLockPx && + abs(totalDx) > abs(totalDy) * 2f + ) { + committedHorizontal = true + } + if (committedHorizontal) change.consume() + } + ) + } + ) { + HorizontalPager(state = pagerState, userScrollEnabled = false, modifier = Modifier.fillMaxSize()) { page -> + ReaderContent( + articleId = safeIds[page], + onBack = onBack, + onOpenLink = onOpenLink, + onOpenBrowser = onOpenBrowser + ) + } } } |
