<feed xmlns='http://www.w3.org/2005/Atom'>
<title>feedreader.git/app/src/main/java/org/terst/feedreader/data/Daos.kt, branch master</title>
<subtitle>FeeDreader — personal Android RSS reader
</subtitle>
<id>https://git.terst.org/feedreader.git/atom?h=master</id>
<link rel='self' href='https://git.terst.org/feedreader.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/'/>
<updated>2026-08-24T08:00:51+00:00</updated>
<entry>
<title>Remove 3 confirmed-dead DAO methods</title>
<updated>2026-08-24T08:00:51+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-24T08:00:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=a0708d3785b93421273e09e0097a43db053937b2'/>
<id>urn:sha1:a0708d3785b93421273e09e0097a43db053937b2</id>
<content type='text'>
ArticleDao.delete(article) -- superseded by deleteUnsavedByFeed.
ArticleDao.markAllReadForFeed -- no "mark all read for this feed" UI
action exists; superseded by the id-list-based markReadBatchChunked.
FilterRuleDao.getAll() -- every caller uses the Flow-returning
observeAll() instead. Re-verified all three have zero callers beyond
their own declaration before removing.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
</content>
</entry>
<entry>
<title>Keep a just-read article visible (and Previous-reachable) until the next fetch</title>
<updated>2026-08-22T01:13:30+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:13:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=55cdfc8fab6ddd1052593f8fd53dbb5bf730cc63'/>
<id>urn:sha1:55cdfc8fab6ddd1052593f8fd53dbb5bf730cc63</id>
<content type='text'>
The Unread/Older views are live Room Flow queries filtered on
isRead = 0, so marking an article read (which only happens on
Next/Previous, not on open) makes it vanish from the list the instant
Room re-emits -- mid-scroll, or from underneath the reader's own
Previous button, which re-queries this same flow to find its
neighbors. Concretely: Next from C to D marks C read and prunes it
from the live list; Previous from D then can't find C anymore, and
jumps past it to whatever was unread before C. A misclick or the
in-flight prune could cost you the article you meant to keep reading.

Adds ArticleEntity.readAt (set whenever isRead flips true, cleared on
explicit "mark unread"); observeUnread/observeOlderUnread now match
"isRead = 0 OR readAt &gt;= sinceFetch", where sinceFetch is
Settings.lastFetchAt -- the same durable signal the new-articles
snackbar already uses. A read article now lingers (shown dimmed via
the existing showReadState styling) until the next real fetch
actually completes, not the next DB write, and Previous/Next read from
the same still-inclusive query so stepping back to what you just read
finds it right where it was.
</content>
</entry>
<entry>
<title>Feed writes: bulk OPML upsert, targeted fetch-result update, shared toggle</title>
<updated>2026-08-22T01:01:05+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:01:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=a3296f5c14a5fb95feaa3751044a1995935e6f3e'/>
<id>urn:sha1:a3296f5c14a5fb95feaa3751044a1995935e6f3e</id>
<content type='text'>
- AddFeedScreen: OPML import no longer crashes the app on a
  non-XML/corrupt file (try/catch around the whole import). Bulk
  upsertAll() instead of a per-feed getById()+upsert() loop -- 2N
  sequential DB round-trips for N feeds otherwise, real cost at 100+
  feed subscription lists.
- FeedDao.updateFetchResult only writes the columns a fetch actually
  produces, instead of a full-row @Update built from a feed snapshot
  read before a (possibly slow) network round-trip -- the old path
  could silently revert a readerModeEnabled/folder edit made via
  FeedListScreen while that feed's fetch was still in flight.
- setReaderModeEnabled(feed, enabled) persists the toggle and returns
  the exact confirmation text, shared by FeedListScreen's per-row
  toggle and ReaderScreen's toggleFeedReaderMode -- previously two
  independently-typed copies of the same snackbar message.
</content>
</entry>
<entry>
<title>In-app YouTube playback, shared ConfirmDialog, hardened link handling</title>
<updated>2026-08-22T01:00:02+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:00:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=fd49bacfb92ef8d29cbad56419acd016ff40f357'/>
<id>urn:sha1:fd49bacfb92ef8d29cbad56419acd016ff40f357</id>
<content type='text'>
- ReaderWebView/LinkReaderScreen/ReaderScreen: tapping a YouTube link
  inside an article now opens an in-app player (youtube-nocookie.com,
  JS scoped to just that dialog) instead of handing off to the
  YouTube app/browser. Falls back to external-open when a confident
  video ID can't be extracted (playlist/channel links).
- A subframe (ad/embed iframe) auto-navigating is now blocked outright
  instead of being treated like a real link tap -- it fired the exact
  same callback as a user tap, with no interaction required.
- wrapReaderHtml can now show a fallback hero image + date when the
  feed's own body HTML has no image of its own.
- ConfirmDialog: the generic "confirm before doing something
  irreversible" dialog shape was independently rebuilt from scratch at
  FeedListScreen's unsubscribe and ArticleListScreen's mark-read-from-
  here confirmations. Both now share one composable; the latter also
  chunks its batch update (markReadBatchChunked) so a long-unread feed
  can't exceed SQLite's per-statement bound-variable limit.
</content>
</entry>
<entry>
<title>Text-only bottom bar, re-extract action, fix YouTube thumbnails, verify Stash creds before saving</title>
<updated>2026-07-25T00:55:59+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-25T00:55:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=f202c603259c40c4d21a4385c9f69a3efca87acc'/>
<id>urn:sha1:f202c603259c40c4d21a4385c9f69a3efca87acc</id>
<content type='text'>
- Bottom bar: Previous/Keep unread/Next are now plain text labels, no icons
- Add "Re-download and re-extract" to the overflow menu (ReaderScreen and
  LinkReaderScreen) to force a fresh fetch + Readability pass on demand,
  overwriting whatever's cached
- Fix a real compile bug in the previous commit's reader-mode-off handler
  (passed null into a non-null setExtractedContent) via a proper
  clearExtractedContent() DAO query
- YouTube embeds: fixed the broken-image-glyph bug two ways -- (1) an
  11-char video-ID regex was accidentally matching a playlist embed's
  literal "videoseries" placeholder path, building a thumbnail URL for a
  video that doesn't exist, and (2) Readability's link-density cleanup was
  discarding the replacement entirely (a bare &lt;a&gt;&lt;img&gt;&lt;/a&gt; reads as an
  ad/share unit). Restructured as a &lt;figure&gt;/&lt;figcaption&gt; pair -- the same
  shape a normal captioned photo takes -- confirmed live this survives
  extraction intact
- Stash settings: API key field is now a password field with a show/hide
  toggle, and "Verify and save" makes a real authenticated request before
  persisting anything, showing why it failed instead of silently saving
  bad credentials

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Rebrand to FeeDreader; overflow-menu cleanup; deferred read-marking; folder-organized feed list</title>
<updated>2026-07-23T07:38:28+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-23T07:38:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=ecdb45778e274126737aa87e27ed1b1c00cda01c'/>
<id>urn:sha1:ecdb45778e274126737aa87e27ed1b1c00cda01c</id>
<content type='text'>
- Rename app to FeeDreader with an owl launcher icon and a paywall-avoidance
  note in Settings
- Move Open original/Save to Stash into the reader's overflow menu; add
  Mark unread there too
- Only mark an article read when navigating away via Previous/Next, not on
  open or on back
- Saved list no longer crosses out read items; its count includes them
- Back from Unread (the root screen) now opens the feed list instead of
  exiting the app
- Feed list: clearer separator above the per-feed list, folder grouping
  (from OPML categories) with case-insensitive sort, and a virtual "Last
  fetch failed" section at the top
- Reader extraction: cap unsized decorative SVG icons (fixes Ars
  Technica's giant section-badge icon), force native loading="lazy"
  images to load eagerly in the hidden extraction WebView, and drop a
  lightbox's duplicate caption only when a real figcaption sibling exists
- Backlog notes on swipe-to-next research, extraction test coverage, and
  an async-rendered-gallery race confirmed live on Ars Technica

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Fix extraction regression on kottke; add Older view, mark-read-from-here</title>
<updated>2026-07-22T20:37:41+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T20:37:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=34b663c6e75a2c360de154418dafb8f616b546a1'/>
<id>urn:sha1:34b663c6e75a2c360de154418dafb8f616b546a1</id>
<content type='text'>
- ReaderExtractor: the un-hide-before-extracting fix (for soft paywalls
  like West Hawaii Today) was unconditional -- it also un-hid normal,
  legitimately-collapsed chrome (kottke's footer/membership notice), which
  Readability then folded into the result alongside the real article.
  Reported live with a screenshot showing the extracted content wrapped in
  site footer boilerplate. Now tries the normal (hidden-respecting) pass
  first and only falls back to the un-hide pass if that came back thin
  (&lt;500 chars), keeping whichever pass actually found more text. Verified
  live: the same kottke article now stays clean and stable (no delayed
  switch to the footer version).

- New Unread/Older split: the default Unread view is now clamped to the
  last 72 hours (RECENT_WINDOW_MS); the rest of the unread backlog moved
  to a new "Older" view (new FeedListScreen row, ArticleDao.observeUnread
  now takes a cutoff, observeOlderUnread/observeUnreadCount/
  observeOlderUnreadCount added). Undated articles are kept in the main
  Unread view rather than silently dropped from both. Verified live: 150
  recent / 1172 older, summing to the prior combined total.

- "Mark read from here": long-press an article row to mark it and
  everything older in that same filtered list as read in one batch
  (re-added ArticleDao.markReadBatch, this time as an explicit,
  confirmed action -- not the implicit on-scroll version that was
  reverted earlier). Verified live: confirmed a 147-article batch: the
  tapped article and older were marked read, newer ones above it were not.

- BACKLOG.md: added a YouTube-embeds note (JS is off in the reader
  WebView, so embeds currently show broken -- rough shape is a tap-to-play
  thumbnail rather than turning JS on for third-party content).
</content>
</entry>
<entry>
<title>Remove mark-as-read-on-scroll</title>
<updated>2026-07-22T19:04:30+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T19:04:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=36ed34cd9ccd7388b96510e0225afe96979403a9'/>
<id>urn:sha1:36ed34cd9ccd7388b96510e0225afe96979403a9</id>
<content type='text'>
Explicitly asked to revert: scrolling the article list shouldn't mark
anything read on its own -- only opening an article (or the existing
manual actions) should. Removed the LazyListState/snapshotFlow tracking
in ArticleListScreen and the now-unused ArticleDao.markReadBatch.

Verified: unread count in the DB is unchanged after scrolling through
the list (1332 before and after).
</content>
</entry>
<entry>
<title>Smooth scrolling, dark reader, mark-read-on-scroll, feed names, per-feed reader-mode opt-out</title>
<updated>2026-07-22T17:56:50+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T17:56:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=9da3fbcdef71e90d52045531c7c18faff02f2ff6'/>
<id>urn:sha1:9da3fbcdef71e90d52045531c7c18faff02f2ff6</id>
<content type='text'>
- ReaderScreen: AndroidView's update() was reloading the WebView's HTML on
  every recomposition (e.g. isSaving flipping mid-read), resetting scroll
  position -- this is what made scrolling feel unsmooth. Now only reloads
  when the content string actually changes.
- ReaderScreen: reader content now uses a hardcoded dark palette instead of
  browser-default white, matching the app's always-dark theme.
- ArticleListScreen: mark-as-read-on-scroll -- once an article has scrolled
  fully above the viewport it's marked read, the standard feed-reader
  behavior, via a new batched ArticleDao.markReadBatch(). Verified against
  the on-device DB (scrolled-past articles flipped to isRead=1, the
  still-visible one stayed unread).
- ArticleListItem/queries: LEFT JOIN feeds for a feedTitle column, shown per
  row in the Unread/Saved aggregated views and as a reader subtitle; the
  per-feed article list's top bar now shows the real feed title instead of
  a generic "Articles".
- FeedEntity.readerModeDisabled (schema v3, migration 2-&gt;3): per-feed
  opt-out of the Readability fallback/toggle, toggled from a new icon in
  FeedListScreen. Added for kottke.org specifically -- confirmed its thin
  linkblog posts were making Readability extract the page footer instead
  of the actual post; disabling reader mode for that feed now shows its
  own (short but correct) content instead.

All verified live on the emulator against the real 107-feed dataset.
</content>
</entry>
<entry>
<title>Fix Unread/startup crash; add reader-mode toggle and swipe navigation</title>
<updated>2026-07-22T16:54:03+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T16:54:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=39cca1cb3650edb066f57d0891b1873b767b1c43'/>
<id>urn:sha1:39cca1cb3650edb066f57d0891b1873b767b1c43</id>
<content type='text'>
- Root cause of the reported crashes: observeUnread/observeByFeed/
  observeSaved used SELECT *, pulling feedContentHtml/extractedContent
  (potentially large per-article blobs) just to render a list row.
  With 107 feeds worth of real articles this blows past Android's
  CursorWindow/memory limits, scaling with item count -- and since
  Unread is now the landing screen, it crashed at startup too. Added
  an ArticleListItem projection excluding the heavy content columns;
  list queries now only select what a row actually renders.
- FilterRuleEngine now takes a small Titled interface (title/feedId)
  instead of the full entity, so it works with both ArticleEntity and
  the new lighter projection.
- Reader-mode toggle: a button in the reader to manually switch
  between feed-provided content and Readability-extracted full text,
  matching Inoreader's full-text-view toggle (previously only an
  automatic fallback when feed content was thin).
- Swipe between articles: ReaderScreen now wraps a HorizontalPager over
  a snapshot of the current list's article-id order, so swiping moves
  to the next/previous article. Snapshotted rather than live, since
  marking an article read would otherwise drop it from the live Unread
  query mid-swipe and shift every later page underneath the user.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
</feed>
