Skip to content

Commit 4eca880

Browse files
committed
Implemented bulk delete chapter/volume on series detail page.
1 parent 019ebc6 commit 4eca880

File tree

4 files changed

+47
-23
lines changed

4 files changed

+47
-23
lines changed

UI/Web/src/app/_services/action.service.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import {inject, Injectable, OnDestroy} from '@angular/core';
1+
import {inject, Injectable} from '@angular/core';
22
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
33
import { ToastrService } from 'ngx-toastr';
4-
import {Subject, tap} from 'rxjs';
54
import { take } from 'rxjs/operators';
65
import { BulkAddToCollectionComponent } from '../cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component';
76
import { AddToListModalComponent, ADD_FLOW } from '../reading-list/_modals/add-to-list-modal/add-to-list-modal.component';
@@ -22,7 +21,6 @@ import { SeriesService } from './series.service';
2221
import {translate} from "@jsverse/transloco";
2322
import {UserCollection} from "../_models/collection-tag";
2423
import {CollectionTagService} from "./collection-tag.service";
25-
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
2624
import {FilterService} from "./filter.service";
2725
import {ReadingListService} from "./reading-list.service";
2826
import {ChapterService} from "./chapter.service";
@@ -468,6 +466,16 @@ export class ActionService {
468466
});
469467
}
470468

469+
async deleteMultipleChapters(seriesId: number, chapterIds: Array<Chapter>, callback?: BooleanActionCallback) {
470+
if (!await this.confirmService.confirm(translate('toasts.confirm-delete-multiple-chapters'))) return;
471+
472+
this.chapterService.deleteMultipleChapters(seriesId, chapterIds.map(c => c.id)).subscribe(() => {
473+
if (callback) {
474+
callback(true);
475+
}
476+
});
477+
}
478+
471479
/**
472480
* Deletes multiple collections
473481
* @param readingLists ReadingList, should have id

UI/Web/src/app/_services/chapter.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export class ChapterService {
2121
return this.httpClient.delete<boolean>(this.baseUrl + 'chapter?chapterId=' + chapterId);
2222
}
2323

24+
deleteMultipleChapters(seriesId: number, chapterIds: Array<number>) {
25+
return this.httpClient.post<boolean>(this.baseUrl + `chapter/delete-multiple?seriesId=${seriesId}`, {chapterIds});
26+
}
27+
2428
updateChapter(chapter: Chapter) {
2529
return this.httpClient.post(this.baseUrl + 'chapter/update', chapter, TextResonse);
2630
}

UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
NgbTooltip
3838
} from '@ng-bootstrap/ng-bootstrap';
3939
import {ToastrService} from 'ngx-toastr';
40-
import {catchError, forkJoin, Observable, of, tap} from 'rxjs';
40+
import {catchError, debounceTime, forkJoin, Observable, of, ReplaySubject, tap} from 'rxjs';
4141
import {map} from 'rxjs/operators';
4242
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
4343
import {
@@ -247,6 +247,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
247247
downloadInProgress: boolean = false;
248248

249249
nextExpectedChapter: NextExpectedChapter | undefined;
250+
loadPageSource = new ReplaySubject<boolean>(1);
251+
loadPage$ = this.loadPageSource.asObservable();
250252

251253
/**
252254
* Track by function for Volume to tell when to refresh card data
@@ -256,14 +258,6 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
256258
* Track by function for Chapter to tell when to refresh card data
257259
*/
258260
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.minNumber}_${item.maxNumber}_${item.volumeId}_${item.pagesRead}`;
259-
trackByRelatedSeriesIdentify = (index: number, item: RelatedSeriesPair) => `${item.series.name}_${item.series.libraryId}_${item.series.pagesRead}_${item.relation}`;
260-
trackBySeriesIdentify = (index: number, item: Series) => `${item.name}_${item.libraryId}_${item.pagesRead}`;
261-
trackByStoryLineIdentity = (index: number, item: StoryLineItem) => {
262-
if (item.isChapter) {
263-
return this.trackByChapterIdentity(index, item!.chapter!)
264-
}
265-
return this.trackByVolumeIdentity(index, item!.volume!);
266-
};
267261

268262
/**
269263
* Are there any related series
@@ -307,7 +301,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
307301
*/
308302
download$: Observable<DownloadEvent | null> | null = null;
309303

310-
bulkActionCallback = (action: ActionItem<any>, data: any) => {
304+
bulkActionCallback = async (action: ActionItem<any>, data: any) => {
311305
if (this.series === undefined) {
312306
return;
313307
}
@@ -355,6 +349,18 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
355349
this.cdRef.markForCheck();
356350
});
357351
break;
352+
case Action.SendTo:
353+
// this.actionService.sendToDevice(selectedChapterIds, _, () => {
354+
//
355+
// });
356+
break;
357+
case Action.Delete:
358+
await this.actionService.deleteMultipleChapters(seriesId, chapters, () => {
359+
// No need to update the page as the backend will spam volume/chapter deletions
360+
this.bulkSelectionService.deselectAll();
361+
this.cdRef.markForCheck();
362+
});
363+
break;
358364
}
359365
}
360366

@@ -459,6 +465,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
459465
return this.downloadService.mapToEntityType(events, this.series);
460466
}));
461467

468+
this.loadPage$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(300), tap(val => this.loadSeries(this.seriesId, val))).subscribe();
469+
462470
this.messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => {
463471
if (event.event === EVENTS.SeriesRemoved) {
464472
const seriesRemovedEvent = event.payload as SeriesRemovedEvent;
@@ -469,7 +477,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
469477
} else if (event.event === EVENTS.ScanSeries) {
470478
const seriesScanEvent = event.payload as ScanSeriesEvent;
471479
if (seriesScanEvent.seriesId === this.seriesId) {
472-
this.loadSeries(this.seriesId);
480+
//this.loadSeries(this.seriesId);
481+
this.loadPageSource.next(false);
473482
}
474483
} else if (event.event === EVENTS.CoverUpdate) {
475484
const coverUpdateEvent = event.payload as CoverUpdateEvent;
@@ -479,7 +488,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
479488
} else if (event.event === EVENTS.ChapterRemoved) {
480489
const removedEvent = event.payload as ChapterRemovedEvent;
481490
if (removedEvent.seriesId !== this.seriesId) return;
482-
this.loadSeries(this.seriesId, false);
491+
//this.loadSeries(this.seriesId, false);
492+
this.loadPageSource.next(false);
483493
}
484494
});
485495

@@ -508,7 +518,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
508518
}
509519
}), takeUntilDestroyed(this.destroyRef)).subscribe();
510520

511-
this.loadSeries(this.seriesId, true);
521+
//this.loadSeries(this.seriesId, true);
522+
this.loadPageSource.next(true);
512523

513524
this.pageExtrasGroup.get('renderMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((val: PageLayoutMode | null) => {
514525
if (val == null) return;
@@ -535,12 +546,12 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
535546
switch(action.action) {
536547
case(Action.MarkAsRead):
537548
this.actionService.markSeriesAsRead(series, (series: Series) => {
538-
this.loadSeries(series.id);
549+
this.loadPageSource.next(false);
539550
});
540551
break;
541552
case(Action.MarkAsUnread):
542553
this.actionService.markSeriesAsUnread(series, (series: Series) => {
543-
this.loadSeries(series.id);
554+
this.loadPageSource.next(false);
544555
});
545556
break;
546557
case(Action.Scan):
@@ -600,7 +611,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
600611
case(Action.Delete):
601612
await this.actionService.deleteVolume(volume.id, (b) => {
602613
if (!b) return;
603-
this.loadSeries(this.seriesId, false);
614+
this.loadPageSource.next(false);
604615
});
605616
break;
606617
case(Action.AddToReadingList):
@@ -1010,7 +1021,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
10101021

10111022
ref.closed.subscribe((res: EditChapterModalCloseResult) => {
10121023
if (res.success && res.isDeleted) {
1013-
this.loadSeries(this.seriesId, false);
1024+
this.loadPageSource.next(false);
10141025
}
10151026
});
10161027
}
@@ -1024,7 +1035,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
10241035

10251036
ref.closed.subscribe((res: EditChapterModalCloseResult) => {
10261037
if (res.success && res.isDeleted) {
1027-
this.loadSeries(this.seriesId, false);
1038+
this.loadPageSource.next(false);
10281039
}
10291040
});
10301041
}
@@ -1035,9 +1046,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
10351046
modalRef.closed.subscribe((closeResult: EditSeriesModalCloseResult) => {
10361047
if (closeResult.success) {
10371048
window.scrollTo(0, 0);
1038-
this.loadSeries(this.seriesId, closeResult.updateExternal);
1049+
this.loadPageSource.next(closeResult.updateExternal);
10391050
} else if (closeResult.updateExternal) {
1040-
this.loadSeries(this.seriesId, closeResult.updateExternal);
1051+
this.loadPageSource.next(closeResult.updateExternal);
10411052
}
10421053
});
10431054
}

UI/Web/src/assets/langs/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,7 @@
23972397
"confirm-regen-covers": "Refresh covers will force all cover images to be recalculated. This is a heavy operation. Are you sure you don't want to perform a Scan instead?",
23982398
"alert-long-running": "This is a long running process. Please give it the time to complete before invoking again.",
23992399
"confirm-delete-multiple-series": "Are you sure you want to delete {{count}} series? It will not modify files on disk.",
2400+
"confirm-delete-multiple-chapters": "Are you sure you want to delete {{count}} chapter/volumes? It will not modify files on disk.",
24002401
"confirm-delete-series": "Are you sure you want to delete this series? It will not modify files on disk.",
24012402
"confirm-delete-chapter": "Are you sure you want to delete this chapter? It will not modify files on disk.",
24022403
"confirm-delete-volume": "Are you sure you want to delete this volume? It will not modify files on disk.",

0 commit comments

Comments
 (0)