diff --git a/API/Services/Tasks/Scanner/ParseScannedFiles.cs b/API/Services/Tasks/Scanner/ParseScannedFiles.cs index 0c8fd8670..111dc71af 100644 --- a/API/Services/Tasks/Scanner/ParseScannedFiles.cs +++ b/API/Services/Tasks/Scanner/ParseScannedFiles.cs @@ -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; diff --git a/UI/Web/src/app/in-progress/in-progress.component.html b/UI/Web/src/app/in-progress/in-progress.component.html index ebae6ff8b..f4ca791f0 100644 --- a/UI/Web/src/app/in-progress/in-progress.component.html +++ b/UI/Web/src/app/in-progress/in-progress.component.html @@ -1,14 +1,14 @@ - - + - - - - - \ No newline at end of file + + + + diff --git a/UI/Web/src/app/in-progress/in-progress.component.ts b/UI/Web/src/app/in-progress/in-progress.component.ts index 164309c27..a247ff4ab 100644 --- a/UI/Web/src/app/in-progress/in-progress.component.ts +++ b/UI/Web/src/app/in-progress/in-progress.component.ts @@ -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 = 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; + } + } + } diff --git a/UI/Web/src/app/library-detail/library-detail.component.html b/UI/Web/src/app/library-detail/library-detail.component.html index 6d7885385..d01cec915 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.html +++ b/UI/Web/src/app/library-detail/library-detail.component.html @@ -9,6 +9,6 @@ (pageChange)="onPageChange($event)" > - + diff --git a/UI/Web/src/app/recently-added/recently-added.component.html b/UI/Web/src/app/recently-added/recently-added.component.html index b9bfbec74..93952f73e 100644 --- a/UI/Web/src/app/recently-added/recently-added.component.html +++ b/UI/Web/src/app/recently-added/recently-added.component.html @@ -1,14 +1,13 @@ - - - - - - - + + + + + + \ No newline at end of file diff --git a/UI/Web/src/app/recently-added/recently-added.component.ts b/UI/Web/src/app/recently-added/recently-added.component.ts index 4615f610a..22ea98b59 100644 --- a/UI/Web/src/app/recently-added/recently-added.component.ts +++ b/UI/Web/src/app/recently-added/recently-added.component.ts @@ -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 = 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; + } + } }