Skip to content

Keep the newest unread articles on the device for offline reading - #4

Merged
ajaydsouza merged 1 commit into
masterfrom
wz/elegant-dijkstra-lvdz1o
Oct 21, 2026
Merged

ajaydsouza merged 1 commit into
masterfrom
wz/elegant-dijkstra-lvdz1o

Conversation

@ajaydsouza

Copy link
Copy Markdown
Contributor

The installed app now keeps the newest unread articles, with their stored images, on each device, so they can be read without a connection.

How it works

  • GET /offline/articles lists what to keep: the newest unread articles that have a readable copy (Settings → Articles kept for offline reading, 30 by default, 0 for none). Each entry has the reader URL, a version that changes whenever the page would, and the stored images the article uses.
  • public/sw.js syncs whenever a logged-in page opens online (at most every 30 seconds). It fetches new or changed reader pages, their stored images and the built CSS/JS they load, and deletes anything no longer listed. Archiving or deleting an article removes its copy at the next sync.
  • Offline: a kept article opens as normal, with a notice that it's the saved copy. Any other page falls back to offline.html, which now lists the saved articles (via offline.js, a plain file so it runs under any CSP).
  • Reading progress made offline is held in localStorage and sent when the connection is back. Online progress also clears anything queued for that article, so an older value never overwrites a newer one.
  • Online behaviour is unchanged: navigations always go to the network first.

Logging out

Nothing stays on the device after logout:

  • the logout form clears the copies before it submits;
  • the logout response sends Clear-Site-Data: "cache", "storage";
  • a sync that finds the session gone (401 or a redirect to login) deletes them.

Testing

  • OfflineReadingTest: auth, selection and ordering, the limit and 0, image extraction, versioning, the setting on the page, the sync marker on logged-in pages only, the logout header, and that sw.js asks for the route the app serves.
  • Full suite, Pint and PHPStan pass locally.
  • End to end in Chromium against php artisan serve, with the browser offline and the server stopped (Playwright's offline mode doesn't reach service-worker requests):
    • all 3 unread articles synced and the archived one left out;
    • the article, its image, the CSS and JS all load offline, and the offline notice shows;
    • / and /archive fall back to the offline list;
    • progress queued offline reaches the database once back online;
    • archiving drops the copy at the next sync;
    • logging out leaves no article cache.

Generated by Claude Code

The service worker now syncs offline copies whenever a logged-in page opens
online. It reads /offline/articles (the newest unread articles with a
readable copy, 30 by default, set under Settings), fetches each reader page
that is new or has changed along with its stored images and the built CSS
and JS, and deletes anything no longer listed, so archiving or deleting an
article removes its copy at the next sync.

Offline, a kept article opens as normal with a notice that it is the saved
copy, and any other page falls back to an offline page listing what is
available. Reading progress made offline is held in localStorage and sent
when the connection returns. Pages are still fetched from the network
first, so online behaviour is unchanged.

Nothing stays on the device after logging out: the logout form clears the
copies before it submits, the response sends Clear-Site-Data, and a sync
that finds the session gone deletes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DPvSBmPyHN2QVmXsQQVKe
@ajaydsouza
ajaydsouza merged commit 335911e into master Oct 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant