fix(library): a reading that never finished no longer says nought
`saveWalkedDirs` filled in a nought beside every folder a reading had listed but never opened. A reading stopped by a closed tab, and one kept to a branch, list the rest of the roll before they have been into any of it — 484 folders of one real roll read empty over tens of thousands of frames — and the record is folded back into itself, so the noughts stayed. Only a reading that met the whole roll may say nought now (`WalkedDirs.full`), and the screen drops a nought from a record that does not say so. An old record has no word to give, so its rows fall back on the catalogue instead. feat(library): search Immich from the filter row One box, one query, one node: the backend reads what is typed as a tag name when the key can see one and as a file name otherwise, and the answer is filed under the node itself. A share link searches its one album in place. The box is not a filter — it leaves the shelf, which is what the md's search permission is for. feat(ui): a shorter status line, and a wall that keeps its own row height The status line is one short 12px row at the foot of the library. A wall with fewer frames than there are columns no longer stretches its one row — and the cards' own borders with it — down to the stage.
This commit is contained in:
@@ -95,8 +95,15 @@ export interface LibraryFolder {
|
||||
// field's empty state — every album the key can see.
|
||||
albums?: string[];
|
||||
// 'album' for a node built from album names, 'share' for one built from a share
|
||||
// link: a share link *is* one album, so it has no picker and no "Người".
|
||||
kind?: 'album' | 'share';
|
||||
// link: a share link *is* one album, so it has no picker and no "Người". 'search'
|
||||
// for one built from the shelf's search box: it has no album rows at all — its
|
||||
// frames are the answer to one query, filed under the node itself, and `query`
|
||||
// is what that query was.
|
||||
kind?: 'album' | 'share' | 'search';
|
||||
// The words a 'search' node was made from. The node's name carries them too —
|
||||
// the name is the store's key — but a label the reader may rename is no place to
|
||||
// keep a query, and the sync asks with this.
|
||||
query?: string;
|
||||
}
|
||||
|
||||
export interface LibraryPhoto {
|
||||
@@ -568,12 +575,18 @@ export interface WalkedDirs {
|
||||
// Folder path in the reading's own spelling, lower-cased on the way in, to the
|
||||
// number of frames the walk met under it.
|
||||
counts: Record<string, number>;
|
||||
// True when the reading that wrote this met the whole of the roll it was
|
||||
// pointed at. A reading that was stopped, or one kept to a branch, has no word
|
||||
// on the folders it listed and never opened, and a folder it never opened is
|
||||
// not a folder it found empty — so only a record that says this one may stand
|
||||
// a nought beside a name. See `saveWalkedDirs`.
|
||||
full?: boolean;
|
||||
}
|
||||
|
||||
export async function listWalkedDirs(): Promise<WalkedDirs[]> {
|
||||
try {
|
||||
const rows = await ask<WalkedDirs[]>(DIRS, 'readonly', (s) => s.getAll());
|
||||
return rows.map((r) => ({ folder: r.folder, dirs: r.dirs ?? [], counts: r.counts ?? {} }));
|
||||
return rows.map((r) => ({ folder: r.folder, dirs: r.dirs ?? [], counts: r.counts ?? {}, full: r.full === true }));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -607,8 +620,12 @@ export async function saveWalkedDirs(
|
||||
};
|
||||
// The folders this reading was in, before another visit's are folded in.
|
||||
const walked = dirs.slice();
|
||||
// Read before the writing, not only when a branch is folded in: the record
|
||||
// carries on saying whether a reading ever met the whole roll, and the noughts
|
||||
// of the one that did are not for a branch reading to lose. See
|
||||
// `WalkedDirs.full`.
|
||||
const prev = replace ? null : ((await ask<WalkedDirs | undefined>(DIRS, 'readonly', (s) => s.get(folder))) ?? null);
|
||||
if (!replace) {
|
||||
const prev = (await ask<WalkedDirs | undefined>(DIRS, 'readonly', (s) => s.get(folder))) ?? null;
|
||||
const byId = new Map((prev?.dirs ?? []).map((d) => [d.id, d]));
|
||||
for (const d of dirs) byId.set(d.id, d);
|
||||
dirs = [...byId.values()];
|
||||
@@ -654,22 +671,31 @@ export async function saveWalkedDirs(
|
||||
// nothing under it: left out of the record, the row falls back on the
|
||||
// catalogue, and a frame taken off the disk keeps its number for good.
|
||||
const key = (rel: string) => (rel ? `${root}/${normPath(rel)}` : root).toLowerCase();
|
||||
// A folder the reading could not open is a folder it has no word on, and
|
||||
// nothing — not even nothing — is what a reading with no word on a folder
|
||||
// may write beside its name. Written in as a nought it takes the place of
|
||||
// the number the catalogue holds, and a column drawn off a drive that
|
||||
// hiccupped once reads noughts over folders full of frames until the reader
|
||||
// rescans the roll. Left out, the row falls back on the catalogue, which is
|
||||
// what the last reading that could open it found.
|
||||
const unsaid = new Set([...(unlisted ?? [])].map((r) => normPath(r).toLowerCase()));
|
||||
for (const d of walked) {
|
||||
if (unsaid.has(normPath(d.rel).toLowerCase())) continue;
|
||||
counts[key(d.rel)] ??= 0;
|
||||
// Only a reading that met the whole of the roll gets to say nought beside a
|
||||
// name. A reading kept to one folder lists the folders under it before it
|
||||
// has been into them, and one cut short lists the rest of the roll and opens
|
||||
// none of it: written in as noughts they take the place of the numbers the
|
||||
// catalogue holds, and the record is folded back into itself, so the column
|
||||
// of a roll full of frames reads empty until the reader walks it again.
|
||||
// Beyond that a folder the reading could not open is a folder it has no word
|
||||
// on, and nothing — not even nothing — is what it may write beside its name.
|
||||
// Left out, the row falls back on the catalogue, which is what the last
|
||||
// reading that could open it found. See `WalkedDirs.full`.
|
||||
if (replace) {
|
||||
const unsaid = new Set([...(unlisted ?? [])].map((r) => normPath(r).toLowerCase()));
|
||||
for (const d of walked) {
|
||||
if (unsaid.has(normPath(d.rel).toLowerCase())) continue;
|
||||
counts[key(d.rel)] ??= 0;
|
||||
}
|
||||
counts[root] ??= 0;
|
||||
}
|
||||
if (replace) counts[root] ??= 0;
|
||||
counts = fold(counts);
|
||||
}
|
||||
await ask(DIRS, 'readwrite', (s) => s.put({ folder, dirs, counts: counts ?? {} } satisfies WalkedDirs));
|
||||
await ask(
|
||||
DIRS,
|
||||
'readwrite',
|
||||
(s) => s.put({ folder, dirs, counts: counts ?? {}, full: replace || prev?.full === true } satisfies WalkedDirs)
|
||||
);
|
||||
} catch {
|
||||
// A catalogue that will not keep them is a column drawn from the catalogue, as
|
||||
// it was before.
|
||||
|
||||
Reference in New Issue
Block a user