Misc Polish (#1569)
* Introduced a lock for DB work during the scan to hopefully reduce the concurrency issues * Don't allow multiple theme scans to occur * Fixed bulk actions not having all actions due to nested actionable menu changes * Refactored the Scan loop to be synchronous to avoid any issues. After first loop, no real performance issues. * Updated the LibraryWatcher when under many internal buffer full issues, to suspend watching for a full hour, to allow whatever downloading to complete. * Removed Semaphore as it's not needed anymore * Updated the output for logger to explicitly say from Kavita (if you're pushing to Seq) * Fixed a broken test * Fixed ReleaseYear not populating due to a change from a contributor around how to populate ReleaseYear. * Ensure when scan folder runs, that we don't double enqueue the same tasks. * Fixed user settings not loading the correct tab * Changed the Release Year -> Release * Added more refresh hooks in reader to hopefully ensure faster refreshes * Reset images between chapter loads to help flush image faster. Don't show broken image icon when an image is still loading. * Fixed the prefetcher not properly loading the correct images and hence, allowing a bit of lag between chapter loads. * Code smells
This commit is contained in:
parent
097ec32842
commit
58bbba29cc
17 changed files with 208 additions and 45 deletions
|
@ -543,12 +543,28 @@ export class ActionFactoryService {
|
|||
});
|
||||
}
|
||||
|
||||
private applyCallbackToList(list: Array<ActionItem<any>>, callback: (action: ActionItem<any>, data: any) => void): Array<ActionItem<any>> {
|
||||
public applyCallbackToList(list: Array<ActionItem<any>>, callback: (action: ActionItem<any>, data: any) => void): Array<ActionItem<any>> {
|
||||
const actions = list.map((a) => {
|
||||
return { ...a };
|
||||
});
|
||||
actions.forEach((action) => this.applyCallback(action, callback));
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Checks the whole tree for the action and returns true if it exists
|
||||
public hasAction(actions: Array<ActionItem<any>>, action: Action) {
|
||||
var actionFound = false;
|
||||
|
||||
if (actions.length === 0) return actionFound;
|
||||
|
||||
for (let i = 0; i < actions.length; i++)
|
||||
{
|
||||
if (actions[i].action === action) return true;
|
||||
if (this.hasAction(actions[i].children, action)) return true;
|
||||
}
|
||||
|
||||
|
||||
return actionFound;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { Action, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import { BulkSelectionService } from '../bulk-selection.service';
|
||||
|
||||
@Component({
|
||||
|
@ -24,14 +24,15 @@ export class BulkOperationsComponent implements OnInit, OnDestroy {
|
|||
return Action;
|
||||
}
|
||||
|
||||
constructor(public bulkSelectionService: BulkSelectionService, private readonly cdRef: ChangeDetectorRef) { }
|
||||
constructor(public bulkSelectionService: BulkSelectionService, private readonly cdRef: ChangeDetectorRef,
|
||||
private actionFactoryService: ActionFactoryService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.bulkSelectionService.actions$.pipe(takeUntil(this.onDestory)).subscribe(actions => {
|
||||
actions.forEach(a => a.callback = this.actionCallback.bind(this));
|
||||
this.actions = actions;
|
||||
this.hasMarkAsRead = this.actions.filter(act => act.action === Action.MarkAsRead).length > 0;
|
||||
this.hasMarkAsUnread = this.actions.filter(act => act.action === Action.MarkAsUnread).length > 0;
|
||||
// We need to do a recursive callback apply
|
||||
this.actions = this.actionFactoryService.applyCallbackToList(actions, this.actionCallback.bind(this));
|
||||
this.hasMarkAsRead = this.actionFactoryService.hasAction(this.actions, Action.MarkAsRead);
|
||||
this.hasMarkAsUnread = this.actionFactoryService.hasAction(this.actions, Action.MarkAsUnread);
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
@ -46,9 +47,7 @@ export class BulkOperationsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
performAction(action: ActionItem<any>) {
|
||||
if (typeof action.callback === 'function') {
|
||||
action.callback(action, null);
|
||||
}
|
||||
this.actionCallback(action, null);
|
||||
}
|
||||
|
||||
executeAction(action: Action) {
|
||||
|
|
|
@ -142,16 +142,17 @@ export class BulkSelectionService {
|
|||
getActions(callback: (action: ActionItem<any>, data: any) => void) {
|
||||
// checks if series is present. If so, returns only series actions
|
||||
// else returns volume/chapter items
|
||||
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection, Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];
|
||||
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection,
|
||||
Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];
|
||||
if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {
|
||||
return this.actionFactory.getSeriesActions(callback).filter(item => allowedActions.includes(item.action));
|
||||
return this.applyFilterToList(this.actionFactory.getSeriesActions(callback), allowedActions);
|
||||
}
|
||||
|
||||
if (Object.keys(this.selectedCards).filter(item => item === 'bookmark').length > 0) {
|
||||
return this.actionFactory.getBookmarkActions(callback);
|
||||
}
|
||||
|
||||
return this.actionFactory.getVolumeActions(callback).filter(item => allowedActions.includes(item.action));
|
||||
return this.applyFilterToList(this.actionFactory.getVolumeActions(callback), allowedActions);
|
||||
}
|
||||
|
||||
private debugLog(message: string, extraData?: any) {
|
||||
|
@ -163,4 +164,29 @@ export class BulkSelectionService {
|
|||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
private applyFilter(action: ActionItem<any>, allowedActions: Array<Action>) {
|
||||
|
||||
var ret = false;
|
||||
if (action.action === Action.Submenu || allowedActions.includes(action.action)) {
|
||||
// Do something
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (action.children === null || action.children?.length === 0) return ret;
|
||||
|
||||
action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));
|
||||
|
||||
// action.children?.forEach((childAction) => {
|
||||
// this.applyFilter(childAction, allowedActions);
|
||||
// });
|
||||
return ret;
|
||||
}
|
||||
|
||||
private applyFilterToList(list: Array<ActionItem<any>>, allowedActions: Array<Action>): Array<ActionItem<any>> {
|
||||
const actions = list.map((a) => {
|
||||
return { ...a };
|
||||
});
|
||||
return actions.filter(action => this.applyFilter(action, allowedActions));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="row g-0 mb-4 mt-3">
|
||||
<ng-container *ngIf="seriesMetadata.releaseYear > 0">
|
||||
<div class="col-lg-1 col-md-4 col-sm-4 col-4 mb-3">
|
||||
<app-icon-and-title label="Release Year" [clickable]="false" fontClasses="fa-regular fa-calendar" title="Release Year">
|
||||
<app-icon-and-title label="Release" [clickable]="false" fontClasses="fa-regular fa-calendar" title="Release Year">
|
||||
{{seriesMetadata.releaseYear}}
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
|
|
|
@ -66,11 +66,11 @@
|
|||
'fit-to-height-double-offset': FittingOption === FITTING_OPTION.HEIGHT && ShouldRenderDoublePage,
|
||||
'original-double-offset' : FittingOption === FITTING_OPTION.ORIGINAL && ShouldRenderDoublePage}"
|
||||
[style.filter]="'brightness(' + generalSettingsForm.get('darkness')?.value + '%)' | safeStyle" (dblclick)="bookmarkPage($event)">
|
||||
<img #image [src]="canvasImage.src" id="image-1"
|
||||
<img alt=" " #image [src]="canvasImage.src" id="image-1"
|
||||
class="{{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}}">
|
||||
|
||||
<ng-container *ngIf="(this.canvasImage2.src !== '') && (readerService.imageUrlToPageNum(canvasImage2.src) <= maxPages - 1 && !isCoverImage())">
|
||||
<img [src]="canvasImage2.src" id="image-2" class="image-2 {{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}}"> <!-- {{ShouldRenderReverseDouble ? 'reverse' : ''}} -->
|
||||
<img alt=" " [src]="canvasImage2.src" id="image-2" class="image-2 {{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}}"> <!-- {{ShouldRenderReverseDouble ? 'reverse' : ''}} -->
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -661,6 +661,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.pageNum = 0;
|
||||
this.pagingDirection = PAGING_DIRECTION.FORWARD;
|
||||
this.inSetup = true;
|
||||
this.canvasImage.src = '';
|
||||
this.canvasImage2.src = '';
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
if (this.goToPageEvent) {
|
||||
|
@ -1042,8 +1044,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.isCoverImage()
|
||||
|| this.isWideImage(this.canvasImagePrev)
|
||||
) ? 2 : 1;
|
||||
}
|
||||
if (this.layoutMode === LayoutMode.DoubleReversed) {
|
||||
} else if (this.layoutMode === LayoutMode.DoubleReversed) {
|
||||
pageAmount = !(
|
||||
this.isCoverImage()
|
||||
|| this.isCoverImage(this.pageNum - 1)
|
||||
|
@ -1300,13 +1301,14 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
* and also maintains page info (wide image, etc) due to onload event.
|
||||
*/
|
||||
prefetch() {
|
||||
for(let i = 1; i <= PREFETCH_PAGES - 3; i++) {
|
||||
for(let i = 0; i <= PREFETCH_PAGES - 3; i++) {
|
||||
const numOffset = this.pageNum + i;
|
||||
if (numOffset > this.maxPages - 1) continue;
|
||||
|
||||
const index = numOffset % this.cachedImages.length;
|
||||
const index = (numOffset % this.cachedImages.length + this.cachedImages.length) % this.cachedImages.length;
|
||||
if (this.readerService.imageUrlToPageNum(this.cachedImages[index].src) !== numOffset) {
|
||||
this.cachedImages[index].src = this.getPageUrl(numOffset);
|
||||
this.cachedImages[index].onload = () => this.cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
|||
{title: 'Theme', fragment: FragmentID.Theme},
|
||||
{title: 'Devices', fragment: FragmentID.Devices},
|
||||
];
|
||||
active = this.tabs[0];
|
||||
active = this.tabs[1];
|
||||
opdsEnabled: boolean = false;
|
||||
makeUrl: (val: string) => string = (val: string) => {return this.transformKeyToOpdsUrl(val)};
|
||||
|
||||
|
@ -87,7 +87,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
|||
if (tab.length > 0) {
|
||||
this.active = tab[0];
|
||||
} else {
|
||||
this.active = this.tabs[0]; // Default to first tab
|
||||
this.active = this.tabs[1]; // Default to preferences
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue