Finished-transfers setting + transfer-list header with Clear finished
- Options popover: 'Finished transfers' Auto-clear | Keep (default auto-clear, the previous behavior); persisted like the rest - Transfer list header: 'N active · M finished' + Clear finished button (dismisses completed/failed/cancelled only, never running/queued) - Failed rows now name the file that failed alongside the error message Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import type { FileEntry, TransferSnapshot } from '@shared/types';
|
||||
import { DialogsService } from './dialogs.service';
|
||||
import { PanelsService, type Side } from './panels.service';
|
||||
import { RpcService } from './rpc.service';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { ToastService } from './toast.service';
|
||||
|
||||
const AUTO_DISMISS_COMPLETED_MS = 5000;
|
||||
@@ -16,6 +17,7 @@ export class TransfersService {
|
||||
private readonly panels = inject(PanelsService);
|
||||
private readonly toast = inject(ToastService);
|
||||
private readonly dialogs = inject(DialogsService);
|
||||
private readonly settings = inject(SettingsService);
|
||||
|
||||
private readonly jobMap = signal<ReadonlyMap<string, TransferSnapshot>>(new Map());
|
||||
/** Smoothed bytes/sec per running job (UI-computed from progress deltas). */
|
||||
@@ -29,6 +31,7 @@ export class TransfersService {
|
||||
);
|
||||
readonly queuedCount = computed(() => this.jobs().filter((j) => j.state === 'queued').length);
|
||||
readonly failedJobs = computed(() => this.jobs().filter((j) => j.state === 'failed'));
|
||||
readonly finishedJobs = computed(() => this.jobs().filter((j) => !ACTIVE_STATES.includes(j.state)));
|
||||
|
||||
/** Whether the detailed per-job list is shown (toggled from the status bar). */
|
||||
readonly expanded = signal(false);
|
||||
@@ -131,6 +134,13 @@ export class TransfersService {
|
||||
await this.rpc.request.cancelTransfer({ jobId });
|
||||
}
|
||||
|
||||
/** Remove every finished (completed/failed/cancelled) entry; running ones stay. */
|
||||
clearFinished(): void {
|
||||
for (const job of this.finishedJobs()) {
|
||||
void this.dismiss(job.id);
|
||||
}
|
||||
}
|
||||
|
||||
async dismiss(jobId: string): Promise<void> {
|
||||
this.jobMap.update((prev) => {
|
||||
const next = new Map(prev);
|
||||
@@ -156,7 +166,9 @@ export class TransfersService {
|
||||
|
||||
if (snapshot.state === 'completed') {
|
||||
this.refreshPanelsOn(snapshot.dstSessionId);
|
||||
setTimeout(() => void this.dismiss(snapshot.id), AUTO_DISMISS_COMPLETED_MS);
|
||||
if (this.settings.autoClearCompleted()) {
|
||||
setTimeout(() => void this.dismiss(snapshot.id), AUTO_DISMISS_COMPLETED_MS);
|
||||
}
|
||||
} else if (snapshot.state === 'failed') {
|
||||
this.expanded.set(true);
|
||||
this.toast.error(`Transfer "${snapshot.name}" failed: ${snapshot.error ?? 'unknown error'}`);
|
||||
|
||||
Reference in New Issue
Block a user