fix(library): land a reading's rows as they are read, and keep the copy the reader reads offline
- scan: rows go down on a clock as well as on a full batch. The reader's folder of 26 RAWs is under one batch of 50, so nothing of it reached the screen until the reading was over — a wall that stood empty for the whole scan and then filled in one go. Frames now reach the screen every 400ms while the roll is still being read, so the count climbs and the first tiles are drawn as the frames land. - tiles: a HEIC or a RAW has its tile written the first time it is drawn, so every later visit reads the tile back instead of the file. - resume: a reading a reload cut off is offered by the next visit with a chip instead of being run behind the reader's back. - offline listing: the desk's listing is kept whole in IndexedDB under `recipescam-offline`/`reads`, past the 200 000 characters localStorage held, so an offline visit opens on the listing it kept instead of on nothing. - offline writes: a vote or a view cast with no server is held and goes out when one answers again, with a bar that says how many are waiting. - sw: the shell precaches its own bundle, stylesheet and icon, read out of the document — a worker that precached only the document answered an offline launch with a page whose JavaScript the machine did not have. - checks in docker/frontend/scripts: tree-scan-check (counts while reading, resume after a reload, tiles of a long roll), offline-read-check, offline-queue-check.
This commit is contained in:
@@ -10,11 +10,14 @@
|
||||
// hashed bundle with it, which the STATIC rule below then served cache-first
|
||||
// forever. Every navigation here now goes past the browser's own cache, so the
|
||||
// build a visitor gets is the one the server has.
|
||||
const VERSION = 'recipescam-v11';
|
||||
const VERSION = 'recipescam-v12';
|
||||
|
||||
// nginx answers all three with the same index.html (SPA fallback), so they are one
|
||||
// document under three keys: an offline navigation finds it whichever key it asks.
|
||||
const SHELL = ['/', '/app', '/library'];
|
||||
// nginx answers all three routes with the same index.html (SPA fallback), so they
|
||||
// are one document under three keys: an offline navigation finds it whichever key
|
||||
// it asks. The manifest is not that document and is precached with them for the
|
||||
// same reason — it is what makes the installed app an app, and offline it is
|
||||
// asked for on every launch.
|
||||
const SHELL = ['/', '/app', '/library', '/manifest.json'];
|
||||
|
||||
// Cache-first paths: vite hashes every filename in its own bundle, and the wasm,
|
||||
// the models and the icons never change under a given build.
|
||||
@@ -47,12 +50,27 @@ const offlineShell = async (request) =>
|
||||
(await caches.match('/')) ??
|
||||
Response.error();
|
||||
|
||||
// The files the shell is built out of, read out of the shell itself: the hashed
|
||||
// bundle, the stylesheet, the icon. A worker that precached only the document
|
||||
// would answer an offline launch with a page whose JavaScript this machine does
|
||||
// not have — and it never would, because the visit that first loads the app is
|
||||
// the visit this worker is not yet controlling, so its own STATIC rule below
|
||||
// never sees those requests. The shell is the one place their names are written.
|
||||
const shellFiles = (html) => [...html.matchAll(/(?:src|href)="(\/assets\/[^"]+)"/g)].map((m) => m[1]);
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
// Best effort, one route at a time: a deploy caught mid-flight must not leave the
|
||||
// worker uninstalled, so each route fails on its own.
|
||||
// Best effort, one file at a time: a deploy caught mid-flight must not leave the
|
||||
// worker uninstalled, so each one fails on its own.
|
||||
event.waitUntil(
|
||||
caches.open(VERSION)
|
||||
.then((cache) => Promise.all(SHELL.map((url) => cache.add(shellRequest(url)).catch(() => {}))))
|
||||
.then(async (cache) => {
|
||||
await Promise.all(SHELL.map((url) => cache.add(shellRequest(url)).catch(() => {})));
|
||||
const shell = await cache.match('/app') ?? await cache.match('/');
|
||||
if (!shell) return;
|
||||
for (const url of shellFiles(await shell.text())) {
|
||||
await cache.add(shellRequest(url)).catch(() => {});
|
||||
}
|
||||
})
|
||||
.then(() => self.skipWaiting()),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user