feat(library): ask the origin to keep the tiles, so the visit after opens painted
This commit is contained in:
@@ -8,6 +8,7 @@ import { readGps, readSpecs, specsLine } from './engine/imageOps';
|
||||
import { isRawName, isTiffName, rawThumbnail, tiffDevelop } from './engine/rawDevelop';
|
||||
import { heicToJpeg, isHeicName } from './engine/heicDevelop';
|
||||
import {
|
||||
askPersistence,
|
||||
breathe,
|
||||
bySourceThenName,
|
||||
cachedFolders,
|
||||
@@ -563,6 +564,12 @@ function Catalogue({ admin, user }: { admin: boolean; user: User | null }) {
|
||||
return () => onLibraryBlocked(null);
|
||||
}, []);
|
||||
|
||||
// The tiles this screen spends its first seconds making are the ones the visit
|
||||
// after reads: the origin is asked to keep them (see `askPersistence`).
|
||||
useEffect(() => {
|
||||
askPersistence();
|
||||
}, []);
|
||||
|
||||
// The folder the catalogue is copied into, and whether the browser is still
|
||||
// letting this page write to it — which it stops doing when the tab closes, so
|
||||
// the row below says so rather than letting a write fail quietly. The ref is
|
||||
|
||||
@@ -2423,6 +2423,10 @@ async function tileFrom(photo: LibraryPhoto): Promise<Blob | null> {
|
||||
// is drawn as it is rather than written to the store whole.
|
||||
const made = await makeTile(file);
|
||||
blob = made ?? file;
|
||||
// Written before the tile is handed back. Held back (lightcraft's
|
||||
// `put_deferred`) it measures the same — the decode is what the tile waits
|
||||
// for, not the transaction — so the await stays and the tile is kept for
|
||||
// certain by the time it is drawn.
|
||||
if (made) await writeThumb(photo.id, made, false);
|
||||
}
|
||||
return blob;
|
||||
@@ -2775,6 +2779,43 @@ export async function loadEdit(id: string): Promise<LibraryEdit | null> {
|
||||
}
|
||||
}
|
||||
|
||||
// The tiles are the whole of what makes the library open painted: a frame whose
|
||||
// tile is in the store is drawn off one read, and a frame whose tile the browser
|
||||
// threw away is a decode, a draw and an encode paid again the first time it is
|
||||
// looked at. A store the browser is free to evict under pressure is a library
|
||||
// that opens grey and mints every tile a second time — the one thing a visit
|
||||
// after a visit cannot pay for. So the origin is asked to be persistent, once,
|
||||
// and the ask is a promise the browser may refuse: Safari grants it only off a
|
||||
// gesture, and there is none behind a page that has just opened itself, so a
|
||||
// refusal is asked again the first time the reader touches the screen. Either
|
||||
// way the visit goes on — an evicted tile is a tile made again, which is what
|
||||
// this whole screen is for.
|
||||
let persistenceAsked = false;
|
||||
|
||||
export function askPersistence(): void {
|
||||
const storage = typeof navigator === 'undefined' ? undefined : navigator.storage;
|
||||
if (persistenceAsked || !storage?.persist) return;
|
||||
persistenceAsked = true;
|
||||
const ask = async (): Promise<boolean> => {
|
||||
const est = await storage.estimate?.();
|
||||
const held = est?.usage && est.quota ? ` (${Math.round(est.usage / 1e6)} MB of ${Math.round(est.quota / 1e6)} MB)` : '';
|
||||
if (await storage.persisted?.()) {
|
||||
console.info(`library: tiles are persistent${held}`);
|
||||
return true;
|
||||
}
|
||||
const granted = await storage.persist();
|
||||
if (granted) console.info(`library: tiles are persistent${held}`);
|
||||
else console.info(`library: tiles are not persistent — the browser may drop them under pressure${held}`);
|
||||
return granted;
|
||||
};
|
||||
void ask().then((granted) => {
|
||||
if (granted) return;
|
||||
// A gesture is the one thing Safari's ask is waiting for; one is enough.
|
||||
const again = () => void storage.persist();
|
||||
window.addEventListener('pointerdown', again, { once: true });
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
export async function listEditedIds(): Promise<Set<string>> {
|
||||
try {
|
||||
const rows = await ask<LibraryEdit[]>(EDITS, 'readonly', (s) => s.getAll());
|
||||
|
||||
Reference in New Issue
Block a user