Misc Bugfixes and Enhancements (#507)

* Removed some extra spam for the console

* Implemented the code to update RowVersion, which is our concurrency check

* Fixed a critical issue where more than one bookmark could occur for a given chapter due to a race condition. Now we use concurrency checks and we also gracefully allow more than one row, by only grabbing first.

* Cleaned up the logic for IHasConcurencyToken and updated the setters to be private.

* Lots of comments and when deleting a library, remove any user progress items for which chapters don't exist.

* When deleting a Series, cleanup user progress rows.

* Now after a scan of library, if a series is removed, collection tags are pruned as well if there are no longer any series bound to it.

* Updated the image on the Readme to show a better picture

* Small code cleanup to remove null check modifier as I check for null just before then

* Fixed images loading multiple times due to using function in binding with random. You can now click chapter images to read that chapter specifically.

* Fixed cards being different sizes when read vs unread

* Moved over Robbie's workflow changes from notifier. Commented out activity indicators as that is not shipping with this release.

* Remove code that isn't needed

* Reverted GA

* Changed GA to trigger only when HEAD is updated
This commit is contained in:
Joseph Milazzo 2021-08-17 14:15:32 -07:00 committed by GitHub
parent 16faa82d20
commit 3ed99afd32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1378 additions and 81 deletions

View file

@ -1,11 +1,15 @@
import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs/operators';
import { UtilityService } from 'src/app/shared/_services/utility.service';
import { Chapter } from 'src/app/_models/chapter';
import { MangaFile } from 'src/app/_models/manga-file';
import { MangaFormat } from 'src/app/_models/manga-format';
import { Volume } from 'src/app/_models/volume';
import { AccountService } from 'src/app/_services/account.service';
import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/action-factory.service';
import { ActionService } from 'src/app/_services/action.service';
import { ImageService } from 'src/app/_services/image.service';
import { UploadService } from 'src/app/_services/upload.service';
import { ChangeCoverImageModalComponent } from '../change-cover-image/change-cover-image-modal.component';
@ -21,6 +25,7 @@ export class CardDetailsModalComponent implements OnInit {
@Input() parentName = '';
@Input() seriesId: number = 0;
@Input() libraryId: number = 0;
@Input() data!: any; // Volume | Chapter
isChapter = false;
chapters: Chapter[] = [];
@ -31,20 +36,34 @@ export class CardDetailsModalComponent implements OnInit {
* If a cover image update occured.
*/
coverImageUpdate: boolean = false;
isAdmin: boolean = false;
actions: ActionItem<any>[] = [];
chapterActions: ActionItem<Chapter>[] = [];
constructor(private modalService: NgbModal, public modal: NgbActiveModal, public utilityService: UtilityService,
public imageService: ImageService, private uploadService: UploadService, private toastr: ToastrService) { }
public imageService: ImageService, private uploadService: UploadService, private toastr: ToastrService,
private accountService: AccountService, private actionFactoryService: ActionFactoryService,
private actionService: ActionService, private router: Router) { }
ngOnInit(): void {
this.isChapter = this.utilityService.isChapter(this.data);
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
if (user) {
this.isAdmin = this.accountService.hasAdminRole(user);
}
});
this.chapterActions = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this)).filter(item => item.action !== Action.Edit);
if (this.isChapter) {
this.chapters.push(this.data);
} else if (!this.isChapter) {
this.chapters.push(...this.data?.chapters);
}
this.chapters.sort(this.utilityService.sortChapters);
this.chapters.forEach(c => c.coverImage = this.imageService.getChapterCoverImage(c.id));
// Try to show an approximation of the reading order for files
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
this.chapters.forEach((c: Chapter) => {
@ -63,10 +82,17 @@ export class CardDetailsModalComponent implements OnInit {
return chapter.number;
}
performAction(action: ActionItem<any>, chapter: Chapter) {
if (typeof action.callback === 'function') {
action.callback(action.action, chapter);
}
}
updateCover() {
const modalRef = this.modalService.open(ChangeCoverImageModalComponent, { size: 'lg' }); // scrollable: true, size: 'lg', windowClass: 'scrollable-modal' (these don't work well on mobile)
if (this.utilityService.isChapter(this.data)) {
const chapter = this.utilityService.asChapter(this.data)
chapter.coverImage = this.imageService.getChapterCoverImage(chapter.id);
modalRef.componentInstance.chapter = chapter;
modalRef.componentInstance.title = 'Select ' + (chapter.isSpecial ? '' : 'Chapter ') + chapter.range + '\'s Cover';
} else {
@ -85,8 +111,52 @@ export class CardDetailsModalComponent implements OnInit {
this.uploadService.resetChapterCoverLock(closeResult.chapter.id).subscribe(() => {
this.toastr.info('Please refresh in a bit for the cover image to be reflected.');
});
} else {
closeResult.chapter.coverImage = this.imageService.randomize(this.imageService.getChapterCoverImage(closeResult.chapter.id));
}
}
});
}
markChapterAsRead(chapter: Chapter) {
if (this.seriesId === 0) {
return;
}
this.actionService.markChapterAsRead(this.seriesId, chapter, () => { /* No Action */ });
}
markChapterAsUnread(chapter: Chapter) {
if (this.seriesId === 0) {
return;
}
this.actionService.markChapterAsUnread(this.seriesId, chapter, () => { /* No Action */ });
}
handleChapterActionCallback(action: Action, chapter: Chapter) {
switch (action) {
case(Action.MarkAsRead):
this.markChapterAsRead(chapter);
break;
case(Action.MarkAsUnread):
this.markChapterAsUnread(chapter);
break;
default:
break;
}
}
readChapter(chapter: Chapter) {
if (chapter.pages === 0) {
this.toastr.error('There are no pages. Kavita was not able to read this archive.');
return;
}
if (chapter.files.length > 0 && chapter.files[0].format === MangaFormat.EPUB) {
this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'book', chapter.id]);
} else {
this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'manga', chapter.id]);
}
}
}