Bulk Operations for In Progress and Recently Added (#677)

* Don't log a message about bad match if the file is a cover image

* Enable bulk operations for In Progress and Recently Added

* Fixed a bad logic case
This commit is contained in:
Joseph Milazzo 2021-10-15 14:32:17 -07:00 committed by GitHub
parent ef383295f0
commit 98e5cb87ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 35 deletions

View File

@ -73,7 +73,8 @@ namespace API.Services.Tasks.Scanner
info = Parser.Parser.Parse(path, rootPath, type);
}
if (info == null)
// If we couldn't match, log. But don't log if the file parses as a cover image
if (info == null || !(Parser.Parser.IsImage(path) || Parser.Parser.IsCoverImage(path)))
{
_logger.LogWarning("[Scanner] Could not parse series from {Path}", path);
return;

View File

@ -1,14 +1,14 @@
<ng-container>
<app-card-detail-layout header="In Progress"
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
<app-card-detail-layout header="In Progress"
[isLoading]="isLoading"
[items]="recentlyAdded"
[items]="series"
[filters]="filters"
[pagination]="pagination"
(pageChange)="onPageChange($event)"
(applyFilter)="updateFilter($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="item.libraryId" (reload)="loadPage()"></app-series-card>
</ng-template>
</app-card-detail-layout>
</ng-container>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="item.libraryId" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
</ng-template>
</app-card-detail-layout>

View File

@ -1,11 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Component, HostListener, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router, ActivatedRoute } from '@angular/router';
import { take } from 'rxjs/operators';
import { BulkSelectionService } from '../cards/bulk-selection.service';
import { UpdateFilterEvent } from '../cards/card-detail-layout/card-detail-layout.component';
import { KEY_CODES } from '../shared/_services/utility.service';
import { Pagination } from '../_models/pagination';
import { Series } from '../_models/series';
import { FilterItem, SeriesFilter, mangaFormatFilters } from '../_models/series-filter';
import { Action } from '../_services/action-factory.service';
import { ActionService } from '../_services/action.service';
import { SeriesService } from '../_services/series.service';
@Component({
@ -16,7 +20,7 @@ import { SeriesService } from '../_services/series.service';
export class InProgressComponent implements OnInit {
isLoading: boolean = true;
recentlyAdded: Series[] = [];
series: Series[] = [];
pagination!: Pagination;
libraryId!: number;
filters: Array<FilterItem> = mangaFormatFilters;
@ -24,7 +28,8 @@ export class InProgressComponent implements OnInit {
mangaFormat: null
};
constructor(private router: Router, private route: ActivatedRoute, private seriesService: SeriesService, private titleService: Title) {
constructor(private router: Router, private route: ActivatedRoute, private seriesService: SeriesService, private titleService: Title,
private actionService: ActionService, public bulkSelectionService: BulkSelectionService) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.titleService.setTitle('Kavita - In Progress');
if (this.pagination === undefined || this.pagination === null) {
@ -33,6 +38,20 @@ export class InProgressComponent implements OnInit {
this.loadPage();
}
@HostListener('document:keydown.shift', ['$event'])
handleKeypress(event: KeyboardEvent) {
if (event.key === KEY_CODES.SHIFT) {
this.bulkSelectionService.isShiftDown = true;
}
}
@HostListener('document:keyup.shift', ['$event'])
handleKeyUp(event: KeyboardEvent) {
if (event.key === KEY_CODES.SHIFT) {
this.bulkSelectionService.isShiftDown = false;
}
}
ngOnInit() {}
seriesClicked(series: Series) {
@ -61,7 +80,7 @@ export class InProgressComponent implements OnInit {
}
this.isLoading = true;
this.seriesService.getInProgress(this.libraryId, this.pagination?.currentPage, this.pagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => {
this.recentlyAdded = series.result;
this.series = series.result;
this.pagination = series.pagination;
this.isLoading = false;
window.scrollTo(0, 0);
@ -73,4 +92,35 @@ export class InProgressComponent implements OnInit {
return urlParams.get('page');
}
bulkActionCallback = (action: Action, data: any) => {
const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series');
const selectedSeries = this.series.filter((series, index: number) => selectedSeriesIndexies.includes(index + ''));
switch (action) {
case Action.AddToReadingList:
this.actionService.addMultipleSeriesToReadingList(selectedSeries, () => {
this.bulkSelectionService.deselectAll();
});
break;
case Action.AddToCollection:
this.actionService.addMultipleSeriesToCollectionTag(selectedSeries, () => {
this.bulkSelectionService.deselectAll();
});
break;
case Action.MarkAsRead:
this.actionService.markMultipleSeriesAsRead(selectedSeries, () => {
this.loadPage();
this.bulkSelectionService.deselectAll();
});
break;
case Action.MarkAsUnread:
this.actionService.markMultipleSeriesAsUnread(selectedSeries, () => {
this.loadPage();
this.bulkSelectionService.deselectAll();
});
break;
}
}
}

View File

@ -9,6 +9,6 @@
(pageChange)="onPageChange($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="libraryId" [suppressLibraryLink]="true" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
<app-series-card [data]="item" [libraryId]="libraryId" [suppressLibraryLink]="true" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
</ng-template>
</app-card-detail-layout>

View File

@ -1,14 +1,13 @@
<ng-container>
<app-card-detail-layout header="Recently Added"
[isLoading]="isLoading"
[items]="recentlyAdded"
[pagination]="pagination"
[filters]="filters"
(applyFilter)="updateFilter($event)"
(pageChange)="onPageChange($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="item.libraryId" (reload)="loadPage()"></app-series-card>
</ng-template>
</app-card-detail-layout>
</ng-container>
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
<app-card-detail-layout header="Recently Added"
[isLoading]="isLoading"
[items]="series"
[pagination]="pagination"
[filters]="filters"
(applyFilter)="updateFilter($event)"
(pageChange)="onPageChange($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="item.libraryId" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
</ng-template>
</app-card-detail-layout>

View File

@ -1,11 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { Component, HostListener, OnDestroy, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { take } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
import { BulkSelectionService } from '../cards/bulk-selection.service';
import { UpdateFilterEvent } from '../cards/card-detail-layout/card-detail-layout.component';
import { KEY_CODES } from '../shared/_services/utility.service';
import { SeriesAddedEvent } from '../_models/events/series-added-event';
import { Pagination } from '../_models/pagination';
import { Series } from '../_models/series';
import { FilterItem, mangaFormatFilters, SeriesFilter } from '../_models/series-filter';
import { Action, ActionFactoryService } from '../_services/action-factory.service';
import { ActionService } from '../_services/action.service';
import { MessageHubService } from '../_services/message-hub.service';
import { SeriesService } from '../_services/series.service';
/**
@ -16,10 +23,10 @@ import { SeriesService } from '../_services/series.service';
templateUrl: './recently-added.component.html',
styleUrls: ['./recently-added.component.scss']
})
export class RecentlyAddedComponent implements OnInit {
export class RecentlyAddedComponent implements OnInit, OnDestroy {
isLoading: boolean = true;
recentlyAdded: Series[] = [];
series: Series[] = [];
pagination!: Pagination;
libraryId!: number;
@ -28,7 +35,10 @@ export class RecentlyAddedComponent implements OnInit {
mangaFormat: null
};
constructor(private router: Router, private route: ActivatedRoute, private seriesService: SeriesService, private titleService: Title) {
onDestroy: Subject<void> = new Subject();
constructor(private router: Router, private route: ActivatedRoute, private seriesService: SeriesService, private titleService: Title,
private actionService: ActionService, public bulkSelectionService: BulkSelectionService, private hubService: MessageHubService) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.titleService.setTitle('Kavita - Recently Added');
if (this.pagination === undefined || this.pagination === null) {
@ -37,7 +47,30 @@ export class RecentlyAddedComponent implements OnInit {
this.loadPage();
}
ngOnInit() {}
@HostListener('document:keydown.shift', ['$event'])
handleKeypress(event: KeyboardEvent) {
if (event.key === KEY_CODES.SHIFT) {
this.bulkSelectionService.isShiftDown = true;
}
}
@HostListener('document:keyup.shift', ['$event'])
handleKeyUp(event: KeyboardEvent) {
if (event.key === KEY_CODES.SHIFT) {
this.bulkSelectionService.isShiftDown = false;
}
}
ngOnInit() {
this.hubService.seriesAdded.pipe(takeWhile(event => event.libraryId === this.libraryId), debounceTime(6000), takeUntil(this.onDestroy)).subscribe((event: SeriesAddedEvent) => {
this.loadPage();
});
}
ngOnDestroy() {
this.onDestroy.next();
this.onDestroy.complete();
}
seriesClicked(series: Series) {
this.router.navigate(['library', this.libraryId, 'series', series.id]);
@ -65,7 +98,7 @@ export class RecentlyAddedComponent implements OnInit {
}
this.isLoading = true;
this.seriesService.getRecentlyAdded(this.libraryId, this.pagination?.currentPage, this.pagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => {
this.recentlyAdded = series.result;
this.series = series.result;
this.pagination = series.pagination;
this.isLoading = false;
window.scrollTo(0, 0);
@ -76,4 +109,35 @@ export class RecentlyAddedComponent implements OnInit {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('page');
}
bulkActionCallback = (action: Action, data: any) => {
const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series');
const selectedSeries = this.series.filter((series, index: number) => selectedSeriesIndexies.includes(index + ''));
switch (action) {
case Action.AddToReadingList:
this.actionService.addMultipleSeriesToReadingList(selectedSeries, () => {
this.bulkSelectionService.deselectAll();
});
break;
case Action.AddToCollection:
this.actionService.addMultipleSeriesToCollectionTag(selectedSeries, () => {
this.bulkSelectionService.deselectAll();
});
break;
case Action.MarkAsRead:
this.actionService.markMultipleSeriesAsRead(selectedSeries, () => {
this.loadPage();
this.bulkSelectionService.deselectAll();
});
break;
case Action.MarkAsUnread:
this.actionService.markMultipleSeriesAsUnread(selectedSeries, () => {
this.loadPage();
this.bulkSelectionService.deselectAll();
});
break;
}
}
}