| Age | Commit message (Collapse) | Author |
|
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
|
|
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 >= 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.
|
|
- 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.
|
|
- 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.
|
|
Stash creds before saving
- 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 <a><img></a> reads as an
ad/share unit). Restructured as a <figure>/<figcaption> 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
folder-organized feed list
- 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
- 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
(<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).
|
|
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).
|
|
reader-mode opt-out
- 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->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.
|
|
- 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
- Fix OPML import status message reporting the raw pre-dedup count
(144) instead of the actual unique feed count after Room's upsert
collapses duplicates (108) -- cosmetic bug, the DB was already
correct.
- Article thumbnails: extract media:thumbnail/enclosure or the first
<img> in feed content, show via Coil in the article list.
- App now lands on Unread instead of the feed list; a top-bar icon
gets from there to the feed list (forward nav, not back-stack pop,
since Unread is now the root).
- Dark color scheme by default.
- Delete-feed action (with confirmation) on each feed row -- was
entirely missing. Deletes its non-saved articles too, so they don't
linger as orphaned rows in the cross-feed Unread view; saved articles
are kept regardless.
- Room migration 1->2 for the new imageUrl column (real migration, not
destructive fallback, since there's now real on-device subscription
data from testing).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
Feed subscriptions (RSS 2.0 + Atom, hand-rolled parser), OPML
import/export, WorkManager periodic fetch with conditional GET,
Room-backed local storage (feeds/articles/filter rules), keyword
mute/highlight filtering.
Reader mode: on-device extraction via Mozilla's Readability.js
(vendored from github.com/mozilla/readability, pinned to release
0.6.0) run in a hidden WebView, for feed articles whose provided
content is too thin to be the real article body.
Save: explicit action (from a feed article, or the Android share-sheet
target for any URL) archives to Stash via POST /artifacts?sync=true,
while a local on-device copy (Readability-extracted) is always kept so
saved articles read offline regardless of Stash's reachability.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|