Comic Rework, New Scanner, Foundation Overahul (is this a full release?) (#2780)

This commit is contained in:
Joe Milazzo 2024-03-17 12:58:32 -05:00 committed by GitHub
parent d7e9e7c832
commit 7552c3f5fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
182 changed files with 27630 additions and 3046 deletions

View file

@ -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">

View file

@ -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();
}

View file

@ -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>

View file

@ -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();
});
}
}

View file

@ -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;

View file

@ -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">

View file

@ -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(() => {});
}

View file

@ -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>

View file

@ -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 || '';