fixed overlapping seeker cycle resets losing progress state

This commit is contained in:
Flaminel committed 2026-10-20 23:17:32 +03:00
1 parent fbee512835
commit 676679befc
3 files changed
+45 -6

No files matched your search

@@ -112,8 +112,8 @@
class="instance-card__reset"
variant="ghost"
size="sm"
[disabled]="inst.cycleItemsSearched === 0 || resettingId() === inst.instanceId"
[loading]="resettingId() === inst.instanceId"
[disabled]="inst.cycleItemsSearched === 0 || resettingIds().has(inst.instanceId)"
[loading]="resettingIds().has(inst.instanceId)"
(clicked)="resetCycle(inst)"
>
Reset cycle
@@ -572,6 +572,37 @@ describe('SearchesTabComponent', () => {
expect(resetButton(fixture, 'Zulu').disabled).toBe(false);
});
it('keeps a reset in progress when another instance finishes meanwhile', async () => {
const { fixture, component, confirm, resetCycle } = setup();
const zulu = new Subject<null>();
const beta = new Subject<null>();
resetCycle.mockImplementation((id: string) => (id === 'radarr-zulu' ? zulu : beta));
const zuluAccepted = component.resetCycle(RADARR_ZULU);
confirm.accept();
await zuluAccepted;
fixture.detectChanges();
const betaAccepted = component.resetCycle(SONARR_BETA);
confirm.accept();
await betaAccepted;
fixture.detectChanges();
beta.next(null);
beta.complete();
fixture.detectChanges();
expect(resetButton(fixture, 'Beta').disabled).toBe(false);
expect(resetButton(fixture, 'Zulu').disabled).toBe(true);
expect(resetButton(fixture, 'Zulu').classList).toContain('btn--loading');
zulu.next(null);
zulu.complete();
fixture.detectChanges();
expect(resetButton(fixture, 'Zulu').disabled).toBe(false);
expect(resetButton(fixture, 'Zulu').classList).not.toContain('btn--loading');
});
it('re-enables the button when the reset fails', async () => {
const { fixture, component, confirm, resetCycle } = setup();
const inFlight = new Subject<null>();
@@ -115,7 +115,7 @@ export class SearchesTabComponent {
);
readonly selectedInstanceId = signal<string>('');
readonly resettingId = signal<string | null>(null);
readonly resettingIds = signal<ReadonlySet<string>>(new Set());
readonly instanceOptions = computed<SelectOption[]>(() => {
return [
{ label: 'All Instances', value: '' },
@@ -323,6 +323,14 @@ export class SearchesTabComponent {
this.eventsResource.reload();
}
private finishReset(instanceId: string): void {
this.resettingIds.update((ids) => {
const next = new Set(ids);
next.delete(instanceId);
return next;
});
}
async resetCycle(inst: InstanceSearchStat): Promise<void> {
const confirmed = await this.confirmService.confirm({
title: 'Reset Cycle',
@@ -334,15 +342,15 @@ export class SearchesTabComponent {
return;
}
this.resettingId.set(inst.instanceId);
this.resettingIds.update((ids) => new Set(ids).add(inst.instanceId));
this.api.resetCycle(inst.instanceId).subscribe({
next: () => {
this.resettingId.set(null);
this.finishReset(inst.instanceId);
this.toast.success(`Cycle reset for ${inst.instanceName}`);
this.summaryResource.reload();
},
error: (err: ApiError) => {
this.resettingId.set(null);
this.finishReset(inst.instanceId);
this.toast.error(err.message);
},
});