Paint saved playlists before the launch sync and swap a background-downloaded update instantly
This commit is contained in:
27
CLAUDE.md
27
CLAUDE.md
@@ -309,6 +309,20 @@ Local DB file: `server/data/ytplayer.db` (gitignored). `BUILD_TAG` is computed f
|
||||
worker and the SW_UPDATE_AVAILABLE broadcast are only *prompts to re-check*.
|
||||
This loop was "fixed" three times by chasing individual triggers — don't add
|
||||
a trigger that calls `showUpdateBanner()` directly.
|
||||
- **Background download + instant swap (added 2026-09-22).** A new worker
|
||||
precaches the whole shell during `install`, so by the time the user presses
|
||||
"Refresh UI" the build is usually already on disk. `applyUpdate` asks the
|
||||
**waiting** worker `CACHE_STATUS` over a `MessageChannel`; when it answers
|
||||
`ready: true` (it re-checks every SHELL url, since a cache can be evicted)
|
||||
the download is skipped entirely and the swap is immediate.
|
||||
Two rules keep this safe: the probe is **opt-in** (`askStatus`) — never
|
||||
implicit, because a probe awaiting a reply hangs forever if the caller
|
||||
injects a `setTimeout` that never fires (the existing tests do exactly
|
||||
that) — and *any* doubt (no reply, timeout, thrown error, `ready: false`)
|
||||
falls back to the all-or-nothing download below, which is still what
|
||||
guarantees correctness. `maybeShowUpdateBanner` also calls `prefetchUpdate()`
|
||||
→ `registration.update()` so the download starts the moment a new build is
|
||||
seen rather than when the user clicks.
|
||||
- **Refresh UI** (`frontend/sw-update.js`): `refreshShellInPlace()` re-downloads
|
||||
every file the shell caches hold — cache-busted (`?__ytpfresh=`) so even an
|
||||
OLD worker's cache-first handler can't answer from its cache, 3 tries per file,
|
||||
@@ -361,6 +375,19 @@ Local DB file: `server/data/ytplayer.db` (gitignored). `BUILD_TAG` is computed f
|
||||
the fetch resolves to `null` offline and `respondWith(null)` throws. It ends
|
||||
with `|| Response.error()`.
|
||||
|
||||
## First paint vs. the network (launch)
|
||||
`boot()` used to `await` the profile pull and the shared-playlist inbox before
|
||||
the first `render()`, so on a slow link the sidebar stayed empty for as long as
|
||||
the network took. Everything the device knows is already in localStorage, so it
|
||||
now paints immediately and reconciles afterwards:
|
||||
- `?list=` / `?profile=` share links still run **before** the first paint — they
|
||||
*replace* the synced slice, so painting first would flash the old playlists
|
||||
and swap them out. They are skipped entirely when the parameter is absent.
|
||||
- `syncOnLaunch()` then runs the network pass with a spinner (`#syncSpinner`,
|
||||
beside the Playlists header, `setSyncing()` is counted so the last finisher
|
||||
clears it) and re-renders **only if** `playlistFingerprint()` changed — a
|
||||
needless render would drop the sidebar's scroll position.
|
||||
|
||||
## Data model quirks
|
||||
- Client state persists in localStorage key **`_ytpdata`** and syncs (debounced
|
||||
400 ms) to `POST /api/user/sync`, keyed by a browser fingerprint.
|
||||
|
||||
Reference in New Issue
Block a user