fix(library): a reading of the cloud says when it has finished, and a window is measured off the shelf that is up
The background walk behind a cloud node read its pages after the answer the screen had already been given, and no one told the screen when the last page landed: the note went on saying a reading was running for the rest of the visit. The walk now gives its own last word, with the count it read. The strip and the wall windows were measured off the node the effect was set up on. The stage is drawn while a node is open, so a visit that loses its nodes and gets them back draws a new strip and a new wall, and a window measured off the old node — a box of nothing, a scroll always nought — never moved again for the rest of the visit. Both now read the element that is up.
This commit is contained in:
@@ -1005,7 +1005,7 @@ function Catalogue({ admin, user }: { admin: boolean; user: User | null }) {
|
||||
syncingServer.current.add(folder.name);
|
||||
setNote(null);
|
||||
try {
|
||||
const res = await syncImmich(folder, (batch) => {
|
||||
const res = await syncImmich(folder, (batch, progress) => {
|
||||
setPhotos((prev) => {
|
||||
const existing = new Set(prev.map((p) => p.id));
|
||||
const toAdd = batch.filter((p) => !existing.has(p.id));
|
||||
@@ -1024,6 +1024,13 @@ function Catalogue({ admin, user }: { admin: boolean; user: User | null }) {
|
||||
saveCachedPhotos(next);
|
||||
return next;
|
||||
});
|
||||
// The reading's own last word. The answer below is the foreground
|
||||
// window's, and the pages behind it are read after it: the note that
|
||||
// said a reading was running is spent here, when the last page of the
|
||||
// whole walk lands, rather than never.
|
||||
if (!progress.hasMore) {
|
||||
setNote(t('lib.immich.added', { n: progress.loaded, folder: folder.label ?? folder.name }));
|
||||
}
|
||||
});
|
||||
setDirs((prev) => [...prev.filter((d) => d.folder !== folder.name), ...res.dirs]);
|
||||
setNote(
|
||||
@@ -3961,8 +3968,15 @@ function useStripWindow(count: number, on: boolean): { ref: RefObject<HTMLDivEle
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const measure = () => {
|
||||
const from = Math.max(0, Math.floor(el.scrollLeft / TILE_STEP) - STRIP_KEEP);
|
||||
const to = Math.min(count, Math.ceil((el.scrollLeft + el.clientWidth) / TILE_STEP) + STRIP_KEEP);
|
||||
// The window is measured off the strip that is up, not off the one this
|
||||
// effect was set up on: the stage is drawn while a node is open, so a visit
|
||||
// that loses its nodes and gets them back draws a new strip, and a window
|
||||
// measured off the old node — a box of nothing, a scroll always nought — is
|
||||
// a window that never moves again for the rest of the visit.
|
||||
const now = ref.current;
|
||||
if (!now) return;
|
||||
const from = Math.max(0, Math.floor(now.scrollLeft / TILE_STEP) - STRIP_KEEP);
|
||||
const to = Math.min(count, Math.ceil((now.scrollLeft + now.clientWidth) / TILE_STEP) + STRIP_KEEP);
|
||||
setWin((prev) => (prev.from === from && prev.to === to ? prev : { from, to }));
|
||||
};
|
||||
measure();
|
||||
@@ -4031,13 +4045,20 @@ function useWallWindow(count: number, on: boolean): { ref: RefObject<HTMLDivElem
|
||||
setWin(next);
|
||||
};
|
||||
const measure = () => {
|
||||
// The window is measured off the wall that is up, not off the one this
|
||||
// effect was set up on: the stage is drawn while a node is open, so a visit
|
||||
// that loses its nodes and gets them back draws a new wall, and a window
|
||||
// measured off the old node — a box of nothing, a scroll always nought — is
|
||||
// a window that never moves again for the rest of the visit.
|
||||
const now = ref.current;
|
||||
if (!now) return;
|
||||
// A wall that is not standing has no box to measure: the library is
|
||||
// `display:none` while the studio is up, and a window read off it would be
|
||||
// a window read off nothing — every card reports `offsetTop` 0 and every
|
||||
// scroll 0 — which threw the shelf back to its top every time the studio
|
||||
// handed back to it. It is read on the frame it comes back up, which is a
|
||||
// box change.
|
||||
if (!el.clientHeight) return;
|
||||
if (!now.clientHeight) return;
|
||||
// And a window that is nowhere on the shelf stands nowhere: outside a shelf
|
||||
// that shrank under it — another folder, a filter, a shorter roll — or empty
|
||||
// while the shelf is not, which is the first paint, before the catalogue has
|
||||
@@ -4054,12 +4075,12 @@ function useWallWindow(count: number, on: boolean): { ref: RefObject<HTMLDivElem
|
||||
// rows, those rows were read back into the count, and a count read off the
|
||||
// run is a count the window feeds itself with. This is the same read the
|
||||
// settle effect above scrolls the wall by.
|
||||
const cols = Math.max(1, getComputedStyle(el).gridTemplateColumns.split(' ').length);
|
||||
const cols = Math.max(1, getComputedStyle(now).gridTemplateColumns.split(' ').length);
|
||||
// The run the step below is read off. A run of a single row is no run at
|
||||
// all — that is the last row of a shelf scrolled to its end — and a wall
|
||||
// that has drawn nothing is nothing to read: the state's own first
|
||||
// screenful stands until there is one.
|
||||
const cards = el.querySelectorAll<HTMLElement>(':scope > .adm-card');
|
||||
const cards = now.querySelectorAll<HTMLElement>(':scope > .adm-card');
|
||||
const rowsDrawn = Math.floor(cards.length / cols);
|
||||
if (rowsDrawn < 2) return;
|
||||
// The step from one row to the next, taken as the drawn run's own average.
|
||||
@@ -4087,8 +4108,8 @@ function useWallWindow(count: number, on: boolean): { ref: RefObject<HTMLDivElem
|
||||
const pitch = pitchRef.current;
|
||||
if (!(pitch > 0)) return;
|
||||
const rows = Math.ceil(count / cols);
|
||||
const row0 = Math.max(0, Math.floor(el.scrollTop / pitch) - WALL_ROWS);
|
||||
const row1 = Math.min(rows, Math.ceil((el.scrollTop + el.clientHeight) / pitch) + WALL_ROWS);
|
||||
const row0 = Math.max(0, Math.floor(now.scrollTop / pitch) - WALL_ROWS);
|
||||
const row1 = Math.min(rows, Math.ceil((now.scrollTop + now.clientHeight) / pitch) + WALL_ROWS);
|
||||
const from = row0 * cols;
|
||||
const to = Math.min(count, row1 * cols);
|
||||
// A spacer's own height keeps the gap the grid would have left under the
|
||||
|
||||
@@ -588,6 +588,14 @@ export async function syncImmich(
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// The reading's last word. The answer `syncImmich` gave was the
|
||||
// foreground window's, and the pages behind it are read after it: without
|
||||
// this the screen goes on saying a reading is running for the rest of the
|
||||
// visit, and a reading nobody is doing is the note the reader reads as a
|
||||
// reading that never stops. A walk that ended at a page the server would
|
||||
// not answer says the same thing — what the catalogue was given is what it
|
||||
// was given — and the place stays where it stopped either way.
|
||||
onBatch?.([], { loaded: rootCount, hasMore: false });
|
||||
walking.delete(root);
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user