mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
fix(downloads): keep recovery instructions open while copying
This commit is contained in:
1 parent
9f91e074aa
commit
08421cc184
8 files changed
+102
-4
No files matched your search
@@ -736,6 +736,18 @@ test('@downloads @epg @xtream @electron downloads a completed archive into the l
|
||||
exact: true,
|
||||
})
|
||||
).toBeVisible();
|
||||
await app.mainWindow.evaluate(() => {
|
||||
Object.defineProperty(navigator.clipboard, 'writeText', {
|
||||
configurable: true,
|
||||
value: () =>
|
||||
Promise.reject(new Error('simulated clipboard failure')),
|
||||
});
|
||||
});
|
||||
await recoveryDialog
|
||||
.getByRole('button', { name: 'Copy recovery path', exact: true })
|
||||
.click();
|
||||
await expect(recoveryDialog).toBeVisible();
|
||||
await expect(recoveryDialog).toContainText(capturedPath);
|
||||
expect(readFileSync(capturedPath, 'utf8')).toBe(
|
||||
'foreign recovery content'
|
||||
);
|
||||
|
||||
@@ -121,6 +121,7 @@ the capture and its journal for recovery. Even after a foreign entry is restored
|
||||
its private recovery copy is never automatically unlinked: another process could
|
||||
remove the public link first. Remove/Clear return a structured recovery path and open a persistent, localized
|
||||
dialog with the full path, Copy recovery path and manual recovery instructions.
|
||||
Copy keeps the dialog open, including on clipboard failure; Close dismisses it.
|
||||
After the user recovers it and explicitly removes that private copy, cleanup may release the
|
||||
journal. Ordinary owned-file cleanup remains automatic. Remove/Clear, Retry/Resume and fresh
|
||||
reservations retry identity-verified cleanup, including after restart and on
|
||||
|
||||
@@ -208,6 +208,7 @@ export class DownloadManagerActionsService {
|
||||
private showRecovery(path: string): void {
|
||||
this.dialogs.openConfirmDialog({
|
||||
title: this.translate.instant('DOWNLOADS.RECOVERY.TITLE'),
|
||||
keepOpenOnConfirm: true,
|
||||
cancelLabel: this.translate.instant('CLOSE'),
|
||||
width: '600px',
|
||||
message: this.translate.instant('DOWNLOADS.RECOVERY.MESSAGE', {
|
||||
|
||||
@@ -668,6 +668,7 @@ describe('DownloadsComponent', () => {
|
||||
title: 'File recovery needed',
|
||||
message: expect.stringContaining(recoveryPath),
|
||||
confirmLabel: 'Copy recovery path',
|
||||
keepOpenOnConfirm: true,
|
||||
cancelLabel: 'Close',
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ConfirmDialogComponent } from './confirm-dialog.component';
|
||||
|
||||
describe('ConfirmDialogComponent actions', () => {
|
||||
it.each([false, true])(
|
||||
'keeps the action in place only when requested: %s',
|
||||
async (keepOpenOnConfirm) => {
|
||||
const onConfirm = jest.fn();
|
||||
const close = jest.fn();
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ConfirmDialogComponent,
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot(),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: MAT_DIALOG_DATA,
|
||||
useValue: {
|
||||
title: 'Recovery',
|
||||
message: '/saved/entry',
|
||||
confirmLabel: 'Copy',
|
||||
cancelLabel: 'Close',
|
||||
keepOpenOnConfirm,
|
||||
onConfirm,
|
||||
},
|
||||
},
|
||||
{ provide: MatDialogRef, useValue: { close } },
|
||||
],
|
||||
}).compileComponents();
|
||||
const fixture = TestBed.createComponent(ConfirmDialogComponent);
|
||||
fixture.detectChanges();
|
||||
const buttons = fixture.nativeElement.querySelectorAll(
|
||||
'button'
|
||||
) as NodeListOf<HTMLButtonElement>;
|
||||
buttons[1].click();
|
||||
if (keepOpenOnConfirm) {
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
expect(close).not.toHaveBeenCalled();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'/saved/entry'
|
||||
);
|
||||
} else {
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalledWith(true);
|
||||
}
|
||||
buttons[0].click();
|
||||
expect(close).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -9,6 +9,8 @@ export interface ConfirmDialogData {
|
||||
width?: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
/** Run the action in the dialog; only Close/backdrop/Escape dismiss it. */
|
||||
keepOpenOnConfirm?: boolean;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
@@ -25,9 +27,23 @@ export interface ConfirmDialogData {
|
||||
<button mat-button mat-dialog-close cdkFocusInitial>
|
||||
{{ dialogData.cancelLabel || 'NO' | translate }}
|
||||
</button>
|
||||
<button mat-flat-button [mat-dialog-close]="true" color="primary">
|
||||
{{ dialogData.confirmLabel || 'YES' | translate }}
|
||||
</button>
|
||||
@if (dialogData.keepOpenOnConfirm) {
|
||||
<button
|
||||
mat-flat-button
|
||||
(click)="dialogData.onConfirm()"
|
||||
color="primary"
|
||||
>
|
||||
{{ dialogData.confirmLabel || 'YES' | translate }}
|
||||
</button>
|
||||
} @else {
|
||||
<button
|
||||
mat-flat-button
|
||||
[mat-dialog-close]="true"
|
||||
color="primary"
|
||||
>
|
||||
{{ dialogData.confirmLabel || 'YES' | translate }}
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
})
|
||||
|
||||
@@ -83,4 +83,15 @@ describe('DialogService', () => {
|
||||
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('does not repeat a persistent dialog action on programmatic close', () => {
|
||||
const onConfirm = jest.fn();
|
||||
dialog.open.mockReturnValue({ afterClosed: () => of(true) });
|
||||
service.openConfirmDialog({
|
||||
title: 'Recovery',
|
||||
message: 'Path',
|
||||
keepOpenOnConfirm: true,
|
||||
onConfirm,
|
||||
});
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -28,6 +28,8 @@ export class DialogService {
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.subscribe((result) => (result ? data.onConfirm() : null));
|
||||
.subscribe((result) =>
|
||||
result && !data.keepOpenOnConfirm ? data.onConfirm() : null
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user