Foundational Rework (#2745)

This commit is contained in:
Joe Milazzo 2024-02-26 14:56:39 -06:00 committed by GitHub
parent 42cd6e9b3a
commit 4fa21fe1ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
92 changed files with 13330 additions and 650 deletions

View file

@ -8,7 +8,7 @@
"minify-langs": "node minify-json.js",
"cache-locale": "node hash-localization.js",
"cache-locale-prime": "node hash-localization-prime.js",
"prod": "npm run cache-locale-prime && ng build --configuration production && npm run minify-langs && npm run cache-locale",
"prod": "npm run cache-locale-prime && ng build --configuration production && npm run minify-langs && npm run cache-locale",
"explore": "ng build --stats-json && webpack-bundle-analyzer dist/stats.json",
"lint": "ng lint",
"e2e": "ng e2e"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -297,6 +297,7 @@ export class EditSeriesModalComponent implements OnInit {
});
this.seriesVolumes.forEach(vol => {
vol.volumeFiles = vol.chapters?.sort(this.utilityService.sortChapters).map((c: Chapter) => c.files.map((f: any) => {
// TODO: Identify how to fix this hack
f.chapter = c.number;
return f;
})).flat();

View file

@ -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.minNumber + '';
}
performAction(action: ActionItem<any>, chapter: Chapter) {
@ -281,5 +282,4 @@ export class CardDetailDrawerComponent implements OnInit {
this.cdRef.markForCheck();
});
}
}

View file

@ -204,7 +204,7 @@ export class CardItemComponent implements OnInit {
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

@ -7,9 +7,9 @@
<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.Manga">
@ -19,9 +19,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">

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

View file

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

View file

@ -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, 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,7 @@ 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 {LooseLeafOrDefaultNumber} 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 +184,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 +242,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) => {
@ -371,13 +372,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;

View file

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

View file

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

View file

@ -6,5 +6,5 @@ export interface ReadHistoryEvent {
libraryId: number;
readDate: string;
chapterId: number;
chapterNumber: string;
}
chapterNumber: number;
}

View file

@ -42,6 +42,8 @@
"is-processed-header": "Is Processed",
"no-data": "No Data",
"volume-and-chapter-num": "Volume {{v}} Chapter {{n}}",
"volume-num": "Volume {{num}}",
"chapter-num": "Chapter {{num}}",
"rating": "Rating {{r}}",
"not-applicable": "Not Applicable",
"processed": "Processed",