diff --git a/docker/frontend/scripts/legacy-dup-check.mjs b/docker/frontend/scripts/legacy-dup-check.mjs index 187025e..d9e2662 100644 --- a/docker/frontend/scripts/legacy-dup-check.mjs +++ b/docker/frontend/scripts/legacy-dup-check.mjs @@ -218,6 +218,14 @@ await page.waitForSelector('[data-key^="lib-node-"]', { timeout: 60_000 }); const before = await rows(); step('the doubled catalogue is read', before.photos.length === names.length * 2, `${before.photos.length} rows: ${before.photos.map((p) => p.id).join(' | ')}`); +// The wall, before any repair: the frames of the drive are drawn once each, even +// while the catalogue holds two rows for every one of them. +await page.locator('[data-key^="lib-node-"]').first().click(); +await page.click('[data-key="lib-view-grid"]'); +await page.waitForTimeout(3000); +const tiles = await page.evaluate(() => document.querySelectorAll('.lib-tile, .mob-lib-thumb-cell').length); +step('the wall draws each frame once', tiles === names.length, `tiles=${tiles}`); + // UPDATE on the drive row. await page.locator('[data-key^="lib-node-"]').first().click({ button: 'right' }); await page.waitForSelector('[data-key^="lib-update-"]', { timeout: 20_000 }); diff --git a/docker/frontend/src/engine/library.ts b/docker/frontend/src/engine/library.ts index a397fdb..15292ef 100644 --- a/docker/frontend/src/engine/library.ts +++ b/docker/frontend/src/engine/library.ts @@ -2541,7 +2541,7 @@ export async function readPhotos( // being filed twice and `prunePhotos` to drop against the spelling that // stands. See both. const raw = folder !== undefined && folder !== root ? folder : null; - const rows: LibraryPhoto[] = []; + let rows: LibraryPhoto[] = []; let emittedFirst = false; const sweep = async (base: string): Promise => { @@ -2578,6 +2578,25 @@ export async function readPhotos( await sweep(root ?? ''); if (raw) await sweep(raw); + // A catalogue that holds a frame twice — under the id this build computes and + // under the spelling a build before it filed it under — is one frame the + // reader is drawn twice, and this is the read that hands the wall both. The + // two are one path once `normPath` has had them, and what the reader is handed + // is the row that is filed under the path it spells: the other is left where + // it is, and the next reading of that disk drops it. See `prunePhotos`. The + // reading of one folder keeps both, because that is the reading that has the + // frame in hand and the one the drop is made from. + if (folder === undefined) { + const shown = new Set(rows.filter((p) => p.id === normPath(p.id)).map((p) => p.id)); + const seen = new Set(); + rows = rows.filter((p) => { + const key = normPath(p.id); + if (shown.has(key)) return p.id === key; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); + } rows.sort((a, b) => b.taken - a.taken); if (!folder) { cached = rows;