Comic Rework, New Scanner, Foundation Overahul (is this a full release?) (#2780)
This commit is contained in:
parent
d7e9e7c832
commit
7552c3f5fa
182 changed files with 27630 additions and 3046 deletions
|
|
@ -1,7 +1,8 @@
|
|||
import { MangaFile } from './manga-file';
|
||||
import { AgeRating } from './metadata/age-rating';
|
||||
|
||||
export const LooseLeafOrSpecialNumber = 0;
|
||||
export const LooseLeafOrDefaultNumber = -100000;
|
||||
export const SpecialVolumeNumber = 100000;
|
||||
|
||||
/**
|
||||
* Chapter table object. This does not have metadata on it, use ChapterMetadata which is the same Chapter but with those fields.
|
||||
|
|
@ -9,7 +10,12 @@ export const LooseLeafOrSpecialNumber = 0;
|
|||
export interface Chapter {
|
||||
id: number;
|
||||
range: string;
|
||||
/**
|
||||
* @deprecated Use minNumber/maxNumber
|
||||
*/
|
||||
number: string;
|
||||
minNumber: number;
|
||||
maxNumber: number;
|
||||
files: Array<MangaFile>;
|
||||
/**
|
||||
* This is used in the UI, it is not updated or sent to Backend
|
||||
|
|
@ -44,4 +50,5 @@ export interface Chapter {
|
|||
webLinks: string;
|
||||
isbn: string;
|
||||
lastReadingProgress: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ export enum LibraryType {
|
|||
Comic = 1,
|
||||
Book = 2,
|
||||
Images = 3,
|
||||
LightNovel = 4
|
||||
LightNovel = 4,
|
||||
ComicVine = 5
|
||||
}
|
||||
|
||||
export interface Library {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ export interface ChapterMetadata {
|
|||
count: number;
|
||||
totalCount: number;
|
||||
wordCount: number;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
genres: Array<Genre>;
|
||||
tags: Array<Tag>;
|
||||
|
|
@ -29,11 +29,14 @@ export interface ChapterMetadata {
|
|||
characters: Array<Person>;
|
||||
pencillers: Array<Person>;
|
||||
inkers: Array<Person>;
|
||||
imprints: Array<Person>;
|
||||
colorists: Array<Person>;
|
||||
letterers: Array<Person>;
|
||||
editors: Array<Person>;
|
||||
translators: Array<Person>;
|
||||
|
||||
teams: Array<Person>;
|
||||
locations: Array<Person>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
export enum PersonRole {
|
||||
Other = 1,
|
||||
Artist = 2,
|
||||
Writer = 3,
|
||||
Penciller = 4,
|
||||
Inker = 5,
|
||||
Colorist = 6,
|
||||
Letterer = 7,
|
||||
CoverArtist = 8,
|
||||
Editor = 9,
|
||||
Publisher = 10,
|
||||
Character = 11,
|
||||
Translator = 12
|
||||
Other = 1,
|
||||
Artist = 2,
|
||||
Writer = 3,
|
||||
Penciller = 4,
|
||||
Inker = 5,
|
||||
Colorist = 6,
|
||||
Letterer = 7,
|
||||
CoverArtist = 8,
|
||||
Editor = 9,
|
||||
Publisher = 10,
|
||||
Character = 11,
|
||||
Translator = 12,
|
||||
Imprint = 13,
|
||||
Team = 14,
|
||||
Location = 15
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
id: number;
|
||||
name: string;
|
||||
role: PersonRole;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,13 @@ export interface SeriesMetadata {
|
|||
characters: Array<Person>;
|
||||
pencillers: Array<Person>;
|
||||
inkers: Array<Person>;
|
||||
imprints: Array<Person>;
|
||||
colorists: Array<Person>;
|
||||
letterers: Array<Person>;
|
||||
editors: Array<Person>;
|
||||
translators: Array<Person>;
|
||||
teams: Array<Person>;
|
||||
locations: Array<Person>;
|
||||
ageRating: AgeRating;
|
||||
releaseYear: number;
|
||||
language: string;
|
||||
|
|
@ -40,10 +43,13 @@ export interface SeriesMetadata {
|
|||
characterLocked: boolean;
|
||||
pencillerLocked: boolean;
|
||||
inkerLocked: boolean;
|
||||
imprintLocked: boolean;
|
||||
coloristLocked: boolean;
|
||||
lettererLocked: boolean;
|
||||
editorLocked: boolean;
|
||||
translatorLocked: boolean;
|
||||
teamLocked: boolean;
|
||||
locationLocked: boolean;
|
||||
ageRatingLocked: boolean;
|
||||
releaseYearLocked: boolean;
|
||||
languageLocked: boolean;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ export enum FilterField
|
|||
FilePath = 25,
|
||||
WantToRead = 26,
|
||||
ReadingDate = 27,
|
||||
AverageRating = 28
|
||||
AverageRating = 28,
|
||||
Imprint = 29,
|
||||
Team = 30,
|
||||
Location = 31
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ export interface RelatedSeries {
|
|||
doujinshis: Array<Series>;
|
||||
parent: Array<Series>;
|
||||
editions: Array<Series>;
|
||||
annuals: Array<Series>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ export enum RelationKind {
|
|||
* This is UI only. Backend will generate Parent series for everything but Prequel/Sequel
|
||||
*/
|
||||
Parent = 12,
|
||||
Edition = 13
|
||||
Edition = 13,
|
||||
Annual = 14
|
||||
}
|
||||
|
||||
const RelationKindsUnsorted = [
|
||||
|
|
@ -22,6 +23,7 @@ const RelationKindsUnsorted = [
|
|||
{text: 'Sequel', value: RelationKind.Sequel},
|
||||
{text: 'Spin Off', value: RelationKind.SpinOff},
|
||||
{text: 'Adaptation', value: RelationKind.Adaptation},
|
||||
{text: 'Annual', value: RelationKind.Annual},
|
||||
{text: 'Alternative Setting', value: RelationKind.AlternativeSetting},
|
||||
{text: 'Alternative Version', value: RelationKind.AlternativeVersion},
|
||||
{text: 'Side Story', value: RelationKind.SideStory},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {TranslocoService} from "@ngneat/transloco";
|
|||
})
|
||||
export class DefaultDatePipe implements PipeTransform {
|
||||
|
||||
// TODO: Figure out how to translate Never
|
||||
constructor(private translocoService: TranslocoService) {
|
||||
}
|
||||
transform(value: any, replacementString = 'default-date-pipe.never'): string {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ export class FilterFieldPipe implements PipeTransform {
|
|||
return translate('filter-field-pipe.genres');
|
||||
case FilterField.Inker:
|
||||
return translate('filter-field-pipe.inker');
|
||||
case FilterField.Imprint:
|
||||
return translate('filter-field-pipe.imprint');
|
||||
case FilterField.Team:
|
||||
return translate('filter-field-pipe.team');
|
||||
case FilterField.Location:
|
||||
return translate('filter-field-pipe.location');
|
||||
case FilterField.Languages:
|
||||
return translate('filter-field-pipe.languages');
|
||||
case FilterField.Libraries:
|
||||
|
|
|
|||
|
|
@ -18,8 +18,14 @@ export class LibraryTypePipe implements PipeTransform {
|
|||
return this.translocoService.translate('library-type-pipe.book');
|
||||
case LibraryType.Comic:
|
||||
return this.translocoService.translate('library-type-pipe.comic');
|
||||
case LibraryType.ComicVine:
|
||||
return this.translocoService.translate('library-type-pipe.comicVine');
|
||||
case LibraryType.Images:
|
||||
return this.translocoService.translate('library-type-pipe.image');
|
||||
case LibraryType.Manga:
|
||||
return this.translocoService.translate('library-type-pipe.manga');
|
||||
case LibraryType.LightNovel:
|
||||
return this.translocoService.translate('library-type-pipe.lightNovel');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,16 @@ export class PersonRolePipe implements PipeTransform {
|
|||
return this.translocoService.translate('person-role-pipe.penciller');
|
||||
case PersonRole.Publisher:
|
||||
return this.translocoService.translate('person-role-pipe.publisher');
|
||||
case PersonRole.Imprint:
|
||||
return this.translocoService.translate('person-role-pipe.imprint');
|
||||
case PersonRole.Writer:
|
||||
return this.translocoService.translate('person-role-pipe.writer');
|
||||
case PersonRole.Team:
|
||||
return this.translocoService.translate('person-role-pipe.team');
|
||||
case PersonRole.Location:
|
||||
return this.translocoService.translate('person-role-pipe.location');
|
||||
case PersonRole.Translator:
|
||||
return this.translocoService.translate('person-role-pipe.translator');
|
||||
case PersonRole.Other:
|
||||
return this.translocoService.translate('person-role-pipe.other');
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ export class RelationshipPipe implements PipeTransform {
|
|||
return this.translocoService.translate('relationship-pipe.parent');
|
||||
case RelationKind.Edition:
|
||||
return this.translocoService.translate('relationship-pipe.edition');
|
||||
case RelationKind.Annual:
|
||||
return this.translocoService.translate('relationship-pipe.annual');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,10 +199,11 @@ export class SeriesService {
|
|||
updateRelationships(seriesId: number, adaptations: Array<number>, characters: Array<number>,
|
||||
contains: Array<number>, others: Array<number>, prequels: Array<number>,
|
||||
sequels: Array<number>, sideStories: Array<number>, spinOffs: Array<number>,
|
||||
alternativeSettings: Array<number>, alternativeVersions: Array<number>, doujinshis: Array<number>, editions: Array<number>) {
|
||||
alternativeSettings: Array<number>, alternativeVersions: Array<number>,
|
||||
doujinshis: Array<number>, editions: Array<number>, annuals: Array<number>) {
|
||||
return this.httpClient.post(this.baseUrl + 'series/update-related?seriesId=' + seriesId,
|
||||
{seriesId, adaptations, characters, sequels, prequels, contains, others, sideStories, spinOffs,
|
||||
alternativeSettings, alternativeVersions, doujinshis, editions});
|
||||
alternativeSettings, alternativeVersions, doujinshis, editions, annuals});
|
||||
}
|
||||
|
||||
getSeriesDetail(seriesId: number) {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,15 @@
|
|||
<td>
|
||||
<ng-container [ngSwitch]="item.scrobbleEventType">
|
||||
<ng-container *ngSwitchCase="ScrobbleEventType.ChapterRead">
|
||||
{{t('volume-and-chapter-num', {v: item.volumeNumber, n: item.chapterNumber})}}
|
||||
@if(item.volumeNumber === SpecialVolumeNumber) {
|
||||
{{t('chapter-num', {num: item.volumeNumber})}}
|
||||
} @else if (item.chapterNumber === LooseLeafOrDefaultNumber) {
|
||||
{{t('volume-num', {num: item.volumeNumber})}}
|
||||
} @else if (item.chapterNumber === LooseLeafOrDefaultNumber && item.volumeNumber === SpecialVolumeNumber) {
|
||||
|
||||
} @else {
|
||||
{{t('volume-and-chapter-num', {v: item.volumeNumber, n: item.chapterNumber})}}
|
||||
}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="ScrobbleEventType.ScoreUpdated">
|
||||
{{t('rating', {r: item.rating})}}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
|||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {LooseLeafOrDefaultNumber, SpecialVolumeNumber} from "../../_models/chapter";
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-scrobble-history',
|
||||
|
|
@ -101,4 +102,6 @@ export class UserScrobbleHistoryComponent implements OnInit {
|
|||
}
|
||||
|
||||
|
||||
protected readonly SpecialVolumeNumber = SpecialVolumeNumber;
|
||||
protected readonly LooseLeafOrDefaultNumber = LooseLeafOrDefaultNumber;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export class ManageLogsComponent implements OnInit, OnDestroy {
|
|||
this.hubConnection.on('SendLogAsObject', resp => {
|
||||
const payload = resp.arguments[0] as LogMessage;
|
||||
const logMessage = {timestamp: payload.timestamp, level: payload.level, message: payload.message, exception: payload.exception};
|
||||
// TODO: It might be better to just have a queue to show this
|
||||
// NOTE: It might be better to just have a queue to show this
|
||||
const values = this.logsSource.getValue();
|
||||
values.push(logMessage);
|
||||
this.logsSource.next(values);
|
||||
|
|
@ -60,7 +60,7 @@ export class ManageLogsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
// unsubscrbe from signalr connection
|
||||
// unsubscribe from signalr connection
|
||||
if (this.hubConnection) {
|
||||
this.hubConnection.stop().catch(err => console.error(err));
|
||||
console.log('Stoping log connection');
|
||||
|
|
|
|||
|
|
@ -229,6 +229,23 @@
|
|||
</app-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="imprint" class="form-label">{{t('imprint-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Imprint);metadata.publisherLocked = true" [settings]="getPersonsSettings(PersonRole.Imprint)"
|
||||
[(locked)]="metadata.imprintLocked" (onUnlock)="metadata.imprintLocked = false"
|
||||
(newItemAdded)="metadata.imprintLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
<ng-template #optionItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
</app-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="penciller" class="form-label">{{t('penciller-label')}}</label>
|
||||
|
|
@ -310,7 +327,21 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="translator" class="form-label">{{t('translator-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Translator);metadata.translatorLocked = true;" [settings]="getPersonsSettings(PersonRole.Translator)"
|
||||
[(locked)]="metadata.translatorLocked" (onUnlock)="metadata.translatorLocked = false"
|
||||
(newItemAdded)="metadata.translatorLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
<ng-template #optionItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
</app-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
|
|
@ -327,12 +358,29 @@
|
|||
</app-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="translator" class="form-label">{{t('translator-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Translator);metadata.translatorLocked = true;" [settings]="getPersonsSettings(PersonRole.Translator)"
|
||||
[(locked)]="metadata.translatorLocked" (onUnlock)="metadata.translatorLocked = false"
|
||||
(newItemAdded)="metadata.translatorLocked = true">
|
||||
<label for="team" class="form-label">{{t('team-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Character);metadata.teamLocked = true" [settings]="getPersonsSettings(PersonRole.Team)"
|
||||
[(locked)]="metadata.teamLocked" (onUnlock)="metadata.teamLocked = false"
|
||||
(newItemAdded)="metadata.teamLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
<ng-template #optionItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
</app-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="location" class="form-label">{{t('location-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Location);metadata.locationLocked = true" [settings]="getPersonsSettings(PersonRole.Location)"
|
||||
[(locked)]="metadata.locationLocked" (onUnlock)="metadata.locationLocked = false"
|
||||
(newItemAdded)="metadata.locationLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
|
|
@ -408,7 +456,7 @@
|
|||
<li class="d-flex my-4" *ngFor="let volume of seriesVolumes">
|
||||
<app-image class="me-3" style="width: 74px;" width="74px" [imageUrl]="imageService.getVolumeCoverImage(volume.id)"></app-image>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mt-0 mb-1">{{t('volume-num')}} {{volume.name}}</h5>
|
||||
<h5 class="mt-0 mb-1">{{formatVolumeName(volume)}}</h5>
|
||||
<div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
|
|
@ -432,7 +480,7 @@
|
|||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="volumeCollapsed[volume.name]">
|
||||
<ul class="list-group mt-2">
|
||||
<li *ngFor="let file of volume.volumeFiles.sort()" class="list-group-item">
|
||||
<li *ngFor="let file of volume.volumeFiles" class="list-group-item">
|
||||
<span>{{file.filePath}}</span>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { forkJoin, Observable, of } from 'rxjs';
|
|||
import { map } from 'rxjs/operators';
|
||||
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { TypeaheadSettings } from 'src/app/typeahead/_models/typeahead-settings';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import {Chapter, LooseLeafOrDefaultNumber, SpecialVolumeNumber} from 'src/app/_models/chapter';
|
||||
import { CollectionTag } from 'src/app/_models/collection-tag';
|
||||
import { Genre } from 'src/app/_models/metadata/genre';
|
||||
import { AgeRatingDto } from 'src/app/_models/metadata/age-rating-dto';
|
||||
|
|
@ -58,6 +58,7 @@ import {EditListComponent} from "../../../shared/edit-list/edit-list.component";
|
|||
import {AccountService} from "../../../_services/account.service";
|
||||
import {LibraryType} from "../../../_models/library/library";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {Volume} from "../../../_models/volume";
|
||||
|
||||
enum TabID {
|
||||
General = 0,
|
||||
|
|
@ -296,8 +297,10 @@ export class EditSeriesModalComponent implements OnInit {
|
|||
this.volumeCollapsed[v.name] = true;
|
||||
});
|
||||
this.seriesVolumes.forEach(vol => {
|
||||
vol.volumeFiles = vol.chapters?.sort(this.utilityService.sortChapters).map((c: Chapter) => c.files.map((f: any) => {
|
||||
f.chapter = c.number;
|
||||
//.sort(this.utilityService.sortChapters) (no longer needed, all data is sorted on the backend)
|
||||
vol.volumeFiles = vol.chapters?.map((c: Chapter) => c.files.map((f: any) => {
|
||||
// TODO: Identify how to fix this hack
|
||||
f.chapter = c.range;
|
||||
return f;
|
||||
})).flat();
|
||||
});
|
||||
|
|
@ -315,6 +318,15 @@ export class EditSeriesModalComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
formatVolumeName(volume: Volume) {
|
||||
if (volume.minNumber === LooseLeafOrDefaultNumber) {
|
||||
return translate('edit-series-modal.loose-leaf-volume');
|
||||
} else if (volume.minNumber === SpecialVolumeNumber) {
|
||||
return translate('edit-series-modal.specials-volume');
|
||||
}
|
||||
return translate('edit-series-modal.volume-num') + ' ' + volume.name;
|
||||
}
|
||||
|
||||
|
||||
setupTypeaheads() {
|
||||
forkJoin([
|
||||
|
|
@ -475,7 +487,10 @@ export class EditSeriesModalComponent implements OnInit {
|
|||
this.updateFromPreset('letterer', this.metadata.letterers, PersonRole.Letterer),
|
||||
this.updateFromPreset('penciller', this.metadata.pencillers, PersonRole.Penciller),
|
||||
this.updateFromPreset('publisher', this.metadata.publishers, PersonRole.Publisher),
|
||||
this.updateFromPreset('translator', this.metadata.translators, PersonRole.Translator)
|
||||
this.updateFromPreset('imprint', this.metadata.imprints, PersonRole.Imprint),
|
||||
this.updateFromPreset('translator', this.metadata.translators, PersonRole.Translator),
|
||||
this.updateFromPreset('teams', this.metadata.teams, PersonRole.Team),
|
||||
this.updateFromPreset('locations', this.metadata.locations, PersonRole.Location),
|
||||
]).pipe(map(results => {
|
||||
return of(true);
|
||||
}));
|
||||
|
|
@ -598,6 +613,10 @@ export class EditSeriesModalComponent implements OnInit {
|
|||
|
||||
updatePerson(persons: Person[], role: PersonRole) {
|
||||
switch (role) {
|
||||
case PersonRole.Other:
|
||||
break;
|
||||
case PersonRole.Artist:
|
||||
break;
|
||||
case PersonRole.CoverArtist:
|
||||
this.metadata.coverArtists = persons;
|
||||
break;
|
||||
|
|
@ -622,11 +641,22 @@ export class EditSeriesModalComponent implements OnInit {
|
|||
case PersonRole.Publisher:
|
||||
this.metadata.publishers = persons;
|
||||
break;
|
||||
case PersonRole.Imprint:
|
||||
this.metadata.imprints = persons;
|
||||
break;
|
||||
case PersonRole.Team:
|
||||
this.metadata.teams = persons;
|
||||
break;
|
||||
case PersonRole.Location:
|
||||
this.metadata.locations = persons;
|
||||
break;
|
||||
case PersonRole.Writer:
|
||||
this.metadata.writers = persons;
|
||||
break;
|
||||
case PersonRole.Translator:
|
||||
this.metadata.translators = persons;
|
||||
break;
|
||||
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
<ul class="list-unstyled">
|
||||
<li class="d-flex my-4" *ngFor="let chapter of chapters">
|
||||
<!-- TODO: Localize title -->
|
||||
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read {{utilityService.formatChapterName(libraryType, true, false)}} {{formatChapterNumber(chapter)}}">
|
||||
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read">
|
||||
<app-image class="me-2" width="74px" [imageUrl]="imageService.getChapterCoverImage(chapter.id)"></app-image>
|
||||
</a>
|
||||
<div class="flex-grow-1">
|
||||
|
|
@ -123,7 +123,7 @@
|
|||
<span>
|
||||
<app-card-actionables (actionHandler)="performAction($event, chapter)" [actions]="chapterActions"
|
||||
[labelBy]="utilityService.formatChapterName(libraryType, true, true) + formatChapterNumber(chapter)"></app-card-actionables>
|
||||
<ng-container *ngIf="chapter.number !== '0'; else specialHeader">
|
||||
<ng-container *ngIf="chapter.minNumber !== LooseLeafOrSpecialNumber; else specialHeader">
|
||||
{{utilityService.formatChapterName(libraryType, true, false) }} {{formatChapterNumber(chapter)}}
|
||||
</ng-container>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { ToastrService } from 'ngx-toastr';
|
|||
import { Observable, of, map, shareReplay } from 'rxjs';
|
||||
import { DownloadService } from 'src/app/shared/_services/download.service';
|
||||
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import {Chapter, LooseLeafOrDefaultNumber} from 'src/app/_models/chapter';
|
||||
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
|
||||
import { Device } from 'src/app/_models/device/device';
|
||||
import { LibraryType } from 'src/app/_models/library/library';
|
||||
|
|
@ -74,6 +74,7 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||
protected readonly Breakpoint = Breakpoint;
|
||||
protected readonly LibraryType = LibraryType;
|
||||
protected readonly TabID = TabID;
|
||||
protected readonly LooseLeafOrSpecialNumber = LooseLeafOrDefaultNumber;
|
||||
|
||||
@Input() parentName = '';
|
||||
@Input() seriesId: number = 0;
|
||||
|
|
@ -182,10 +183,10 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||
}
|
||||
|
||||
formatChapterNumber(chapter: Chapter) {
|
||||
if (chapter.number === '0') {
|
||||
if (chapter.minNumber === LooseLeafOrDefaultNumber) {
|
||||
return '1';
|
||||
}
|
||||
return chapter.number;
|
||||
return chapter.range + '';
|
||||
}
|
||||
|
||||
performAction(action: ActionItem<any>, chapter: Chapter) {
|
||||
|
|
@ -281,5 +282,4 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,13 +198,14 @@ export class CardItemComponent implements OnInit {
|
|||
this.format = (this.entity as Series).format;
|
||||
|
||||
if (this.utilityService.isChapter(this.entity)) {
|
||||
const chapterTitle = this.utilityService.asChapter(this.entity).titleName;
|
||||
const chapter = this.utilityService.asChapter(this.entity);
|
||||
const chapterTitle = chapter.titleName;
|
||||
if (chapterTitle === '' || chapterTitle === null || chapterTitle === undefined) {
|
||||
const volumeTitle = this.utilityService.asChapter(this.entity).volumeTitle
|
||||
const volumeTitle = chapter.volumeTitle
|
||||
if (volumeTitle === '' || volumeTitle === null || volumeTitle === undefined) {
|
||||
this.tooltipTitle = (this.title).trim();
|
||||
} else {
|
||||
this.tooltipTitle = (this.utilityService.asChapter(this.entity).volumeTitle + ' ' + this.title).trim();
|
||||
this.tooltipTitle = (volumeTitle + ' ' + this.title).trim();
|
||||
}
|
||||
} else {
|
||||
this.tooltipTitle = chapterTitle;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
&& chapter.pencillers.length === 0 && chapter.inkers.length === 0
|
||||
&& chapter.colorists.length === 0 && chapter.letterers.length === 0
|
||||
&& chapter.editors.length === 0 && chapter.publishers.length === 0
|
||||
&& chapter.characters.length === 0 && chapter.translators.length === 0">
|
||||
&& chapter.characters.length === 0 && chapter.translators.length === 0
|
||||
&& chapter.imprints.length === 0 && chapter.locations.length === 0
|
||||
&& chapter.teams.length === 0">
|
||||
{{t('no-data')}}
|
||||
</span>
|
||||
<div class="row g-0">
|
||||
<div class="container-flex row row-cols-auto row-cols-lg-5 g-2 g-lg-3 me-0 mt-2">
|
||||
<div class="col-auto mt-2" *ngIf="chapter.writers && chapter.writers.length > 0">
|
||||
<h6>{{t('writers-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.writers">
|
||||
|
|
@ -81,6 +83,15 @@
|
|||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="col-auto mt-2" *ngIf="chapter.imprints && chapter.imprints.length > 0">
|
||||
<h6>{{t('imprints-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.imprints">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx">
|
||||
<app-person-badge [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="col-auto mt-2" *ngIf="chapter.characters && chapter.characters.length > 0">
|
||||
<h6>{{t('characters-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.characters">
|
||||
|
|
@ -89,6 +100,25 @@
|
|||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="col-auto mt-2" *ngIf="chapter.teams && chapter.teams.length > 0">
|
||||
<h6>{{t('teams-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.teams">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx">
|
||||
<app-person-badge [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="col-auto mt-2" *ngIf="chapter.locations && chapter.locations.length > 0">
|
||||
<h6>{{t('locations-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.locations">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx">
|
||||
<app-person-badge [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="col-auto mt-2" *ngIf="chapter.translators && chapter.translators.length > 0">
|
||||
<h6>{{t('translators-title')}}</h6>
|
||||
<app-badge-expander [items]="chapter.translators">
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export class EditSeriesRelationComponent implements OnInit {
|
|||
focusTypeahead = new EventEmitter();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seriesService.getRelatedForSeries(this.series.id).subscribe(async relations => {
|
||||
this.seriesService.getRelatedForSeries(this.series.id).subscribe( relations => {
|
||||
this.setupRelationRows(relations.prequels, RelationKind.Prequel);
|
||||
this.setupRelationRows(relations.sequels, RelationKind.Sequel);
|
||||
this.setupRelationRows(relations.sideStories, RelationKind.SideStory);
|
||||
|
|
@ -85,6 +85,7 @@ export class EditSeriesRelationComponent implements OnInit {
|
|||
this.setupRelationRows(relations.contains, RelationKind.Contains);
|
||||
this.setupRelationRows(relations.parent, RelationKind.Parent);
|
||||
this.setupRelationRows(relations.editions, RelationKind.Edition);
|
||||
this.setupRelationRows(relations.annuals, RelationKind.Annual);
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
|
||||
|
|
@ -181,9 +182,10 @@ export class EditSeriesRelationComponent implements OnInit {
|
|||
const alternativeVersions = this.relations.filter(item => (parseInt(item.formControl.value, 10) as RelationKind) === RelationKind.AlternativeVersion && item.series !== undefined).map(item => item.series!.id);
|
||||
const doujinshis = this.relations.filter(item => (parseInt(item.formControl.value, 10) as RelationKind) === RelationKind.Doujinshi && item.series !== undefined).map(item => item.series!.id);
|
||||
const editions = this.relations.filter(item => (parseInt(item.formControl.value, 10) as RelationKind) === RelationKind.Edition && item.series !== undefined).map(item => item.series!.id);
|
||||
const annuals = this.relations.filter(item => (parseInt(item.formControl.value, 10) as RelationKind) === RelationKind.Annual && item.series !== undefined).map(item => item.series!.id);
|
||||
|
||||
// NOTE: We can actually emit this onto an observable and in main parent, use mergeMap into the forkJoin
|
||||
this.seriesService.updateRelationships(this.series.id, adaptations, characters, contains, others, prequels, sequels, sideStories, spinOffs, alternativeSettings, alternativeVersions, doujinshis, editions).subscribe(() => {});
|
||||
this.seriesService.updateRelationships(this.series.id, adaptations, characters, contains, others, prequels, sequels, sideStories, spinOffs, alternativeSettings, alternativeVersions, doujinshis, editions, annuals).subscribe(() => {});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,25 @@
|
|||
<ng-template #fullComicTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{Number !== LooseLeafOrSpecialNumber ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{Number !== LooseLeafOrSpecialNumber ? (isChapter ? t('issue-num') + Number : volumeTitle) : t('special')}}
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter ? t('issue-num') + Number : volumeTitle) : t('special')}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="LibraryType.ComicVine">
|
||||
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullComicTitle">
|
||||
{{titleName}}
|
||||
</ng-container>
|
||||
<ng-template #fullComicTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter ? t('issue-num') + Number : volumeTitle) : t('special')}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="LibraryType.Manga">
|
||||
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullMangaTitle">
|
||||
{{titleName}}
|
||||
|
|
@ -19,9 +33,9 @@
|
|||
<ng-template #fullMangaTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{Number !== LooseLeafOrSpecialNumber ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{Number !== LooseLeafOrSpecialNumber ? (isChapter ? (t('chapter') + ' ') + Number : volumeTitle) : t('special')}}
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter ? (t('chapter') + ' ') + Number : volumeTitle) : t('special')}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Book">
|
||||
|
|
@ -30,5 +44,8 @@
|
|||
<ng-container *ngSwitchCase="LibraryType.LightNovel">
|
||||
{{volumeTitle}}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Images">
|
||||
{{Number !== LooseLeafOrSpecial ? (isChapter ? (t('chapter') + ' ') + Number : volumeTitle) : t('special')}}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Chapter, LooseLeafOrSpecialNumber } from 'src/app/_models/chapter';
|
||||
import { Chapter, LooseLeafOrDefaultNumber } from 'src/app/_models/chapter';
|
||||
import { LibraryType } from 'src/app/_models/library/library';
|
||||
import { Volume } from 'src/app/_models/volume';
|
||||
import {CommonModule, NgSwitch} from "@angular/common";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
|
||||
/**
|
||||
* This is primarily used for list item
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-entity-title',
|
||||
standalone: true,
|
||||
|
|
@ -20,7 +23,9 @@ import {TranslocoModule} from "@ngneat/transloco";
|
|||
})
|
||||
export class EntityTitleComponent implements OnInit {
|
||||
|
||||
protected readonly LooseLeafOrSpecialNumber = LooseLeafOrSpecialNumber;
|
||||
protected readonly LooseLeafOrSpecialNumber = LooseLeafOrDefaultNumber;
|
||||
protected readonly LooseLeafOrSpecial = LooseLeafOrDefaultNumber + "";
|
||||
protected readonly LibraryType = LibraryType;
|
||||
|
||||
/**
|
||||
* Library type for which the entity belongs
|
||||
|
|
@ -42,19 +47,18 @@ export class EntityTitleComponent implements OnInit {
|
|||
volumeTitle: string = '';
|
||||
|
||||
get Number() {
|
||||
if (this.utilityService.isVolume(this.entity)) return (this.entity as Volume).minNumber;
|
||||
return (this.entity as Chapter).number;
|
||||
if (this.isChapter) return (this.entity as Chapter).range;
|
||||
return (this.entity as Volume).name;
|
||||
}
|
||||
|
||||
get LibraryType() {
|
||||
return LibraryType;
|
||||
}
|
||||
|
||||
constructor(private utilityService: UtilityService, private readonly cdRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isChapter = this.utilityService.isChapter(this.entity);
|
||||
|
||||
|
||||
|
||||
if (this.isChapter) {
|
||||
const c = (this.entity as Chapter);
|
||||
this.volumeTitle = c.volumeTitle || '';
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ const DropdownFields = [FilterField.PublicationStatus, FilterField.Languages, Fi
|
|||
FilterField.Editor, FilterField.CoverArtist, FilterField.Letterer,
|
||||
FilterField.Colorist, FilterField.Inker, FilterField.Penciller,
|
||||
FilterField.Writers, FilterField.Genres, FilterField.Libraries,
|
||||
FilterField.Formats, FilterField.CollectionTags, FilterField.Tags
|
||||
FilterField.Formats, FilterField.CollectionTags, FilterField.Tags,
|
||||
FilterField.Imprint, FilterField.Team, FilterField.Location
|
||||
];
|
||||
const BooleanFields = [FilterField.WantToRead];
|
||||
const DateFields = [FilterField.ReadingDate];
|
||||
|
|
@ -297,6 +298,9 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
case FilterField.Letterer: return this.getPersonOptions(PersonRole.Letterer);
|
||||
case FilterField.Penciller: return this.getPersonOptions(PersonRole.Penciller);
|
||||
case FilterField.Publisher: return this.getPersonOptions(PersonRole.Publisher);
|
||||
case FilterField.Imprint: return this.getPersonOptions(PersonRole.Imprint);
|
||||
case FilterField.Team: return this.getPersonOptions(PersonRole.Imprint);
|
||||
case FilterField.Location: return this.getPersonOptions(PersonRole.Imprint);
|
||||
case FilterField.Translators: return this.getPersonOptions(PersonRole.Translator);
|
||||
case FilterField.Writers: return this.getPersonOptions(PersonRole.Writer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@
|
|||
<ng-container *ngIf="item.files.length > 0">
|
||||
<app-series-format [format]="item.files?.[0].format"></app-series-format>
|
||||
</ng-container>
|
||||
<span>{{item.titleName}}</span>
|
||||
<!-- TODO: this needs the series name before the chapter issue -->
|
||||
<span>{{item.titleName || item.range}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
|||
|
|
@ -148,6 +148,8 @@ export class NavHeaderComponent implements OnInit {
|
|||
this.clearSearch();
|
||||
filter = filter + '';
|
||||
switch(role) {
|
||||
case PersonRole.Other:
|
||||
break;
|
||||
case PersonRole.Writer:
|
||||
this.goTo({field: FilterField.Writers, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
|
|
@ -178,9 +180,19 @@ export class NavHeaderComponent implements OnInit {
|
|||
case PersonRole.Publisher:
|
||||
this.goTo({field: FilterField.Publisher, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
case PersonRole.Imprint:
|
||||
this.goTo({field: FilterField.Imprint, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
case PersonRole.Team:
|
||||
this.goTo({field: FilterField.Team, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
case PersonRole.Location:
|
||||
this.goTo({field: FilterField.Location, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
case PersonRole.Translator:
|
||||
this.goTo({field: FilterField.Translators, comparison: FilterComparison.Equal, value: filter});
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,40 +21,20 @@
|
|||
<ng-container *ngIf="currentStepIndex === Step.Validate">
|
||||
<p>{{t('validate-description')}}</p>
|
||||
<div class="row g-0">
|
||||
|
||||
<div ngbAccordion #accordion="ngbAccordion">
|
||||
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
|
||||
<div ngbAccordionItem *ngIf="fileToProcess.validateSummary as summary">
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
@if(summary.results.length > 0) {
|
||||
<h5>{{t('validate-warning')}}</h5>
|
||||
<ol class="list-group list-group-numbered list-group-flush" >
|
||||
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
|
||||
[innerHTML]="result | cblConflictReason | safeHtml">
|
||||
</li>
|
||||
</ol>
|
||||
} @else {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-no-issue')}}
|
||||
</div>
|
||||
</div>
|
||||
{{t('validate-no-issue-description')}}
|
||||
</div>
|
||||
}
|
||||
<div ngbAccordionItem *ngIf="fileToProcess.validateSummary as summary">
|
||||
<h5 ngbAccordionHeader>
|
||||
<button ngbAccordionButton>
|
||||
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
|
||||
</button>
|
||||
</h5>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-container [ngTemplateOutlet]="validationList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -105,6 +85,38 @@
|
|||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #validationList let-summary="summary">
|
||||
@if (summary.results.length > 0) {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-triangle-exclamation" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-warning')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="list-group list-group-numbered list-group-flush" >
|
||||
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
|
||||
[innerHTML]="result | cblConflictReason | safeHtml">
|
||||
</li>
|
||||
</ol>
|
||||
}
|
||||
@else {
|
||||
<div class="justify-content-center col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
{{t('validate-no-issue-description')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #resultsList let-summary="summary">
|
||||
<ul class="list-group list-group-flush">
|
||||
@for(result of summary.results; track result.order) {
|
||||
|
|
@ -115,23 +127,46 @@
|
|||
</ng-template>
|
||||
|
||||
<ng-template #heading let-filename="filename" let-summary="summary">
|
||||
<ng-container *ngIf="summary.success | cblImportResult as success">
|
||||
<ng-container [ngSwitch]="summary.success">
|
||||
<span *ngSwitchCase="CblImportResult.Success" class="badge bg-primary me-1">{{success}}</span>
|
||||
<span *ngSwitchCase="CblImportResult.Fail" class="badge bg-danger me-1">{{success}}</span>
|
||||
<span *ngSwitchCase="CblImportResult.Partial" class="badge bg-warning me-1">{{success}}</span>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<span>{{filename}}<span *ngIf="summary.cblName">: ({{summary.cblName}})</span></span>
|
||||
@switch (summary.success) {
|
||||
@case (CblImportResult.Success) {
|
||||
<span class="badge heading-badge bg-primary me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Fail) {
|
||||
<span class="badge heading-badge bg-danger me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
@case (CblImportResult.Partial) {
|
||||
<span class="badge heading-badge bg-warning me-1">{{summary.success | cblImportResult}}</span>
|
||||
}
|
||||
}
|
||||
<span>{{filename}}<span *ngIf="summary.cblName">: ({{summary.cblName}})</span></span>
|
||||
</ng-template>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-icon" href="https://wiki.kavitareader.com/en/guides/get-started-using-your-library/reading-lists#creating-a-reading-list-via-cbl" target="_blank" rel="noopener noreferrer">Help</a>
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">{{t('close')}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="prevStep()" [disabled]="!canMoveToPrevStep()">{{t('prev')}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="!canMoveToNextStep()">{{t(NextButtonLabel)}}</button>
|
||||
<form [formGroup]="cblSettingsForm" class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="settings-comicvine-mode" role="switch" formControlName="comicVineMatching" class="form-check-input"
|
||||
aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="settings-comicvine-mode">{{t('comicvine-parsing-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Spacer -->
|
||||
<div class="col" aria-hidden="true"></div>
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-icon" href="https://wiki.kavitareader.com/en/guides/get-started-using-your-library/reading-lists#creating-a-reading-list-via-cbl" target="_blank" rel="noopener noreferrer">Help</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">{{t('close')}}</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" (click)="prevStep()" [disabled]="!canMoveToPrevStep()">{{t('prev')}}</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="!canMoveToNextStep()">{{t(NextButtonLabel)}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.heading-badge {
|
||||
color: var(--bs-badge-color);
|
||||
}
|
||||
|
||||
::ng-deep .file-info {
|
||||
width: 83%;
|
||||
float: left;
|
||||
|
|
@ -38,4 +42,4 @@ file-upload {
|
|||
|
||||
::ng-deep .reading-list-fail--item {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,12 @@ enum Step {
|
|||
})
|
||||
export class ImportCblModalComponent {
|
||||
|
||||
protected readonly CblImportResult = CblImportResult;
|
||||
protected readonly Step = Step;
|
||||
|
||||
@ViewChild('fileUpload') fileUpload!: ElementRef<HTMLInputElement>;
|
||||
|
||||
|
||||
fileUploadControl = new FormControl<undefined | Array<File>>(undefined, [
|
||||
FileUploadValidators.accept(['.cbl']),
|
||||
]);
|
||||
|
|
@ -55,6 +59,9 @@ export class ImportCblModalComponent {
|
|||
uploadForm = new FormGroup({
|
||||
files: this.fileUploadControl
|
||||
});
|
||||
cblSettingsForm = new FormGroup({
|
||||
comicVineMatching: new FormControl(true, [])
|
||||
});
|
||||
|
||||
isLoading: boolean = false;
|
||||
|
||||
|
|
@ -70,10 +77,6 @@ export class ImportCblModalComponent {
|
|||
failedFiles: Array<FileStep> = [];
|
||||
|
||||
|
||||
get Breakpoint() { return Breakpoint; }
|
||||
get Step() { return Step; }
|
||||
get CblImportResult() { return CblImportResult; }
|
||||
|
||||
get NextButtonLabel() {
|
||||
switch(this.currentStepIndex) {
|
||||
case Step.DryRun:
|
||||
|
|
@ -105,11 +108,12 @@ export class ImportCblModalComponent {
|
|||
return;
|
||||
}
|
||||
// Load each file into filesToProcess and group their data
|
||||
let pages = [];
|
||||
const pages = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const formData = new FormData();
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', true + '');
|
||||
formData.append('dryRun', 'true');
|
||||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.validateCbl(formData));
|
||||
}
|
||||
forkJoin(pages).subscribe(results => {
|
||||
|
|
@ -195,12 +199,13 @@ export class ImportCblModalComponent {
|
|||
const filenamesAllowedToProcess = this.filesToProcess.map(p => p.fileName);
|
||||
const files = (this.uploadForm.get('files')?.value || []).filter(f => filenamesAllowedToProcess.includes(f.name));
|
||||
|
||||
let pages = [];
|
||||
const pages = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const formData = new FormData();
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', 'true');
|
||||
pages.push(this.readingListService.importCbl(formData));
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', 'true');
|
||||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.importCbl(formData));
|
||||
}
|
||||
forkJoin(pages).subscribe(results => {
|
||||
results.forEach(cblImport => {
|
||||
|
|
@ -224,6 +229,7 @@ export class ImportCblModalComponent {
|
|||
const formData = new FormData();
|
||||
formData.append('cbl', files[i]);
|
||||
formData.append('dryRun', 'false');
|
||||
formData.append('comicVineMatching', this.cblSettingsForm.get('comicVineMatching')?.value + '');
|
||||
pages.push(this.readingListService.importCbl(formData));
|
||||
}
|
||||
forkJoin(pages).subscribe(results => {
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@
|
|||
<ng-container *ngIf="nextExpectedChapter">
|
||||
<ng-container [ngSwitch]="tabId">
|
||||
<ng-container *ngSwitchCase="TabID.Volumes">
|
||||
<app-next-expected-card *ngIf="nextExpectedChapter.volumeNumber > 0 && nextExpectedChapter.chapterNumber === LooseLeafOrSpecialNumber"
|
||||
<app-next-expected-card *ngIf="nextExpectedChapter.volumeNumber !== SpecialVolumeNumber && nextExpectedChapter.chapterNumber === LooseLeafOrSpecialNumber"
|
||||
class="col-auto mt-2 mb-2" [entity]="nextExpectedChapter"
|
||||
[imageUrl]="imageService.getSeriesCoverImage(series.id)"></app-next-expected-card>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ import {
|
|||
import {TagBadgeCursor} from 'src/app/shared/tag-badge/tag-badge.component';
|
||||
import {DownloadEvent, DownloadService} from 'src/app/shared/_services/download.service';
|
||||
import {KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
|
||||
import {Chapter} from 'src/app/_models/chapter';
|
||||
import {Chapter, LooseLeafOrDefaultNumber, SpecialVolumeNumber} from 'src/app/_models/chapter';
|
||||
import {Device} from 'src/app/_models/device/device';
|
||||
import {ScanSeriesEvent} from 'src/app/_models/events/scan-series-event';
|
||||
import {SeriesRemovedEvent} from 'src/app/_models/events/series-removed-event';
|
||||
|
|
@ -67,7 +67,6 @@ import {RelationKind} from 'src/app/_models/series-detail/relation-kind';
|
|||
import {SeriesMetadata} from 'src/app/_models/metadata/series-metadata';
|
||||
import {User} from 'src/app/_models/user';
|
||||
import {Volume} from 'src/app/_models/volume';
|
||||
import {LooseLeafOrSpecialNumber} from 'src/app/_models/chapter';
|
||||
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';
|
||||
|
|
@ -184,7 +183,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
protected readonly PageLayoutMode = PageLayoutMode;
|
||||
protected readonly TabID = TabID;
|
||||
protected readonly TagBadgeCursor = TagBadgeCursor;
|
||||
protected readonly LooseLeafOrSpecialNumber = LooseLeafOrSpecialNumber;
|
||||
protected readonly LooseLeafOrSpecialNumber = LooseLeafOrDefaultNumber;
|
||||
protected readonly SpecialVolumeNumber = SpecialVolumeNumber;
|
||||
|
||||
@ViewChild('scrollingBlock') scrollingBlock: ElementRef<HTMLDivElement> | undefined;
|
||||
@ViewChild('companionBar') companionBar: ElementRef<HTMLDivElement> | undefined;
|
||||
|
|
@ -241,7 +241,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
/**
|
||||
* Track by function for Chapter to tell when to refresh card data
|
||||
*/
|
||||
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.number}_${item.volumeId}_${item.pagesRead}`;
|
||||
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.minNumber}_${item.maxNumber}_${item.volumeId}_${item.pagesRead}`;
|
||||
trackByRelatedSeriesIdentify = (index: number, item: RelatedSeriesPair) => `${item.series.name}_${item.series.libraryId}_${item.series.pagesRead}_${item.relation}`;
|
||||
trackBySeriesIdentify = (index: number, item: Series) => `${item.name}_${item.libraryId}_${item.pagesRead}`;
|
||||
trackByStoryLineIdentity = (index: number, item: StoryLineItem) => {
|
||||
|
|
@ -338,12 +338,20 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
}
|
||||
|
||||
get ShowStorylineTab() {
|
||||
return (this.libraryType !== LibraryType.Book && this.libraryType !== LibraryType.LightNovel) && (this.volumes.length > 0 || this.chapters.length > 0);
|
||||
if (this.libraryType === LibraryType.ComicVine) return false;
|
||||
return (this.libraryType !== LibraryType.Book && this.libraryType !== LibraryType.LightNovel && this.libraryType !== LibraryType.Comic)
|
||||
&& (this.volumes.length > 0 || this.chapters.length > 0);
|
||||
}
|
||||
|
||||
get ShowVolumeTab() {
|
||||
if (this.libraryType === LibraryType.ComicVine) {
|
||||
if (this.volumes.length > 1) return true;
|
||||
if (this.specials.length === 0 && this.chapters.length === 0) return true;
|
||||
return false;
|
||||
}
|
||||
return this.volumes.length > 0;
|
||||
}
|
||||
|
||||
get ShowChaptersTab() {
|
||||
return this.chapters.length > 0;
|
||||
}
|
||||
|
|
@ -371,13 +379,13 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
|
||||
// This is a lone chapter
|
||||
if (vol.length === 0) {
|
||||
return 'Ch ' + this.currentlyReadingChapter.number;
|
||||
return 'Ch ' + this.currentlyReadingChapter.minNumber; // TODO: Refactor this to use DisplayTitle (or Range) and Localize it
|
||||
}
|
||||
|
||||
if (this.currentlyReadingChapter.number === "0") {
|
||||
if (this.currentlyReadingChapter.minNumber === LooseLeafOrDefaultNumber) {
|
||||
return 'Vol ' + vol[0].minNumber;
|
||||
}
|
||||
return 'Vol ' + vol[0].minNumber + ' Ch ' + this.currentlyReadingChapter.number;
|
||||
return 'Vol ' + vol[0].minNumber + ' Ch ' + this.currentlyReadingChapter.minNumber;
|
||||
}
|
||||
|
||||
return this.currentlyReadingChapter.title;
|
||||
|
|
@ -661,6 +669,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
...relations.doujinshis.map(item => this.createRelatedSeries(item, RelationKind.Doujinshi)),
|
||||
...relations.parent.map(item => this.createRelatedSeries(item, RelationKind.Parent)),
|
||||
...relations.editions.map(item => this.createRelatedSeries(item, RelationKind.Edition)),
|
||||
...relations.annuals.map(item => this.createRelatedSeries(item, RelationKind.Annual)),
|
||||
];
|
||||
if (this.relations.length > 0) {
|
||||
this.hasRelations = true;
|
||||
|
|
@ -729,7 +738,16 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
|||
if (this.volumes.length === 0 && this.chapters.length === 0 && this.specials.length > 0) {
|
||||
this.activeTabId = TabID.Specials;
|
||||
} else {
|
||||
this.activeTabId = TabID.Storyline;
|
||||
if (this.libraryType == LibraryType.Comic || this.libraryType == LibraryType.ComicVine) {
|
||||
if (this.chapters.length === 0) {
|
||||
this.activeTabId = TabID.Specials;
|
||||
} else {
|
||||
this.activeTabId = TabID.Chapters;
|
||||
}
|
||||
} else {
|
||||
this.activeTabId = TabID.Storyline;
|
||||
}
|
||||
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,15 +76,6 @@
|
|||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" id="extended-series-metadata">
|
||||
|
||||
<!-- @if (libraryType === LibraryType.Comic || libraryType === LibraryType.Images) {-->
|
||||
<!-- <app-metadata-detail [tags]="seriesMetadata.writers" [libraryId]="series.libraryId" [queryParam]="FilterField.Writers" [heading]="t('writers-title')">-->
|
||||
<!-- <ng-template #itemTemplate let-item>-->
|
||||
<!-- <app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
<!-- -->
|
||||
<!-- }-->
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.coverArtists" [libraryId]="series.libraryId" [queryParam]="FilterField.CoverArtist" [heading]="t('cover-artists-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
|
|
@ -92,26 +83,6 @@
|
|||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
|
||||
<!-- <app-metadata-detail [tags]="seriesMetadata.writers" [libraryId]="series.libraryId" [queryParam]="FilterField.Writers" [heading]="t('writers-title')">-->
|
||||
<!-- <ng-template #itemTemplate let-item>-->
|
||||
<!-- <app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
|
||||
<!-- <app-metadata-detail [tags]="seriesMetadata.coverArtists" [libraryId]="series.libraryId" [queryParam]="FilterField.CoverArtist" [heading]="t('cover-artists-title')">-->
|
||||
<!-- <ng-template #itemTemplate let-item>-->
|
||||
<!-- <app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.characters" [libraryId]="series.libraryId" [queryParam]="FilterField.Characters" [heading]="t('characters-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.colorists" [libraryId]="series.libraryId" [queryParam]="FilterField.Colorist" [heading]="t('colorists-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
|
|
@ -136,24 +107,48 @@
|
|||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.translators" [libraryId]="series.libraryId" [queryParam]="FilterField.Translators" [heading]="t('translators-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.pencillers" [libraryId]="series.libraryId" [queryParam]="FilterField.Penciller" [heading]="t('pencillers-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.characters" [libraryId]="series.libraryId" [queryParam]="FilterField.Characters" [heading]="t('characters-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.teams" [libraryId]="series.libraryId" [queryParam]="FilterField.Team" [heading]="t('teams-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.locations" [libraryId]="series.libraryId" [queryParam]="FilterField.Location" [heading]="t('locations-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.publishers" [libraryId]="series.libraryId" [queryParam]="FilterField.Publisher" [heading]="t('publishers-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.imprints" [libraryId]="series.libraryId" [queryParam]="FilterField.Imprint" [heading]="t('imprints-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="seriesMetadata.translators" [libraryId]="series.libraryId" [queryParam]="FilterField.Translators" [heading]="t('translators-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-person-badge a11y-click="13,32" class="col-auto" [person]="item"></app-person-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
Component,
|
||||
inject,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnChanges, OnInit,
|
||||
SimpleChanges,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
|
|
@ -46,7 +46,7 @@ import {Rating} from "../../../_models/rating";
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class SeriesMetadataDetailComponent implements OnChanges {
|
||||
export class SeriesMetadataDetailComponent implements OnChanges, OnInit {
|
||||
|
||||
protected readonly imageService = inject(ImageService);
|
||||
protected readonly utilityService = inject(UtilityService);
|
||||
|
|
@ -83,13 +83,26 @@ export class SeriesMetadataDetailComponent implements OnChanges {
|
|||
return this.seriesMetadata?.webLinks.split(',') || [];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
ngOnInit() {
|
||||
// If on desktop, we can just have all the data expanded by default:
|
||||
this.isCollapsed = this.utilityService.getActiveBreakpoint() < Breakpoint.Desktop;
|
||||
// Check if there is a lot of extended data, if so, re-collapse
|
||||
const sum = (this.seriesMetadata.colorists.length + this.seriesMetadata.editors.length
|
||||
+ this.seriesMetadata.coverArtists.length + this.seriesMetadata.inkers.length
|
||||
+ this.seriesMetadata.letterers.length + this.seriesMetadata.pencillers.length
|
||||
+ this.seriesMetadata.publishers.length + this.seriesMetadata.characters.length
|
||||
+ this.seriesMetadata.imprints.length + this.seriesMetadata.translators.length
|
||||
+ this.seriesMetadata.writers.length + this.seriesMetadata.teams.length + this.seriesMetadata.locations.length) / 13;
|
||||
if (sum > 10) {
|
||||
this.isCollapsed = true;
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
|
||||
|
||||
|
||||
this.hasExtendedProperties = this.seriesMetadata.colorists.length > 0 ||
|
||||
this.seriesMetadata.editors.length > 0 ||
|
||||
this.seriesMetadata.coverArtists.length > 0 ||
|
||||
|
|
@ -98,7 +111,11 @@ export class SeriesMetadataDetailComponent implements OnChanges {
|
|||
this.seriesMetadata.pencillers.length > 0 ||
|
||||
this.seriesMetadata.publishers.length > 0 ||
|
||||
this.seriesMetadata.characters.length > 0 ||
|
||||
this.seriesMetadata.translators.length > 0;
|
||||
this.seriesMetadata.imprints.length > 0 ||
|
||||
this.seriesMetadata.teams.length > 0 ||
|
||||
this.seriesMetadata.locations.length > 0 ||
|
||||
this.seriesMetadata.translators.length > 0
|
||||
;
|
||||
|
||||
|
||||
this.seriesSummary = (this.seriesMetadata?.summary === null ? '' : this.seriesMetadata.summary).replace(/\n/g, '<br>');
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export class DownloadService {
|
|||
case 'volume':
|
||||
return (downloadEntity as Volume).minNumber + '';
|
||||
case 'chapter':
|
||||
return (downloadEntity as Chapter).number;
|
||||
return (downloadEntity as Chapter).minNumber + '';
|
||||
case 'bookmark':
|
||||
return '';
|
||||
case 'logs':
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class UtilityService {
|
|||
|
||||
|
||||
sortChapters = (a: Chapter, b: Chapter) => {
|
||||
return parseFloat(a.number) - parseFloat(b.number);
|
||||
return a.minNumber - b.minNumber;
|
||||
}
|
||||
|
||||
mangaFormatToText(format: MangaFormat): string {
|
||||
|
|
@ -67,6 +67,7 @@ export class UtilityService {
|
|||
case LibraryType.LightNovel:
|
||||
return this.translocoService.translate('common.book-num') + (includeSpace ? ' ' : '');
|
||||
case LibraryType.Comic:
|
||||
case LibraryType.ComicVine:
|
||||
if (includeHash) {
|
||||
return this.translocoService.translate('common.issue-hash-num');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ export class SideNavComponent implements OnInit {
|
|||
case LibraryType.LightNovel:
|
||||
return 'fa-book';
|
||||
case LibraryType.Comic:
|
||||
case LibraryType.ComicVine:
|
||||
case LibraryType.Manga:
|
||||
return 'fa-book-open';
|
||||
case LibraryType.Images:
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ export class LibrarySettingsModalComponent implements OnInit {
|
|||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||
break;
|
||||
case LibraryType.Comic:
|
||||
case LibraryType.ComicVine:
|
||||
this.libraryForm.get(FileTypeGroup.Archive + '')?.setValue(true);
|
||||
this.libraryForm.get(FileTypeGroup.Images + '')?.setValue(false);
|
||||
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(false);
|
||||
|
|
@ -196,6 +197,9 @@ export class LibrarySettingsModalComponent implements OnInit {
|
|||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||
break;
|
||||
}
|
||||
|
||||
this.libraryForm.get('allowScrobbling')?.setValue(this.IsKavitaPlusEligible);
|
||||
this.cdRef.markForCheck();
|
||||
}),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe();
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ export interface ReadHistoryEvent {
|
|||
libraryId: number;
|
||||
readDate: string;
|
||||
chapterId: number;
|
||||
chapterNumber: string;
|
||||
}
|
||||
chapterNumber: number;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue