feat(11-02): update config-page with per-library progress display and queue-aware cancel dialog
- Add libraryId, libraryName, queuedCount to ScanProgress interface - Replace CancelScan import with CancelCurrentScan, CancelAllScans - Add ScanAllLibraries import and Scan All Libraries button - Show library name in progress label (Scanning: [Library Name]) - Show queue count below progress bar when libraries queued - Cancel dialog shows scope choice (Cancel This Library / Cancel All) when queue > 0 - Subscribe to LibraryScanQueued and LibraryScanQueueDrained events - Track scanQueuedCount state for queue-aware UI behavior
This commit is contained in:
@@ -5,7 +5,9 @@ import { EventsOn } from '@runtime/runtime';
|
|||||||
import {
|
import {
|
||||||
Scan,
|
Scan,
|
||||||
FullRescan,
|
FullRescan,
|
||||||
CancelScan,
|
CancelCurrentScan,
|
||||||
|
CancelAllScans,
|
||||||
|
ScanAllLibraries,
|
||||||
PauseScan,
|
PauseScan,
|
||||||
ResumeScan,
|
ResumeScan,
|
||||||
} from '@go/library/Library';
|
} from '@go/library/Library';
|
||||||
@@ -52,6 +54,9 @@ interface ScanProgress {
|
|||||||
added: number;
|
added: number;
|
||||||
skipped: number;
|
skipped: number;
|
||||||
updated: number;
|
updated: number;
|
||||||
|
libraryId: number;
|
||||||
|
libraryName: string;
|
||||||
|
queuedCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScanMetrics {
|
interface ScanMetrics {
|
||||||
@@ -345,6 +350,7 @@ export class ConfigPage extends LitElement {
|
|||||||
@state() private scanPaused = false;
|
@state() private scanPaused = false;
|
||||||
@state() private showCancelDialog = false;
|
@state() private showCancelDialog = false;
|
||||||
@state() private cancelMetrics: { added: number } | null = null;
|
@state() private cancelMetrics: { added: number } | null = null;
|
||||||
|
@state() private scanQueuedCount = 0;
|
||||||
@state() private shortcutConflict: {
|
@state() private shortcutConflict: {
|
||||||
newAction: string;
|
newAction: string;
|
||||||
newKey: string;
|
newKey: string;
|
||||||
@@ -357,6 +363,8 @@ export class ConfigPage extends LitElement {
|
|||||||
private cancelScanPaused?: () => void;
|
private cancelScanPaused?: () => void;
|
||||||
private cancelScanResumed?: () => void;
|
private cancelScanResumed?: () => void;
|
||||||
private cancelScanCancelled?: () => void;
|
private cancelScanCancelled?: () => void;
|
||||||
|
private cancelScanQueued?: () => void;
|
||||||
|
private cancelScanQueueDrained?: () => void;
|
||||||
|
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
:host {
|
:host {
|
||||||
@@ -901,6 +909,14 @@ export class ConfigPage extends LitElement {
|
|||||||
Events.LibraryScanCancelled,
|
Events.LibraryScanCancelled,
|
||||||
this.handleScanCancelled,
|
this.handleScanCancelled,
|
||||||
);
|
);
|
||||||
|
this.cancelScanQueued = EventsOn(
|
||||||
|
Events.LibraryScanQueued,
|
||||||
|
this.handleScanQueued,
|
||||||
|
);
|
||||||
|
this.cancelScanQueueDrained = EventsOn(
|
||||||
|
Events.LibraryScanQueueDrained,
|
||||||
|
this.handleScanQueueDrained,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override disconnectedCallback(): void {
|
override disconnectedCallback(): void {
|
||||||
@@ -911,6 +927,8 @@ export class ConfigPage extends LitElement {
|
|||||||
this.cancelScanPaused?.();
|
this.cancelScanPaused?.();
|
||||||
this.cancelScanResumed?.();
|
this.cancelScanResumed?.();
|
||||||
this.cancelScanCancelled?.();
|
this.cancelScanCancelled?.();
|
||||||
|
this.cancelScanQueued?.();
|
||||||
|
this.cancelScanQueueDrained?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadLibraryConfig(): Promise<void> {
|
private async loadLibraryConfig(): Promise<void> {
|
||||||
@@ -950,15 +968,26 @@ export class ConfigPage extends LitElement {
|
|||||||
): void => {
|
): void => {
|
||||||
if (progress) {
|
if (progress) {
|
||||||
this.scanProgress = progress;
|
this.scanProgress = progress;
|
||||||
|
this.scanQueuedCount =
|
||||||
|
progress.queuedCount ?? 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleScanComplete = (
|
private handleScanComplete = (
|
||||||
metrics?: ScanMetrics,
|
metrics?: ScanMetrics,
|
||||||
): void => {
|
): void => {
|
||||||
|
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.scanning = false;
|
||||||
this.scanPaused = false;
|
this.scanPaused = false;
|
||||||
this.scanProgress = null;
|
|
||||||
this.statusMessage = 'Scan complete.';
|
this.statusMessage = 'Scan complete.';
|
||||||
|
|
||||||
if (metrics) {
|
if (metrics) {
|
||||||
@@ -966,6 +995,16 @@ export class ConfigPage extends LitElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleScanQueued = (): void => {
|
||||||
|
this.scanQueuedCount++;
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleScanQueueDrained = (): void => {
|
||||||
|
this.scanQueuedCount = 0;
|
||||||
|
this.scanning = false;
|
||||||
|
this.scanPaused = false;
|
||||||
|
};
|
||||||
|
|
||||||
private handleScanPaused = (): void => {
|
private handleScanPaused = (): void => {
|
||||||
this.scanPaused = true;
|
this.scanPaused = true;
|
||||||
};
|
};
|
||||||
@@ -1010,17 +1049,24 @@ export class ConfigPage extends LitElement {
|
|||||||
private handleCancelKeep = (): void => {
|
private handleCancelKeep = (): void => {
|
||||||
this.showCancelDialog = false;
|
this.showCancelDialog = false;
|
||||||
this.cancelMetrics = null;
|
this.cancelMetrics = null;
|
||||||
CancelScan();
|
CancelCurrentScan();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleCancelDiscard = (): void => {
|
private handleCancelDiscard = (): void => {
|
||||||
this.showCancelDialog = false;
|
this.showCancelDialog = false;
|
||||||
this.cancelMetrics = null;
|
this.cancelMetrics = null;
|
||||||
CancelScan();
|
CancelCurrentScan();
|
||||||
this.statusMessage =
|
this.statusMessage =
|
||||||
'Scan cancelled. Partial results discarded \u2014 run Full Rescan for a clean library.';
|
'Scan cancelled. Partial results discarded \u2014 run Full Rescan for a clean library.';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleCancelAll = (): void => {
|
||||||
|
this.showCancelDialog = false;
|
||||||
|
this.cancelMetrics = null;
|
||||||
|
CancelAllScans();
|
||||||
|
this.statusMessage = 'All scanning cancelled.';
|
||||||
|
};
|
||||||
|
|
||||||
private handleCancelDialogDismiss = (): void => {
|
private handleCancelDialogDismiss = (): void => {
|
||||||
this.showCancelDialog = false;
|
this.showCancelDialog = false;
|
||||||
this.cancelMetrics = null;
|
this.cancelMetrics = null;
|
||||||
@@ -1073,6 +1119,16 @@ export class ConfigPage extends LitElement {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleScanAll = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await ScanAllLibraries();
|
||||||
|
} catch (err) {
|
||||||
|
this.statusMessage =
|
||||||
|
'Scan all libraries completed with errors.';
|
||||||
|
this.scanErrors = String(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private handleSoftScan = async (): Promise<void> => {
|
private handleSoftScan = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await Scan();
|
await Scan();
|
||||||
@@ -1938,6 +1994,12 @@ export class ConfigPage extends LitElement {
|
|||||||
>
|
>
|
||||||
Full Rescan
|
Full Rescan
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn-primary"
|
||||||
|
@click=${this.handleScanAll}
|
||||||
|
>
|
||||||
|
Scan All Libraries
|
||||||
|
</button>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1985,45 +2047,98 @@ export class ConfigPage extends LitElement {
|
|||||||
class="cancel-dialog"
|
class="cancel-dialog"
|
||||||
@click=${(e: Event) => e.stopPropagation()}
|
@click=${(e: Event) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div
|
${this.scanQueuedCount > 0
|
||||||
class="cancel-dialog-title"
|
? html`
|
||||||
>
|
<div
|
||||||
Cancel Scan
|
class="cancel-dialog-title"
|
||||||
</div>
|
>
|
||||||
<div
|
Cancel Scanning
|
||||||
class="cancel-dialog-message"
|
</div>
|
||||||
>
|
<div
|
||||||
${this.cancelMetrics
|
class="cancel-dialog-message"
|
||||||
?.added
|
>
|
||||||
? `Keep ${this.cancelMetrics.added} tracks found so far, or discard?`
|
A scan is in
|
||||||
: 'Cancel the current scan?'}
|
progress with
|
||||||
</div>
|
${this
|
||||||
<div
|
.scanQueuedCount}
|
||||||
class="cancel-dialog-actions"
|
${this
|
||||||
>
|
.scanQueuedCount ===
|
||||||
<button
|
1
|
||||||
class="btn-primary"
|
? 'library'
|
||||||
@click=${this.handleCancelKeep}
|
: 'libraries'}
|
||||||
>
|
still queued.
|
||||||
${this
|
</div>
|
||||||
.cancelMetrics
|
<div
|
||||||
?.added
|
class="cancel-dialog-actions"
|
||||||
? `Keep ${this.cancelMetrics.added} tracks`
|
>
|
||||||
: 'Cancel Scan'}
|
<button
|
||||||
</button>
|
class="btn-warning"
|
||||||
<button
|
@click=${this.handleCancelKeep}
|
||||||
class="btn-danger"
|
>
|
||||||
@click=${this.handleCancelDiscard}
|
Cancel
|
||||||
>
|
This
|
||||||
Discard
|
Library
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn-ghost"
|
class="btn-danger"
|
||||||
@click=${this.handleCancelDialogDismiss}
|
@click=${this.handleCancelAll}
|
||||||
>
|
>
|
||||||
Continue Scanning
|
Cancel
|
||||||
</button>
|
All
|
||||||
</div>
|
Scanning
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn-ghost"
|
||||||
|
@click=${this.handleCancelDialogDismiss}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
Scanning
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div
|
||||||
|
class="cancel-dialog-title"
|
||||||
|
>
|
||||||
|
Cancel Scan
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cancel-dialog-message"
|
||||||
|
>
|
||||||
|
${this
|
||||||
|
.cancelMetrics
|
||||||
|
?.added
|
||||||
|
? `Keep ${this.cancelMetrics.added} tracks found so far, or discard?`
|
||||||
|
: 'Cancel the current scan?'}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cancel-dialog-actions"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-primary"
|
||||||
|
@click=${this.handleCancelKeep}
|
||||||
|
>
|
||||||
|
${this
|
||||||
|
.cancelMetrics
|
||||||
|
?.added
|
||||||
|
? `Keep ${this.cancelMetrics.added} tracks`
|
||||||
|
: 'Cancel Scan'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn-danger"
|
||||||
|
@click=${this.handleCancelDiscard}
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn-ghost"
|
||||||
|
@click=${this.handleCancelDialogDismiss}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
Scanning
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -2055,11 +2170,25 @@ export class ConfigPage extends LitElement {
|
|||||||
|
|
||||||
if (!p) return nothing;
|
if (!p) return nothing;
|
||||||
|
|
||||||
|
const libraryPrefix = p.libraryName
|
||||||
|
? `Scanning: ${p.libraryName}`
|
||||||
|
: '';
|
||||||
|
|
||||||
if (p.phase === 'counting') {
|
if (p.phase === 'counting') {
|
||||||
return html`
|
return html`
|
||||||
<div class="progress-phase">
|
<div class="progress-phase">
|
||||||
|
${libraryPrefix
|
||||||
|
? html`<strong>${libraryPrefix}</strong> \u2014 `
|
||||||
|
: ''}
|
||||||
Counting files\u2026
|
Counting files\u2026
|
||||||
</div>
|
</div>
|
||||||
|
${p.queuedCount > 0
|
||||||
|
? html`<div class="progress-detail">
|
||||||
|
${p.queuedCount}
|
||||||
|
${p.queuedCount === 1 ? 'library' : 'libraries'}
|
||||||
|
queued
|
||||||
|
</div>`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2079,7 +2208,11 @@ export class ConfigPage extends LitElement {
|
|||||||
thumbnails: 'Generating thumbnails',
|
thumbnails: 'Generating thumbnails',
|
||||||
};
|
};
|
||||||
|
|
||||||
const label = phaseLabel[p.phase] ?? 'Scanning';
|
const baseLabel =
|
||||||
|
phaseLabel[p.phase] ?? 'Scanning';
|
||||||
|
const label = p.libraryName
|
||||||
|
? `${baseLabel}: ${p.libraryName}`
|
||||||
|
: baseLabel;
|
||||||
|
|
||||||
// Build detail string: "1,247 / 2,013 files (891 new, 23 updated, 356 skipped)"
|
// Build detail string: "1,247 / 2,013 files (891 new, 23 updated, 356 skipped)"
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
@@ -2121,6 +2254,13 @@ export class ConfigPage extends LitElement {
|
|||||||
style="width: ${percent}%"
|
style="width: ${percent}%"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
${p.queuedCount > 0
|
||||||
|
? html`<div class="progress-detail">
|
||||||
|
${p.queuedCount}
|
||||||
|
${p.queuedCount === 1 ? 'library' : 'libraries'}
|
||||||
|
queued
|
||||||
|
</div>`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user