mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
More Bugfixes (#2874)
This commit is contained in:
parent
f02e1f7d1f
commit
6d9a5d8f65
@ -176,6 +176,7 @@ public class SeriesController : BaseApiController
|
||||
return Ok(await _unitOfWork.ChapterRepository.AddChapterModifiers(User.GetUserId(), chapter));
|
||||
}
|
||||
|
||||
[Obsolete("All chapter entities will load this data by default. Will not be maintained as of v0.8.1")]
|
||||
[HttpGet("chapter-metadata")]
|
||||
public async Task<ActionResult<ChapterMetadataDto>> GetChapterMetadata(int chapterId)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using API.DTOs.Metadata;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Interfaces;
|
||||
|
||||
@ -120,4 +121,42 @@ public class ChapterDto : IHasReadTimeEstimate
|
||||
/// </summary>
|
||||
/// <remarks>This is guaranteed to be Valid</remarks>
|
||||
public string ISBN { get; set; }
|
||||
|
||||
#region Metadata
|
||||
|
||||
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> CoverArtists { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Publishers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Characters { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Pencillers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Inkers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Imprints { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Colorists { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Letterers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Editors { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Translators { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Teams { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Locations { get; set; } = new List<PersonDto>();
|
||||
|
||||
public ICollection<GenreTagDto> Genres { get; set; } = new List<GenreTagDto>();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of all Tags from underlying chapters for a Series
|
||||
/// </summary>
|
||||
public ICollection<TagDto> Tags { get; set; } = new List<TagDto>();
|
||||
public PublicationStatus PublicationStatus { get; set; }
|
||||
/// <summary>
|
||||
/// Language for the Chapter/Issue
|
||||
/// </summary>
|
||||
public string? Language { get; set; }
|
||||
/// <summary>
|
||||
/// Number in the TotalCount of issues
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
/// <summary>
|
||||
/// Total number of issues for the series
|
||||
/// </summary>
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ public class AutoMapperProfiles : Profile
|
||||
CreateMap<Volume, VolumeDto>()
|
||||
.ForMember(dest => dest.Number, opt => opt.MapFrom(src => (int) src.MinNumber));
|
||||
CreateMap<MangaFile, MangaFileDto>();
|
||||
CreateMap<Chapter, ChapterDto>();
|
||||
CreateMap<Series, SeriesDto>();
|
||||
CreateMap<CollectionTag, CollectionTagDto>();
|
||||
CreateMap<AppUserCollection, AppUserCollectionDto>()
|
||||
@ -191,6 +190,48 @@ public class AutoMapperProfiles : Profile
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Location).OrderBy(p => p.NormalizedName)))
|
||||
;
|
||||
|
||||
CreateMap<Chapter, ChapterDto>()
|
||||
.ForMember(dest => dest.Writers,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.CoverArtists,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Colorists,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Colorist).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Inkers,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Inker).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Imprints,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Imprint).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Letterers,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Letterer).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Pencillers,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Penciller).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Publishers,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Publisher).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Translators,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Translator).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Characters,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Character).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Editors,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Editor).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Teams,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Team).OrderBy(p => p.NormalizedName)))
|
||||
.ForMember(dest => dest.Locations,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Location).OrderBy(p => p.NormalizedName)))
|
||||
;
|
||||
|
||||
CreateMap<AppUser, UserDto>()
|
||||
.ForMember(dest => dest.AgeRestriction,
|
||||
opt =>
|
||||
|
@ -191,7 +191,14 @@ public class CacheService : ICacheService
|
||||
|
||||
if (files.Count > 0 && files[0].Format == MangaFormat.Image)
|
||||
{
|
||||
_readingItemService.Extract(files[0].FilePath, extractPath, MangaFormat.Image, files.Count);
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (fileCount > 1)
|
||||
{
|
||||
extraPath = file.Id + string.Empty;
|
||||
}
|
||||
_readingItemService.Extract(file.FilePath, Path.Join(extractPath, extraPath), MangaFormat.Image, files.Count);
|
||||
}
|
||||
_directoryService.Flatten(extractDi.FullName);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { MangaFile } from './manga-file';
|
||||
import { AgeRating } from './metadata/age-rating';
|
||||
import {PublicationStatus} from "./metadata/publication-status";
|
||||
import {Genre} from "./metadata/genre";
|
||||
import {Tag} from "./tag";
|
||||
import {Person} from "./metadata/person";
|
||||
|
||||
export const LooseLeafOrDefaultNumber = -100000;
|
||||
export const SpecialVolumeNumber = 100000;
|
||||
@ -51,4 +55,28 @@ export interface Chapter {
|
||||
isbn: string;
|
||||
lastReadingProgress: string;
|
||||
sortOrder: number;
|
||||
|
||||
// originally in ChapterMetadata but now inlined with Chapter data
|
||||
|
||||
year: string;
|
||||
language: string;
|
||||
publicationStatus: PublicationStatus;
|
||||
count: number;
|
||||
totalCount: number;
|
||||
|
||||
genres: Array<Genre>;
|
||||
tags: Array<Tag>;
|
||||
writers: Array<Person>;
|
||||
coverArtists: Array<Person>;
|
||||
publishers: Array<Person>;
|
||||
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,42 +0,0 @@
|
||||
import { Genre } from "./genre";
|
||||
import { AgeRating } from "./age-rating";
|
||||
import { PublicationStatus } from "./publication-status";
|
||||
import { Person } from "./person";
|
||||
import { Tag } from "../tag";
|
||||
|
||||
export interface ChapterMetadata {
|
||||
id: number;
|
||||
chapterId: number;
|
||||
title: string;
|
||||
year: string;
|
||||
|
||||
ageRating: AgeRating;
|
||||
releaseDate: string;
|
||||
language: string;
|
||||
publicationStatus: PublicationStatus;
|
||||
summary: string;
|
||||
count: number;
|
||||
totalCount: number;
|
||||
wordCount: number;
|
||||
|
||||
|
||||
|
||||
genres: Array<Genre>;
|
||||
tags: Array<Tag>;
|
||||
writers: Array<Person>;
|
||||
coverArtists: Array<Person>;
|
||||
publishers: Array<Person>;
|
||||
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>;
|
||||
|
||||
|
||||
|
||||
}
|
@ -5,8 +5,6 @@ import { map } from 'rxjs/operators';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { UtilityService } from '../shared/_services/utility.service';
|
||||
import { Chapter } from '../_models/chapter';
|
||||
import { ChapterMetadata } from '../_models/metadata/chapter-metadata';
|
||||
import { UserCollection } from '../_models/collection-tag';
|
||||
import { PaginatedResult } from '../_models/pagination';
|
||||
import { Series } from '../_models/series';
|
||||
import { RelatedSeries } from '../_models/series-detail/related-series';
|
||||
@ -75,10 +73,6 @@ export class SeriesService {
|
||||
return this.httpClient.get<Chapter>(this.baseUrl + 'series/chapter?chapterId=' + chapterId);
|
||||
}
|
||||
|
||||
getChapterMetadata(chapterId: number) {
|
||||
return this.httpClient.get<ChapterMetadata>(this.baseUrl + 'series/chapter-metadata?chapterId=' + chapterId);
|
||||
}
|
||||
|
||||
delete(seriesId: number) {
|
||||
return this.httpClient.delete<string>(this.baseUrl + 'series/' + seriesId, TextResonse).pipe(map(s => s === "true"));
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
||||
<li [ngbNavItem]="tabs[TabID.Metadata]">
|
||||
<a ngbNavLink>{{t(tabs[TabID.Metadata].title)}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<app-chapter-metadata-detail [chapter]="chapterMetadata"></app-chapter-metadata-detail>
|
||||
<app-chapter-metadata-detail [chapter]="chapter"></app-chapter-metadata-detail>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
|
@ -21,7 +21,6 @@ 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, 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';
|
||||
import { MangaFile } from 'src/app/_models/manga-file';
|
||||
@ -48,7 +47,7 @@ import {BytesPipe} from "../../_pipes/bytes.pipe";
|
||||
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
||||
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
|
||||
import {EditChapterProgressComponent} from "../edit-chapter-progress/edit-chapter-progress.component";
|
||||
|
||||
@ -113,9 +112,7 @@ export class CardDetailDrawerComponent implements OnInit {
|
||||
];
|
||||
active = this.tabs[0];
|
||||
|
||||
chapterMetadata: ChapterMetadata | undefined;
|
||||
summary: string = '';
|
||||
|
||||
downloadInProgress: boolean = false;
|
||||
|
||||
|
||||
@ -139,10 +136,6 @@ export class CardDetailDrawerComponent implements OnInit {
|
||||
this.isChapter = this.utilityService.isChapter(this.data);
|
||||
this.chapter = this.utilityService.isChapter(this.data) ? (this.data as Chapter) : (this.data as Volume).chapters[0];
|
||||
|
||||
this.seriesService.getChapterMetadata(this.chapter.id).subscribe(metadata => {
|
||||
this.chapterMetadata = metadata;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
|
||||
if (this.isChapter) {
|
||||
this.coverImageUrl = this.imageService.getChapterCoverImage(this.data.id);
|
||||
|
@ -171,8 +171,10 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
||||
|
||||
hasCustomSort() {
|
||||
if (this.filteringDisabled) return false;
|
||||
return this.filter?.sortOptions?.sortField != SortField.SortName || !this.filter?.sortOptions.isAscending
|
||||
|| this.filterSettings?.presetsV2?.sortOptions?.sortField != SortField.SortName || !this.filterSettings?.presetsV2?.sortOptions?.isAscending;
|
||||
const hasCustomSort = this.filter?.sortOptions?.sortField != SortField.SortName || !this.filter?.sortOptions.isAscending;
|
||||
const hasNonDefaultSortField = this.filterSettings?.presetsV2?.sortOptions?.sortField != SortField.SortName;
|
||||
|
||||
return hasCustomSort;
|
||||
}
|
||||
|
||||
performAction(action: ActionItem<any>) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {Chapter} from "../../_models/chapter";
|
||||
|
||||
@Component({
|
||||
selector: 'app-chapter-metadata-detail',
|
||||
@ -14,5 +14,5 @@ import {TranslocoDirective} from "@ngneat/transloco";
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ChapterMetadataDetailComponent {
|
||||
@Input() chapter: ChapterMetadata | undefined;
|
||||
@Input() chapter: Chapter | undefined;
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
<ng-container *transloco="let t; read: 'entity-info-cards'">
|
||||
|
||||
<div class="mt-3 mb-3">
|
||||
<div class="row g-0" *ngIf="chapterMetadata ">
|
||||
<div class="row g-0" *ngIf="chapter ">
|
||||
<!-- Tags and Characters are used a lot of Hentai and Doujinshi type content, so showing in list item has value add on first glance -->
|
||||
<app-metadata-detail [tags]="chapterMetadata.tags" [libraryId]="libraryId" [queryParam]="FilterField.Tags" heading="Tags">
|
||||
<app-metadata-detail [tags]="chapter.tags" [libraryId]="libraryId" [queryParam]="FilterField.Tags" heading="Tags">
|
||||
<ng-template #titleTemplate let-item>{{item.title}}</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<app-metadata-detail [tags]="chapterMetadata.characters" [libraryId]="libraryId" [queryParam]="FilterField.Characters" heading="Characters">
|
||||
<app-metadata-detail [tags]="chapter.characters" [libraryId]="libraryId" [queryParam]="FilterField.Characters" heading="Characters">
|
||||
<ng-template #titleTemplate let-item>{{item.name}}</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
} from '@angular/core';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
|
||||
import { HourEstimateRange } from 'src/app/_models/series-detail/hour-estimate-range';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { AgeRating } from 'src/app/_models/metadata/age-rating';
|
||||
@ -51,10 +50,6 @@ export class EntityInfoCardsComponent implements OnInit {
|
||||
|
||||
@Input({required: true}) entity!: Volume | Chapter;
|
||||
@Input({required: true}) libraryId!: number;
|
||||
/**
|
||||
* This will pull extra information
|
||||
*/
|
||||
@Input() includeMetadata: boolean = false;
|
||||
|
||||
/**
|
||||
* Hide more system based fields, like id or Date Added
|
||||
@ -64,7 +59,6 @@ export class EntityInfoCardsComponent implements OnInit {
|
||||
isChapter = false;
|
||||
chapter!: Chapter;
|
||||
|
||||
chapterMetadata!: ChapterMetadata;
|
||||
ageRating!: string;
|
||||
totalPages: number = 0;
|
||||
totalWordCount: number = 0;
|
||||
@ -94,12 +88,6 @@ export class EntityInfoCardsComponent implements OnInit {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (this.includeMetadata) {
|
||||
this.seriesService.getChapterMetadata(this.chapter.id).subscribe(metadata => {
|
||||
this.chapterMetadata = metadata;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
this.totalPages = this.chapter.pages;
|
||||
if (!this.isChapter) {
|
||||
|
@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="ps-2 d-none d-md-inline-block" style="width: 100%">
|
||||
<app-entity-info-cards [entity]="entity" [libraryId]="libraryId" [includeMetadata]="ShowExtended" [showExtendedProperties]="ShowExtended"></app-entity-info-cards>
|
||||
<app-entity-info-cards [entity]="entity" [libraryId]="libraryId" [showExtendedProperties]="ShowExtended"></app-entity-info-cards>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,6 +30,7 @@
|
||||
<div infinite-scroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50">
|
||||
<ng-container *ngFor="let item of webtoonImages | async; let index = index;">
|
||||
<img src="{{item.src}}" style="display: block"
|
||||
[style.filter]="(darkness$ | async) ?? '' | safeStyle"
|
||||
class="mx-auto {{pageNum === item.page && showDebugOutline() ? 'active': ''}} {{areImagesWiderThanWindow ? 'full-width' : ''}}"
|
||||
rel="nofollow" alt="image" (load)="onImageLoad($event)" id="page-{{item.page}}" [attr.page]="item.page" ondragstart="return false;" onselectstart="return false;">
|
||||
</ng-container>
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
Renderer2,
|
||||
SimpleChanges, ViewChild
|
||||
} from '@angular/core';
|
||||
import { BehaviorSubject, fromEvent, ReplaySubject } from 'rxjs';
|
||||
import {BehaviorSubject, filter, fromEvent, map, Observable, of, ReplaySubject} from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { ScrollService } from 'src/app/_services/scroll.service';
|
||||
import { ReaderService } from '../../../_services/reader.service';
|
||||
@ -27,6 +27,8 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {MangaReaderComponent} from "../manga-reader/manga-reader.component";
|
||||
import {InfiniteScrollModule} from "ngx-infinite-scroll";
|
||||
import {ReaderSetting} from "../../_models/reader-setting";
|
||||
import {SafeStylePipe} from "../../../_pipes/safe-style.pipe";
|
||||
|
||||
/**
|
||||
* How much additional space should pass, past the original bottom of the document height before we trigger the next chapter load
|
||||
@ -61,7 +63,7 @@ const enum DEBUG_MODES {
|
||||
styleUrls: ['./infinite-scroller.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIf, NgFor, AsyncPipe, TranslocoDirective, InfiniteScrollModule]
|
||||
imports: [NgIf, NgFor, AsyncPipe, TranslocoDirective, InfiniteScrollModule, SafeStylePipe]
|
||||
})
|
||||
export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
|
||||
|
||||
@ -70,6 +72,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
|
||||
private readonly renderer = inject(Renderer2);
|
||||
private readonly scrollService = inject(ScrollService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
/**
|
||||
* Current page number aka what's recorded on screen
|
||||
@ -87,6 +90,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
|
||||
* Method to generate the src for Image loading
|
||||
*/
|
||||
@Input({required: true}) urlProvider!: (page: number) => string;
|
||||
@Input({required: true}) readerSettings$!: Observable<ReaderSetting>;
|
||||
@Output() pageNumberChange: EventEmitter<number> = new EventEmitter<number>();
|
||||
@Output() loadNextChapter: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Output() loadPrevChapter: EventEmitter<void> = new EventEmitter<void>();
|
||||
@ -99,7 +103,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
|
||||
bottomSpacerIntersectionObserver: IntersectionObserver = new IntersectionObserver((entries) => this.handleBottomIntersection(entries),
|
||||
{ threshold: 1.0 });
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
darkness$: Observable<string> = of('brightness(100%)');
|
||||
|
||||
readerElemRef!: ElementRef<HTMLDivElement>;
|
||||
|
||||
@ -223,6 +227,11 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
|
||||
|
||||
this.recalculateImageWidth();
|
||||
|
||||
this.darkness$ = this.readerSettings$.pipe(
|
||||
map(values => 'brightness(' + values.darkness + '%)'),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
);
|
||||
|
||||
if (this.goToPage) {
|
||||
this.goToPage.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(page => {
|
||||
const isSamePage = this.pageNum === page;
|
||||
|
@ -28,11 +28,13 @@
|
||||
<i class="fa-regular fa-rectangle-list" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">{{t('shortcuts-menu-alt')}}</span>
|
||||
</button>
|
||||
<button *ngIf="!bookmarkMode && hasBookmarkRights" class="btn btn-icon" role="checkbox" [attr.aria-checked]="CurrentPageBookmarked"
|
||||
title="{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}" (click)="bookmarkPage()">
|
||||
<i class="{{CurrentPageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}</span>
|
||||
</button>
|
||||
@if (!bookmarkMode && hasBookmarkRights) {
|
||||
<button class="btn btn-icon" role="checkbox" [attr.aria-checked]="CurrentPageBookmarked"
|
||||
title="{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}" (click)="bookmarkPage()">
|
||||
<i class="{{CurrentPageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -118,7 +120,8 @@
|
||||
(loadNextChapter)="loadNextChapter()"
|
||||
(loadPrevChapter)="loadPrevChapter()"
|
||||
[bookmarkPage]="showBookmarkEffectEvent"
|
||||
[fullscreenToggled]="fullscreenEvent">
|
||||
[fullscreenToggled]="fullscreenEvent"
|
||||
[readerSettings$]="readerSettings$">
|
||||
</app-infinite-scroller>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
@ -2,12 +2,14 @@
|
||||
<div class="d-flex flex-row g-0 mb-2 reading-list-item">
|
||||
<div class="pe-2">
|
||||
<app-image width="106px" maxHeight="125px" class="img-top me-3" [imageUrl]="imageService.getChapterCoverImage(item.chapterId)"></app-image>
|
||||
<ng-container *ngIf="item.pagesRead === 0 && item.pagesTotal > 0">
|
||||
@if (item.pagesRead === 0 && item.pagesTotal > 0) {
|
||||
<div class="not-read-badge" ></div>
|
||||
</ng-container>
|
||||
<div class="progress-banner" *ngIf="item.pagesRead < item.pagesTotal && item.pagesTotal > 0 && item.pagesRead !== item.pagesTotal">
|
||||
<p><ngb-progressbar type="primary" height="5px" [value]="item.pagesRead" [max]="item.pagesTotal"></ngb-progressbar></p>
|
||||
</div>
|
||||
}
|
||||
@if (item.pagesRead < item.pagesTotal && item.pagesTotal > 0 && item.pagesRead !== item.pagesTotal) {
|
||||
<div class="progress-banner">
|
||||
<p><ngb-progressbar type="primary" height="5px" [value]="item.pagesRead" [max]="item.pagesTotal"></ngb-progressbar></p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1">
|
||||
@ -37,9 +39,11 @@
|
||||
|
||||
<!-- TODO: Let's add summary here-->
|
||||
|
||||
<div class="ps-1 mt-2" *ngIf="item.releaseDate !== '0001-01-01T00:00:00'">
|
||||
Released: {{item.releaseDate | date:'longDate'}}
|
||||
</div>
|
||||
@if (item.releaseDate !== '0001-01-01T00:00:00') {
|
||||
<div class="ps-1 mt-2">
|
||||
Released: {{item.releaseDate | date:'longDate'}}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, EventEmitter, inject, Input, Output} from '@angular/core';
|
||||
import { LibraryType } from 'src/app/_models/library/library';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { ReadingListItem } from 'src/app/_models/reading-list';
|
||||
@ -6,7 +6,7 @@ import { ImageService } from 'src/app/_services/image.service';
|
||||
import { MangaFormatIconPipe } from '../../../_pipes/manga-format-icon.pipe';
|
||||
import { MangaFormatPipe } from '../../../_pipes/manga-format.pipe';
|
||||
import { NgbProgressbar } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgIf, DatePipe } from '@angular/common';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ImageComponent } from '../../../shared/image/image.component';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
|
||||
@ -17,10 +17,13 @@ import {SeriesFormatComponent} from "../../../shared/series-format/series-format
|
||||
styleUrls: ['./reading-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ImageComponent, NgIf, NgbProgressbar, DatePipe, MangaFormatPipe, MangaFormatIconPipe, TranslocoDirective, SeriesFormatComponent]
|
||||
imports: [ImageComponent, NgbProgressbar, DatePipe, MangaFormatPipe, MangaFormatIconPipe, TranslocoDirective, SeriesFormatComponent]
|
||||
})
|
||||
export class ReadingListItemComponent {
|
||||
|
||||
protected readonly imageService = inject(ImageService);
|
||||
protected readonly MangaFormat = MangaFormat;
|
||||
|
||||
@Input({required: true}) item!: ReadingListItem;
|
||||
@Input() position: number = 0;
|
||||
@Input() libraryTypes: {[key: number]: LibraryType} = {};
|
||||
@ -32,15 +35,7 @@ export class ReadingListItemComponent {
|
||||
@Output() read: EventEmitter<ReadingListItem> = new EventEmitter();
|
||||
@Output() remove: EventEmitter<ReadingListItem> = new EventEmitter();
|
||||
|
||||
get MangaFormat(): typeof MangaFormat {
|
||||
return MangaFormat;
|
||||
}
|
||||
|
||||
constructor(public imageService: ImageService) { }
|
||||
|
||||
readChapter(item: ReadingListItem) {
|
||||
this.read.emit(item);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
134
openapi.json
134
openapi.json
@ -7,7 +7,7 @@
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.8.0.3"
|
||||
"version": "0.8.0.4"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
@ -14716,6 +14716,138 @@
|
||||
"type": "string",
|
||||
"description": "ISBN-13 (usually) of the Chapter",
|
||||
"nullable": true
|
||||
},
|
||||
"writers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"coverArtists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"publishers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"characters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"pencillers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"inkers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"imprints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"colorists": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"letterers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"editors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"translators": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"teams": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"genres": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GenreTagDto"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TagDto"
|
||||
},
|
||||
"description": "Collection of all Tags from underlying chapters for a Series",
|
||||
"nullable": true
|
||||
},
|
||||
"publicationStatus": {
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "Language for the Chapter/Issue",
|
||||
"nullable": true
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"description": "Number in the TotalCount of issues",
|
||||
"format": "int32"
|
||||
},
|
||||
"totalCount": {
|
||||
"type": "integer",
|
||||
"description": "Total number of issues for the series",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user