fix(library): resolve main-thread UI freeze and process kill data loss via OPFS payload capping, yield throttling, and UI batching
This commit is contained in:
@@ -469,19 +469,32 @@ export function Library() {
|
||||
fresh = 0;
|
||||
}
|
||||
};
|
||||
// The reading hands its frames over the moment their batch is stored, so the
|
||||
// strip is the roll arriving rather than the roll being found again — a read
|
||||
// of the whole catalogue costs every thumbnail in it, and on a folder of RAW
|
||||
// that read beside a quarter of a gigabyte of decoder is what took the tab.
|
||||
let pendingRows: LibraryPhoto[] = [];
|
||||
let updateTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const flushPending = () => {
|
||||
if (pendingRows.length > 0) {
|
||||
const toAdd = pendingRows;
|
||||
pendingRows = [];
|
||||
setPhotos((prev) => [...prev, ...toAdd]);
|
||||
}
|
||||
updateTimer = null;
|
||||
};
|
||||
|
||||
watchRows((rows) => {
|
||||
fresh += rows.length;
|
||||
setPhotos((prev) => [...prev, ...rows]);
|
||||
pendingRows.push(...rows);
|
||||
if (!updateTimer) {
|
||||
updateTimer = setTimeout(flushPending, 500);
|
||||
}
|
||||
});
|
||||
const unwatch = watchScan(watching);
|
||||
// A scan already in flight when this screen opens is picked up where it is:
|
||||
// the reader who left for the studio comes back to the same roll.
|
||||
watching();
|
||||
return () => {
|
||||
if (updateTimer) clearTimeout(updateTimer);
|
||||
flushPending();
|
||||
unwatch();
|
||||
watchRows(null);
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ const LANES = 4;
|
||||
// How often the walk's position is written down, in the same seconds as the
|
||||
// catalogue is read back on: the position is the whole of what the walk has found
|
||||
// and not read, and writing it is a string of a few megabytes on a long roll.
|
||||
const WALK_MS = 2000;
|
||||
const WALK_MS = 5000;
|
||||
// How often the screen is told how far the reading has come: the column is
|
||||
// rebuilt out of the count, and a rebuild a frame is a reading that spends its
|
||||
// time drawing itself. Five times a second is a counter that moves to the eye
|
||||
@@ -834,11 +834,12 @@ export async function scanFolder(
|
||||
if (at - wroteAt < WALK_MS) return;
|
||||
wroteAt = at;
|
||||
const held = [...inHand, ...batch.map(pathOf)];
|
||||
const savedEntries = entries.slice(0, 500).map((e) => e.rel);
|
||||
await saveWalk(
|
||||
folder.name,
|
||||
[...tree.walked],
|
||||
tree.pending.map((p) => p.rel),
|
||||
[...held, ...entries.map((e) => e.rel)],
|
||||
[...held, ...savedEntries],
|
||||
held.length,
|
||||
progress
|
||||
);
|
||||
@@ -962,11 +963,12 @@ export async function scanFolder(
|
||||
|
||||
// Micro-yield to the browser main thread event loop so scanning never blocks UI rendering or user interactions
|
||||
processed++;
|
||||
if (processed % 12 === 0) {
|
||||
if (processed % 3 === 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
}
|
||||
}
|
||||
await Promise.all(lanes);
|
||||
await flush();
|
||||
};
|
||||
|
||||
// How many of the frames in hand were counted by the reading that handed them
|
||||
|
||||
Reference in New Issue
Block a user