"Since you last visited" cue + PWA install (add to home screen)
Two calm returning-reader features. Since-last-visit (Highlights companion, not a nav lane — per Codex): * queries.feed gains a `since` filter; GET /api/since?ts= returns the count + a few accepted/non-dup/visible articles discovered since the reader's last visit (boundary-respecting; invalid/future ts → 0, no error). * Home stores last_seen in localStorage (reads prev, then stamps now); on Highlights, a gentle "Since you were last here, N new calm reads came in" note with a "See what's new" reveal of a compact inline section. Dismissible. No badges, no unread counts, no "missed" language. PWA: * Real PNG icons (192/512 + full-bleed maskable) rasterized from favicon.svg; manifest fixed (azure theme to match the brand, PNG icons); apple-touch-icon. * Minimal service worker: precache the app shell, always-fresh API + /a/ pages. * Gentle, dismissible install banner (beforeinstallprompt → Install; iOS → the Share → Add to Home Screen hint). Never nags. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
// Minimal, calm service worker: precache the app shell + static assets so the
|
||||
// site is installable (PWA) and the shell opens fast / works offline. Live data
|
||||
// (the API and server-rendered /a/ pages) is always fetched fresh, never staled.
|
||||
import { build, files, version } from '$service-worker';
|
||||
|
||||
const CACHE = `upbeat-${version}`;
|
||||
const SHELL = [...build, ...files];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).then(() => self.skipWaiting()));
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.keys()
|
||||
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event;
|
||||
if (request.method !== 'GET') return;
|
||||
const url = new URL(request.url);
|
||||
if (url.origin !== location.origin) return;
|
||||
|
||||
// Always-fresh, never cached: the API and the server-rendered article pages.
|
||||
if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/a/')) return;
|
||||
|
||||
// Navigations: serve the cached app shell when offline (SPA fallback).
|
||||
if (request.mode === 'navigate') {
|
||||
event.respondWith(fetch(request).catch(() => caches.match('/') || caches.match('/index.html')));
|
||||
return;
|
||||
}
|
||||
|
||||
// Static assets (JS/CSS/icons/fonts): cache-first, fall back to network.
|
||||
event.respondWith(caches.match(request).then((cached) => cached || fetch(request)));
|
||||
});
|
||||
Reference in New Issue
Block a user