feat(11-02): update library-manager with per-library progress and Scan All button

- Add libraryId, libraryName, queuedCount to ScanProgress interface
- Show library name in progress label (Scanning: [Library Name])
- Show queue count below progress bar when libraries queued
- Add Scan All Libraries button (calls ScanAllLibraries binding)
- Subscribe to LibraryScanQueued/LibraryScanQueueDrained events
- Scan state properly tracks queue draining (doesn't reset early)
This commit is contained in:
2026-03-09 16:11:23 -04:00
parent d01591d6cc
commit d61f122b56
@@ -1,7 +1,11 @@
import { LitElement, html, css, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { EventsOn } from '@runtime/runtime';
import { Scan, FullRescan } from '@go/library/Library';
import {
Scan,
FullRescan,
ScanAllLibraries,
} from '@go/library/Library';
import {
GetLibraryDirectory,
SetLibraryDirectory,
@@ -26,6 +30,9 @@ interface ScanProgress {
added: number;
skipped: number;
updated: number;
libraryId: number;
libraryName: string;
queuedCount: number;
}
interface ScanMetrics {
@@ -247,9 +254,12 @@ export class LibraryManager extends LitElement {
@state() private errorsCopied = false;
@state() private scanErrors = '';
@state() private concurrencyMode = 'auto';
@state() private scanQueuedCount = 0;
private cancelScanStarted?: () => void;
private cancelScanProgress?: () => void;
private cancelScanComplete?: () => void;
private cancelScanQueued?: () => void;
private cancelScanQueueDrained?: () => void;
static override styles = css`
:host {
@@ -635,6 +645,14 @@ export class LibraryManager extends LitElement {
Events.LibraryScanComplete,
this.handleScanComplete,
);
this.cancelScanQueued = EventsOn(
Events.LibraryScanQueued,
this.handleScanQueued,
);
this.cancelScanQueueDrained = EventsOn(
Events.LibraryScanQueueDrained,
this.handleScanQueueDrained,
);
}
override disconnectedCallback(): void {
@@ -642,6 +660,8 @@ export class LibraryManager extends LitElement {
this.cancelScanStarted?.();
this.cancelScanProgress?.();
this.cancelScanComplete?.();
this.cancelScanQueued?.();
this.cancelScanQueueDrained?.();
}
private async loadCurrentDirectory(): Promise<void> {
@@ -704,14 +724,25 @@ export class LibraryManager extends LitElement {
): void => {
if (progress) {
this.scanProgress = progress;
this.scanQueuedCount =
progress.queuedCount ?? 0;
}
};
private handleScanComplete = (
metrics?: ScanMetrics,
): void => {
this.scanning = false;
this.scanProgress = null;
// If queue still has entries, don't fully reset — next scan will fire ScanStarted
if (this.scanQueuedCount > 0) {
if (metrics) {
this.metrics = metrics;
}
return;
}
this.scanning = false;
this.statusMessage = 'Scan complete.';
if (metrics) {
@@ -719,6 +750,15 @@ export class LibraryManager extends LitElement {
}
};
private handleScanQueued = (): void => {
this.scanQueuedCount++;
};
private handleScanQueueDrained = (): void => {
this.scanQueuedCount = 0;
this.scanning = false;
};
private handleSelectDirectory =
async (): Promise<void> => {
try {
@@ -756,6 +796,20 @@ export class LibraryManager extends LitElement {
}
};
private handleScanAll = async (): Promise<void> => {
try {
await ScanAllLibraries();
} catch (err) {
this.statusMessage =
'Scan all libraries completed with errors.';
this.scanErrors = String(err);
console.error(
'Scan all libraries failed:',
err,
);
}
};
private handleSoftScan = async (): Promise<void> => {
try {
await Scan();
@@ -874,11 +928,25 @@ export class LibraryManager extends LitElement {
if (!p) return nothing;
const libraryPrefix = p.libraryName
? `Scanning: ${p.libraryName}`
: '';
if (p.phase === 'counting') {
return html`
<div class="progress-phase">
${libraryPrefix
? html`<strong>${libraryPrefix}</strong> \u2014 `
: ''}
Counting files\u2026
</div>
${p.queuedCount > 0
? html`<div class="progress-detail">
${p.queuedCount}
${p.queuedCount === 1 ? 'library' : 'libraries'}
queued
</div>`
: nothing}
`;
}
@@ -898,7 +966,11 @@ export class LibraryManager extends LitElement {
thumbnails: 'Generating thumbnails',
};
const label = phaseLabel[p.phase] ?? 'Scanning';
const baseLabel =
phaseLabel[p.phase] ?? 'Scanning';
const label = p.libraryName
? `${baseLabel}: ${p.libraryName}`
: baseLabel;
const parts: string[] = [];
@@ -939,6 +1011,13 @@ export class LibraryManager extends LitElement {
style="width: ${percent}%"
></div>
</div>
${p.queuedCount > 0
? html`<div class="progress-detail">
${p.queuedCount}
${p.queuedCount === 1 ? 'library' : 'libraries'}
queued
</div>`
: nothing}
`;
}
@@ -1208,6 +1287,15 @@ export class LibraryManager extends LitElement {
? 'Scanning...'
: 'Full Rescan'}
</button>
<button
class="btn-primary"
?disabled=${this.scanning}
@click=${this.handleScanAll}
>
${this.scanning
? 'Scanning...'
: 'Scan All Libraries'}
</button>
</div>
</div>