From a492e2def3dabe5e8de5ef4486b92859850503db Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Tue, 29 Sep 2026 07:12:48 +0000 Subject: Reflow reader text live while pinching, and let a zoomed image pan Pinching in the reader used to scale the whole WebView like a photo and snap back to the real font size on release. It now drives WebSettings.textZoom as the fingers move, so the text itself resizes and reflows during the gesture with nothing to snap back from; images and margins stay put. The reload at the new saved size is what returns textZoom to 100%, so the two never overlap -- and loading reader HTML now goes through ReaderWebView.loadReaderHtml, which does both together, so a future load site can't forget the reset. Reader view for a tapped link now honors the saved font size and the same pinch gesture, instead of always rendering at the 18px default. A zoomed image can be panned again. The viewer computed its pan bounds from values captured when the dialog first composed -- an empty viewport around a not-yet-decoded image -- because a pointerInput(Unit) block keeps the lambda it was created with. Those bounds allowed exactly one offset, 0, so zooming worked and dragging did nothing. The whole pinch/pan step is now one pure function the gesture callback calls with values it reads fresh each time. --- .../org/terst/feedreader/ui/LinkReaderScreen.kt | 42 ++++++++++++++++------ 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt') diff --git a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt index b8956f2..4ed0722 100644 --- a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt @@ -31,10 +31,12 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -45,6 +47,8 @@ import androidx.compose.ui.viewinterop.AndroidView import kotlinx.coroutines.launch import org.terst.feedreader.data.AppDatabase import org.terst.feedreader.data.ArticleEntity +import org.terst.feedreader.data.DEFAULT_READER_FONT_SIZE_PX +import org.terst.feedreader.data.Settings import org.terst.feedreader.data.ensureStandaloneArticle import org.terst.feedreader.data.saveArticleToStash import org.terst.feedreader.data.urlToArticleId @@ -58,7 +62,9 @@ import java.net.URLEncoder * dropping into the system browser. Brought to parity with the main article reader: * save locally / save to Stash (lazily creates a standalone ArticleEntity, feedId=null, * keyed by a hash of the URL -- same scheme ShareReceiverActivity uses, so sharing the - * same link later lands on the same row instead of duplicating it), a reader-mode + * same link later lands on the same row instead of duplicating it), the saved reader font + * size and its pinch-to-resize gesture (a link read here is the same reading surface as an + * article, so it shouldn't ignore a size the article view respects), a reader-mode * toggle (falls back to the live page, JS on, when the user wants the original instead * of the extracted view or extraction failed), and the same login/search-alternate menu. */ @@ -68,9 +74,11 @@ import java.net.URLEncoder fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Unit, onOpenBrowser: (String) -> Unit) { val context = LocalContext.current val db = remember { AppDatabase.get(context) } + val settings = remember { Settings(context) } val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } val articleId = remember(url) { urlToArticleId(url) } + val fontSizePx by settings.readerFontSizePx.collectAsState(initial = DEFAULT_READER_FONT_SIZE_PX) var article by remember(url) { mutableStateOf(null) } var extractedTitle by remember(url) { mutableStateOf(null) } @@ -229,7 +237,12 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un val currentExtractedContent = extractedContent when { isReaderMode && currentExtractedContent != null -> { - val loadedContent = remember(url) { mutableStateOf(null) } + // Keyed on font size as well as content, exactly like ReaderScreen: a link + // opened in reader view is the same reading surface as an article, so it + // honors the same saved size and the same pinch-to-resize gesture, and picks + // up a size changed elsewhere while this screen stayed open. + val loadedKey = remember(url) { mutableStateOf?>(null) } + val latestFontSizePx by rememberUpdatedState(fontSizePx) AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> @@ -239,17 +252,21 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un onOpenLink = onOpenLink, onLongPressLink = { longPressedLink = it }, onYouTubeVideo = { youtubeVideoId = it }, - onImageTap = { zoomedImageUrl = it } + onImageTap = { zoomedImageUrl = it }, + currentFontSizePx = { latestFontSizePx }, + onFontSizeChanged = { newSizePx -> scope.launch { settings.setReaderFontSizePx(newSizePx) } } ) ).apply { - loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null) - loadedContent.value = currentExtractedContent + val html = wrapReaderHtml(currentExtractedContent, extractedTitle, fontSizePx = fontSizePx) + loadReaderHtml(url, html, fontSizePx) + loadedKey.value = currentExtractedContent to fontSizePx } }, update = { webView -> - if (loadedContent.value != currentExtractedContent) { - webView.loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null) - loadedContent.value = currentExtractedContent + if (loadedKey.value != currentExtractedContent to fontSizePx) { + val html = wrapReaderHtml(currentExtractedContent, extractedTitle, fontSizePx = fontSizePx) + webView.loadReaderHtml(url, html, fontSizePx) + loadedKey.value = currentExtractedContent to fontSizePx } }, onRelease = { webView -> webView.destroy() } @@ -276,8 +293,13 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un modifier = Modifier.fillMaxSize(), factory = { ctx -> WebView(ctx).apply { - settings.javaScriptEnabled = true - settings.domStorageEnabled = true + // Qualified: `settings` unqualified would now reach this + // screen's app Settings (reader font size), which shadows + // the WebView's own WebSettings inside this apply block. + // JS on here is this screen's documented fallback -- see the + // @SuppressLint and the http(s)-only guard just above. + this.settings.javaScriptEnabled = true + this.settings.domStorageEnabled = true loadUrl(url) } }, -- cgit v1.2.3