Skip to content

Commit

Permalink
Implemented bulk delete chapter/volume on series detail page.
Browse files Browse the repository at this point in the history
  • Loading branch information
majora2007 committed Nov 17, 2024
1 parent 019ebc6 commit 4eca880
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 23 deletions.
14 changes: 11 additions & 3 deletions UI/Web/src/app/_services/action.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {inject, Injectable, OnDestroy} from '@angular/core';
import {inject, Injectable} from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import {Subject, tap} from 'rxjs';
import { take } from 'rxjs/operators';
import { BulkAddToCollectionComponent } from '../cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component';
import { AddToListModalComponent, ADD_FLOW } from '../reading-list/_modals/add-to-list-modal/add-to-list-modal.component';
Expand All @@ -22,7 +21,6 @@ import { SeriesService } from './series.service';
import {translate} from "@jsverse/transloco";
import {UserCollection} from "../_models/collection-tag";
import {CollectionTagService} from "./collection-tag.service";
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
import {FilterService} from "./filter.service";
import {ReadingListService} from "./reading-list.service";
import {ChapterService} from "./chapter.service";
Expand Down Expand Up @@ -468,6 +466,16 @@ export class ActionService {
});
}

async deleteMultipleChapters(seriesId: number, chapterIds: Array<Chapter>, callback?: BooleanActionCallback) {
if (!await this.confirmService.confirm(translate('toasts.confirm-delete-multiple-chapters'))) return;

this.chapterService.deleteMultipleChapters(seriesId, chapterIds.map(c => c.id)).subscribe(() => {
if (callback) {
callback(true);
}
});
}

/**
* Deletes multiple collections
* @param readingLists ReadingList, should have id
Expand Down
4 changes: 4 additions & 0 deletions UI/Web/src/app/_services/chapter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export class ChapterService {
return this.httpClient.delete<boolean>(this.baseUrl + 'chapter?chapterId=' + chapterId);
}

deleteMultipleChapters(seriesId: number, chapterIds: Array<number>) {
return this.httpClient.post<boolean>(this.baseUrl + `chapter/delete-multiple?seriesId=${seriesId}`, {chapterIds});
}

updateChapter(chapter: Chapter) {
return this.httpClient.post(this.baseUrl + 'chapter/update', chapter, TextResonse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
NgbTooltip
} from '@ng-bootstrap/ng-bootstrap';
import {ToastrService} from 'ngx-toastr';
import {catchError, forkJoin, Observable, of, tap} from 'rxjs';
import {catchError, debounceTime, forkJoin, Observable, of, ReplaySubject, tap} from 'rxjs';
import {map} from 'rxjs/operators';
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
import {
Expand Down Expand Up @@ -247,6 +247,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
downloadInProgress: boolean = false;

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

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

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

bulkActionCallback = (action: ActionItem<any>, data: any) => {
bulkActionCallback = async (action: ActionItem<any>, data: any) => {
if (this.series === undefined) {
return;
}
Expand Down Expand Up @@ -355,6 +349,18 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.cdRef.markForCheck();
});
break;
case Action.SendTo:
// this.actionService.sendToDevice(selectedChapterIds, _, () => {
//
// });
break;
case Action.Delete:
await this.actionService.deleteMultipleChapters(seriesId, chapters, () => {
// No need to update the page as the backend will spam volume/chapter deletions
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
});
break;
}
}

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

this.loadPage$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(300), tap(val => this.loadSeries(this.seriesId, val))).subscribe();

this.messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => {
if (event.event === EVENTS.SeriesRemoved) {
const seriesRemovedEvent = event.payload as SeriesRemovedEvent;
Expand All @@ -469,7 +477,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
} else if (event.event === EVENTS.ScanSeries) {
const seriesScanEvent = event.payload as ScanSeriesEvent;
if (seriesScanEvent.seriesId === this.seriesId) {
this.loadSeries(this.seriesId);
//this.loadSeries(this.seriesId);
this.loadPageSource.next(false);
}
} else if (event.event === EVENTS.CoverUpdate) {
const coverUpdateEvent = event.payload as CoverUpdateEvent;
Expand All @@ -479,7 +488,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
} else if (event.event === EVENTS.ChapterRemoved) {
const removedEvent = event.payload as ChapterRemovedEvent;
if (removedEvent.seriesId !== this.seriesId) return;
this.loadSeries(this.seriesId, false);
//this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
}
});

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

this.loadSeries(this.seriesId, true);
//this.loadSeries(this.seriesId, true);
this.loadPageSource.next(true);

this.pageExtrasGroup.get('renderMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((val: PageLayoutMode | null) => {
if (val == null) return;
Expand All @@ -535,12 +546,12 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
switch(action.action) {
case(Action.MarkAsRead):
this.actionService.markSeriesAsRead(series, (series: Series) => {
this.loadSeries(series.id);
this.loadPageSource.next(false);
});
break;
case(Action.MarkAsUnread):
this.actionService.markSeriesAsUnread(series, (series: Series) => {
this.loadSeries(series.id);
this.loadPageSource.next(false);
});
break;
case(Action.Scan):
Expand Down Expand Up @@ -600,7 +611,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
case(Action.Delete):
await this.actionService.deleteVolume(volume.id, (b) => {
if (!b) return;
this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
});
break;
case(Action.AddToReadingList):
Expand Down Expand Up @@ -1010,7 +1021,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {

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

ref.closed.subscribe((res: EditChapterModalCloseResult) => {
if (res.success && res.isDeleted) {
this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
}
});
}
Expand All @@ -1035,9 +1046,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
modalRef.closed.subscribe((closeResult: EditSeriesModalCloseResult) => {
if (closeResult.success) {
window.scrollTo(0, 0);
this.loadSeries(this.seriesId, closeResult.updateExternal);
this.loadPageSource.next(closeResult.updateExternal);
} else if (closeResult.updateExternal) {
this.loadSeries(this.seriesId, closeResult.updateExternal);
this.loadPageSource.next(closeResult.updateExternal);
}
});
}
Expand Down
1 change: 1 addition & 0 deletions UI/Web/src/assets/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,7 @@
"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?",
"alert-long-running": "This is a long running process. Please give it the time to complete before invoking again.",
"confirm-delete-multiple-series": "Are you sure you want to delete {{count}} series? It will not modify files on disk.",
"confirm-delete-multiple-chapters": "Are you sure you want to delete {{count}} chapter/volumes? It will not modify files on disk.",
"confirm-delete-series": "Are you sure you want to delete this series? It will not modify files on disk.",
"confirm-delete-chapter": "Are you sure you want to delete this chapter? It will not modify files on disk.",
"confirm-delete-volume": "Are you sure you want to delete this volume? It will not modify files on disk.",
Expand Down

0 comments on commit 4eca880

Please sign in to comment.