fix(library): fix photo count live update and optimize scan speed

This commit is contained in:
2026-10-07 21:16:10 +07:00
parent 8dd8ba9bc7
commit aa981e4e65
2 changed files with 56 additions and 44 deletions
+22 -12
View File
@@ -943,25 +943,30 @@ function Catalogue() {
// lies under it, with nothing at all to say about the rest of the roll: its
// numbers drawn over the column would count a roll down to a branch of itself.
// The column falls back on the catalogue until the reading is through.
const read = live && !live.progress.from ? live.progress.counts : null;
const read = live ? live.progress.counts : null;
// Memoize photo count map derived from photos and dirs array separately so that
// live scan progress ticks (every 200ms) do not re-scan tens of thousands of photos.
const baseTreeInfo = useMemo(() => {
const hits = new Map<string, number>();
const foldersUnder = new Map<string, Set<string>>();
const add = (folder: string, rel: string, counted: boolean) => {
const set = foldersUnder.get(folder) ?? new Set<string>();
foldersUnder.set(folder, set);
const normF = (folder || '').replace(/\\/g, '/');
const normRel = (rel || '').replace(/\\/g, '/');
const set = foldersUnder.get(normF) ?? new Set<string>();
foldersUnder.set(normF, set);
let path = '';
for (const part of rel.split('/')) {
for (const part of normRel.split('/')) {
if (!part) continue;
path = path ? `${path}/${part}` : part;
set.add(path);
if (counted) hits.set(`${folder}/${path}`, (hits.get(`${folder}/${path}`) ?? 0) + 1);
if (counted) hits.set(`${normF}/${path}`, (hits.get(`${normF}/${path}`) ?? 0) + 1);
}
};
for (const photo of photos) {
hits.set(photo.folder, (hits.get(photo.folder) ?? 0) + 1);
if (photo.dir) add(photo.folder, photo.dir, true);
const normF = (photo.folder || '').replace(/\\/g, '/');
const normDir = (photo.dir || '').replace(/\\/g, '/');
hits.set(normF, (hits.get(normF) ?? 0) + 1);
if (normDir) add(normF, normDir, true);
}
for (const dir of dirs) add(dir.folder, dir.rel, false);
return { hits, foldersUnder };
@@ -969,19 +974,24 @@ function Catalogue() {
const nodes = useMemo<Node[]>(() => {
const { hits, foldersUnder } = baseTreeInfo;
const counted = (key: string, hit: number | undefined) => Math.max(hit ?? 0, read?.[key] ?? 0);
const counted = (key: string, hit: number | undefined) => {
const normKey = (key || '').replace(/\\/g, '/');
return Math.max(hit ?? 0, read?.[normKey] ?? read?.[key] ?? 0);
};
const out: Node[] = [];
for (const folder of folders) {
out.push({ key: folder.name, label: folder.label || folder.name, depth: 0, count: counted(folder.name, hits.get(folder.name)) });
const normF = (folder.name || '').replace(/\\/g, '/');
out.push({ key: normF, label: folder.label || folder.name, depth: 0, count: counted(normF, hits.get(normF)) });
// Path order puts a folder before its own children, which is the order the
// indented rows have to come in.
for (const dir of [...(foldersUnder.get(folder.name) ?? [])].sort((a, b) => a.localeCompare(b))) {
for (const dir of [...(foldersUnder.get(normF) ?? [])].sort((a, b) => a.localeCompare(b))) {
const parts = dir.split('/');
const key = `${normF}/${dir}`;
out.push({
key: `${folder.name}/${dir}`,
key,
label: parts[parts.length - 1],
depth: parts.length,
count: counted(`${folder.name}/${dir}`, hits.get(`${folder.name}/${dir}`)),
count: counted(key, hits.get(key)),
});
}
}