summaryrefslogtreecommitdiff
path: root/.coverage-baseline
AgeCommit message (Collapse)Author
2026-09-20Fix intermittent extraction failures, and stop them being silentPeter Stone
Bug report: extraction failed on one 1500days.com article while others worked. It is not that article. Extraction was racing the page load, and the report could say nothing more because the app discarded the reason. Two separate defects. 1. The race. onPageFinished fires before the page has finished loading — observed at progress=25 on this article — and the extraction script was injected there. Sometimes the DOM is far enough along; sometimes evaluateJavascript hands back a bare "null" because the document is still being replaced. Measured against the reported URL: 4 successes in 6 attempts before, 10 in 10 after. An unusable result is no longer final. A null or empty-content result is retried (6 attempts, 500ms apart, well inside the existing 25s timeout); a Readability error still fails immediately, since retrying will not change what the parser thinks of the markup. 2. The silence. ReaderScreen did `extracted?.content?.let { }`, so every failure — dead host, HTTP error, timeout, parser throw — arrived as the same null and was dropped. The reader just kept showing raw feed content. The only way a failure ever surfaced was the user noticing. extractResult() now returns a typed ExtractionFailure carrying the reason; ReaderScreen logs the diagnostic and shows the user a short explanation. Also replaced a dead error callback. The only override was the four-argument onReceivedError, which Android stopped calling at API 23; minSdk is 26, so it never fired on any supported device. A failed load therefore invoked nothing and hung for the full 25 seconds before surfacing as an indistinguishable null. Now overrides the request/error and onReceivedHttpError forms Android actually calls, both filtered to the main frame so a 404'd tracker cannot abort an article that renders fine. The instrumented test does NOT reproduce the race and says so in its own docstring: a MockWebServer page finishes loading before onPageFinished fires, and slow subresources do not help because WebView waits for them. It is kept as a reliability guard over repeated back-to-back extractions. The race was verified by hand against the live URL. 47 unit tests, 1 instrumented test, both green.
2026-09-15Support pinch-to-zoom for reader text size in the article viewPeter Stone
Pinching in ReaderScreen's article WebView now adjusts the same persisted reader font size the Settings slider controls, instead of doing nothing (WebView's own page zoom was never enabled, so a pinch previously had no effect at all). A ScaleGestureDetector is fed through setOnTouchListener, which always returns false so it never consumes the touch -- WebView's existing one-finger scrolling, link taps and long-press-link handling are unaffected, since ScaleGestureDetector only fires once a second pointer is down. During the gesture the WebView is scaled live via View.scaleX/ scaleY for immediate visual feedback; at gesture end the scale resets to 1x and the computed, clamped font size is persisted, which drives ReaderScreen's existing content-reload-on-fontSizePx-change path to re-render the actual HTML at the new size. TDD: pinchZoomFontSizePx (the pure scale-factor -> clamped-px calculation) was written test-first, red on a TODO() stub, green on the real implementation -- covered by SettingsTest. newReaderWebView grew from 6 to 8 parameters to carry the new gesture hooks, which pushed it over detekt's LongParameterList threshold; bundled all of its callbacks into a ReaderWebViewCallbacks data class rather than baselining past it, updating both call sites (ReaderScreen, LinkReaderScreen). ReaderContent gained no new parameter -- it now independently remembers its own Settings instance to persist the pinch-committed size, matching how it already remembers its own db and scope rather than threading them from ReaderScreen. LinkReaderScreen's WebView is untouched: it doesn't read the reader font size setting at all today, so wiring pinch-zoom there would be a separate, unrelated fix.
2026-08-31Add reader font size setting, rewrite paywall-avoidance settings copyPeter Stone
Font size was hardcoded at 18px in ReaderWebView's injected CSS. Added a DataStore-backed setting (Settings.readerFontSizePx, 14-28px range) with a slider in Settings, threaded through ReaderScreen/ReaderContent into wrapReaderHtml. The reload-tracking key in ReaderContent's AndroidView now includes font size alongside content, so changing it and returning to an already-open article picks up the new size without reloading on every unrelated recomposition (matches the existing comment there about avoiding scroll-position jank). Rewrote the Settings screen's paywall note -- it read as defensive throat-clearing about what the app won't do. Restated as what it's actually for: avoiding tracking, manipulation, and junk fees, with reader mode pulling up anything findable by search anyway. Regenerated the detekt baseline: ReaderContent's pre-existing LongMethod/LongParameterList/CyclomaticComplexMethod findings are keyed by exact function signature text, and adding the fontSizePx parameter changed that text, invalidating the old baseline entries. The underlying debt (a 300+ line, 9-now-10-parameter composable) predates this change and isn't what this commit is about. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-08-24Let filter rules be scoped to one feed; fix coverage-gate report path bugPeter Stone
The data model and FilterRuleEngine already supported a per-feed scope (FilterRuleEntity.feedId), but Settings' "Add rule" form never let a user set it -- only a hardcoded migration had ever created one. Add a FeedScopePicker so a rule can target "All feeds" (unchanged default) or one specific feed, and show each existing rule's scope in the list. Added the first real unit tests in this repo (FilterRuleEngineTest), covering global vs. feed-scoped matching and mute-wins-over-highlight priority -- the exact behavior this UI gap left unexercised outside one migration-seeded row. First real jacoco coverage number: 2.31%. While verifying, found the pre-commit hook's coverage step was silently a no-op: it pointed at app/build/reports/jacoco/report.xml, but the jacoco setup that landed here uses the standard Gradle JacocoReport task, which writes to reports/jacoco/jacocoTestReport/jacocoTestReport.xml instead. Fixed the path so the gate actually reads real numbers now. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
2026-08-24Add TDD quality gate: detekt, coverage tracking, pre-commit hook; fix theme ↵Peter Stone
crash risk CLAUDE.md now documents mandatory red-green-refactor TDD, a per-commit quality gate (build, tests, lint, detekt, coverage-non-decrease, WebView-JS justification, manifest permission diff, secret scan), and why each check exists (tracing back to real bugs this audit found). Wires up detekt (static analysis) and a standalone Jacoco setup (scoped to data/readability/opml/work/stash -- ui/ Compose screens are impractical to unit-test for coverage) via app/build.gradle.kts. Bumped Kotlin/KSP to 2.0.21/2.0.21-1.0.28 to match detekt 1.23.8's compatibility requirement. Also fixes the themes.xml bug the lint gate immediately caught: Theme.FeedReader's parent required API 29 while minSdk is 26 -- a real crash risk on Android 8.0-9.0 devices. Swapped to the plain (non-DayNight) DeviceDefault parent, since Compose already owns all real theming via a hardcoded darkColorScheme() regardless of system day/night state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK