
* Adding multiple cases for comic naming conventions
* Changing "Chapter" to "Issue" for comic libraries
* Fixed an issue where the Parse method was using filename with extension to run regex matching, while it should be running on name without extension.
* Refactored to use Getter
* Cleaned up file to use conditional labelling rather than conditional html fragments
* Refactored code to properly check against library type for a given readinglist item
* Cleaned up series detail
* Conditionally remove special tags during parse
* Setup ParseInfoTests for ComicParserTests and also added unit tests from other comic issues created.
* Added more regex cases for naming patterns reported to be common with comics. Some cases added without regex.
* Pushing up changes
Fixed issue with cleanTitleTest.
Tried some patterns for "Cyberpunk 2077" but reverted
* Updated some cases and some spacing on Parser. Cyberpunk 2077 is not implemented as long as there is a # before issue number.
* Fixed the case for Special parsing on TPB. Fixed a piece of code that got deleted that prevented specials from rendering on volumes tab.
* Potential fix for parsing Cyberpunk 2077
- Added a ComicsSeriesSpecialCasesRegex and passed any filename that contains "Cyberpunk 2077" over to it so we can parse it separately. This could be used for any other potential problem series.
* Revert "Potential fix for parsing Cyberpunk 2077"
This reverts commit a14417e640
.
* Added more tests
* Refactored all places in Kavita to use Book, Issue, or Chapter depending on the Library type. Updated Volumes/Chapters to remove Volumes to make it cleaner.
* Removed some leftover test code
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
172 lines
6.5 KiB
TypeScript
172 lines
6.5 KiB
TypeScript
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 { 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';
|
|
import { LibraryType } from '../../../_models/library';
|
|
import { LibraryService } from '../../../_services/library.service';
|
|
|
|
|
|
|
|
@Component({
|
|
selector: 'app-card-details-modal',
|
|
templateUrl: './card-details-modal.component.html',
|
|
styleUrls: ['./card-details-modal.component.scss']
|
|
})
|
|
export class CardDetailsModalComponent implements OnInit {
|
|
|
|
@Input() parentName = '';
|
|
@Input() seriesId: number = 0;
|
|
@Input() libraryId: number = 0;
|
|
@Input() data!: any; // Volume | Chapter
|
|
isChapter = false;
|
|
chapters: Chapter[] = [];
|
|
seriesVolumes: any[] = [];
|
|
isLoadingVolumes = false;
|
|
formatKeys = Object.keys(MangaFormat);
|
|
/**
|
|
* If a cover image update occured.
|
|
*/
|
|
coverImageUpdate: boolean = false;
|
|
isAdmin: boolean = false;
|
|
actions: ActionItem<any>[] = [];
|
|
chapterActions: ActionItem<Chapter>[] = [];
|
|
libraryType: LibraryType = LibraryType.Manga;
|
|
|
|
get LibraryType(): typeof LibraryType {
|
|
return LibraryType;
|
|
}
|
|
|
|
constructor(private modalService: NgbModal, public modal: NgbActiveModal, public utilityService: UtilityService,
|
|
public imageService: ImageService, private uploadService: UploadService, private toastr: ToastrService,
|
|
private accountService: AccountService, private actionFactoryService: ActionFactoryService,
|
|
private actionService: ActionService, private router: Router, private libraryService: LibraryService) { }
|
|
|
|
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.libraryService.getLibraryType(this.libraryId).subscribe(type => {
|
|
this.libraryType = type;
|
|
});
|
|
|
|
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) => {
|
|
c.files.sort((a: MangaFile, b: MangaFile) => collator.compare(a.filePath, b.filePath));
|
|
});
|
|
}
|
|
|
|
close() {
|
|
this.modal.close({coverImageUpdate: this.coverImageUpdate});
|
|
}
|
|
|
|
formatChapterNumber(chapter: Chapter) {
|
|
if (chapter.number === '0') {
|
|
return '1';
|
|
}
|
|
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 ? '' : this.utilityService.formatChapterName(this.libraryType, false, true)) + chapter.range + '\'s Cover';
|
|
} else {
|
|
const volume = this.utilityService.asVolume(this.data);
|
|
const chapters = volume.chapters;
|
|
if (chapters && chapters.length > 0) {
|
|
modalRef.componentInstance.chapter = chapters[0];
|
|
modalRef.componentInstance.title = 'Select Volume ' + volume.number + '\'s Cover';
|
|
}
|
|
}
|
|
|
|
modalRef.closed.subscribe((closeResult: {success: boolean, chapter: Chapter, coverImageUpdate: boolean}) => {
|
|
if (closeResult.success) {
|
|
this.coverImageUpdate = closeResult.coverImageUpdate;
|
|
if (!this.coverImageUpdate) {
|
|
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]);
|
|
}
|
|
}
|
|
}
|