mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Bugs from UX Overhaul (#3117)
Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
3b915a8289
commit
d4bcd354dd
@ -34,7 +34,7 @@ public class CblController : BaseApiController
|
||||
/// <param name="comicVineMatching">Use comic vine matching or not. Defaults to false</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("validate")]
|
||||
public async Task<ActionResult<CblImportSummaryDto>> ValidateCbl(IFormFile cbl, bool comicVineMatching = false)
|
||||
public async Task<ActionResult<CblImportSummaryDto>> ValidateCbl(IFormFile cbl, [FromForm] bool comicVineMatching = false)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
try
|
||||
@ -85,7 +85,7 @@ public class CblController : BaseApiController
|
||||
/// <param name="comicVineMatching">Use comic vine matching or not. Defaults to false</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("import")]
|
||||
public async Task<ActionResult<CblImportSummaryDto>> ImportCbl(IFormFile cbl, bool dryRun = false, bool comicVineMatching = false)
|
||||
public async Task<ActionResult<CblImportSummaryDto>> ImportCbl(IFormFile cbl, [FromForm] bool dryRun = false, [FromForm] bool comicVineMatching = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -881,6 +881,8 @@ public class OpdsController : BaseApiController
|
||||
foreach (var chapter in chaptersForVolume)
|
||||
{
|
||||
var chapterId = chapter.Id;
|
||||
if (chapterDict.ContainsKey(chapterId)) continue;
|
||||
|
||||
var chapterDto = _mapper.Map<ChapterDto>(chapter);
|
||||
foreach (var mangaFile in chapter.Files)
|
||||
{
|
||||
@ -889,7 +891,6 @@ public class OpdsController : BaseApiController
|
||||
chapterDto, apiKey, prefix, baseUrl));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var chapters = seriesDetail.StorylineChapters;
|
||||
|
@ -318,7 +318,7 @@ public class UploadController : BaseApiController
|
||||
/// <summary>
|
||||
/// Replaces volume cover image and locks it with a base64 encoded image.
|
||||
/// </summary>
|
||||
/// <remarks>This is a helper API for Komf - Kavita UI does not use. Volume will find first chapter to update.</remarks>
|
||||
/// <remarks>This will not update the underlying chapter</remarks>
|
||||
/// <param name="uploadFileDto"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
@ -333,24 +333,15 @@ public class UploadController : BaseApiController
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(uploadFileDto.Id, VolumeIncludes.Chapters);
|
||||
if (volume == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "volume-doesnt-exist"));
|
||||
|
||||
// Find the first chapter of the volume
|
||||
var chapter = volume.Chapters[0];
|
||||
|
||||
var filePath = string.Empty;
|
||||
var lockState = false;
|
||||
if (!string.IsNullOrEmpty(uploadFileDto.Url))
|
||||
{
|
||||
filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetChapterFormat(chapter.Id, uploadFileDto.Id)}");
|
||||
filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetVolumeFormat(uploadFileDto.Id)}");
|
||||
lockState = uploadFileDto.LockCover;
|
||||
}
|
||||
|
||||
|
||||
chapter.CoverImage = filePath;
|
||||
chapter.CoverImageLocked = lockState;
|
||||
_imageService.UpdateColorScape(chapter);
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
|
||||
volume.CoverImage = chapter.CoverImage;
|
||||
volume.CoverImage = filePath;
|
||||
volume.CoverImageLocked = lockState;
|
||||
_imageService.UpdateColorScape(volume);
|
||||
_unitOfWork.VolumeRepository.Update(volume);
|
||||
@ -368,7 +359,7 @@ public class UploadController : BaseApiController
|
||||
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
|
||||
MessageFactory.CoverUpdateEvent(chapter.VolumeId, MessageFactoryEntityTypes.Volume), false);
|
||||
MessageFactory.CoverUpdateEvent(uploadFileDto.Id, MessageFactoryEntityTypes.Volume), false);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
|
||||
MessageFactory.CoverUpdateEvent(volume.Id, MessageFactoryEntityTypes.Chapter), false);
|
||||
return Ok();
|
||||
|
@ -59,6 +59,11 @@ public class SeriesDto : IHasReadTimeEstimate, IHasCoverImage
|
||||
/// </summary>
|
||||
public string FolderPath { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Lowest path (that is under library root) that contains all files for the series.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="Services.Tasks.Scanner.Parser.Parser.NormalizePath"/> must be used before setting</remarks>
|
||||
public string? LowestFolderPath { get; set; }
|
||||
/// <summary>
|
||||
/// The last time the folder for this series was scanned
|
||||
/// </summary>
|
||||
public DateTime LastFolderScanned { get; set; }
|
||||
|
@ -44,6 +44,7 @@ public interface IVolumeRepository
|
||||
Task<IEnumerable<Volume>> GetVolumes(int seriesId);
|
||||
Task<Volume?> GetVolumeByIdAsync(int volumeId);
|
||||
Task<IList<Volume>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat);
|
||||
Task<IEnumerable<string>> GetCoverImagesForLockedVolumesAsync();
|
||||
}
|
||||
public class VolumeRepository : IVolumeRepository
|
||||
{
|
||||
@ -252,4 +253,17 @@ public class VolumeRepository : IVolumeRepository
|
||||
.Sum(p => p.PagesRead);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns cover images for locked chapters
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<string>> GetCoverImagesForLockedVolumesAsync()
|
||||
{
|
||||
return (await _context.Volume
|
||||
.Where(c => c.CoverImageLocked)
|
||||
.Select(c => c.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.ToListAsync())!;
|
||||
}
|
||||
}
|
||||
|
@ -739,6 +739,16 @@ public class ImageService : IImageService
|
||||
return $"v{volumeId}_c{chapterId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name format for a volume cover image (custom)
|
||||
/// </summary>
|
||||
/// <param name="volumeId"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetVolumeFormat(int volumeId)
|
||||
{
|
||||
return $"v{volumeId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name format for a library cover image
|
||||
/// </summary>
|
||||
|
@ -550,6 +550,7 @@ public class ReadingListService : IReadingListService
|
||||
Results = new List<CblBookResult>(),
|
||||
SuccessfulInserts = new List<CblBookResult>()
|
||||
};
|
||||
|
||||
if (IsCblEmpty(cblReading, importSummary, out var readingListFromCbl)) return readingListFromCbl;
|
||||
|
||||
// Is there another reading list with the same name?
|
||||
|
@ -179,6 +179,10 @@ public class BackupService : IBackupService
|
||||
_directoryService.CopyFilesToDirectory(
|
||||
chapterImages.Select(s => _directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, s)), outputTempDir);
|
||||
|
||||
var volumeImages = await _unitOfWork.VolumeRepository.GetCoverImagesForLockedVolumesAsync();
|
||||
_directoryService.CopyFilesToDirectory(
|
||||
volumeImages.Select(s => _directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, s)), outputTempDir);
|
||||
|
||||
var libraryImages = await _unitOfWork.LibraryRepository.GetAllCoverImagesAsync();
|
||||
_directoryService.CopyFilesToDirectory(
|
||||
libraryImages.Select(s => _directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, s)), outputTempDir);
|
||||
|
@ -49,7 +49,7 @@ $image-width: 160px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 158px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.not-read-badge {
|
||||
@ -158,7 +158,6 @@ $image-width: 160px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 115;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.library {
|
||||
@ -186,7 +185,7 @@ $image-width: 160px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chapter,
|
||||
.chapter,
|
||||
.volume,
|
||||
.series,
|
||||
.expected {
|
||||
@ -200,4 +199,4 @@ $image-width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ export interface Series extends IHasCover, IHasReadingTime {
|
||||
* Highest level folder containing this series
|
||||
*/
|
||||
folderPath: string;
|
||||
lowestFolderPath: string;
|
||||
/**
|
||||
* This is currently only used on Series detail page for recommendations
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ export class CblConflictReasonPipe implements PipeTransform {
|
||||
case CblImportReason.EmptyFile:
|
||||
return failIcon + this.translocoService.translate('cbl-conflict-reason-pipe.empty-file');
|
||||
case CblImportReason.NameConflict:
|
||||
return failIcon + this.translocoService.translate('cbl-conflict-reason-pipe.chapter-missing', {readingListName: result.readingListName});
|
||||
return failIcon + this.translocoService.translate('cbl-conflict-reason-pipe.name-conflict', {readingListName: result.readingListName});
|
||||
case CblImportReason.SeriesCollision:
|
||||
return failIcon + this.translocoService.translate('cbl-conflict-reason-pipe.series-collision', {seriesLink: `<a href="/library/${result.libraryId}/series/${result.seriesId}" target="_blank">${result.series}</a>`});
|
||||
case CblImportReason.SeriesMissing:
|
||||
|
@ -99,6 +99,7 @@ export class NavService {
|
||||
*/
|
||||
showNavBar() {
|
||||
this.renderer.setStyle(this.document.querySelector('body'), 'margin-top', 'var(--nav-offset)');
|
||||
this.renderer.removeStyle(this.document.querySelector('body'), 'scrollbar-gutter');
|
||||
this.renderer.setStyle(this.document.querySelector('body'), 'height', 'calc(var(--vh)*100 - var(--nav-offset))');
|
||||
this.renderer.setStyle(this.document.querySelector('html'), 'height', 'calc(var(--vh)*100 - var(--nav-offset))');
|
||||
this.navbarVisibleSource.next(true);
|
||||
@ -109,6 +110,7 @@ export class NavService {
|
||||
*/
|
||||
hideNavBar() {
|
||||
this.renderer.setStyle(this.document.querySelector('body'), 'margin-top', '0px');
|
||||
this.renderer.setStyle(this.document.querySelector('body'), 'scrollbar-gutter', 'initial');
|
||||
this.renderer.removeStyle(this.document.querySelector('body'), 'height');
|
||||
this.renderer.removeStyle(this.document.querySelector('html'), 'height');
|
||||
this.navbarVisibleSource.next(false);
|
||||
|
@ -16,7 +16,7 @@
|
||||
{{t('cover-image-description')}}
|
||||
</p>
|
||||
<app-cover-image-chooser [(imageUrls)]="imageUrls" (imageSelected)="updateSelectedIndex($event)" (selectedBase64Url)="updateSelectedImage($event)"
|
||||
[showReset]="volume.coverImageLocked" (resetClicked)="handleReset()"></app-cover-image-chooser>
|
||||
[showReset]="true" (resetClicked)="handleReset()"></app-cover-image-chooser>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
@ -34,15 +34,15 @@
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="col-lg-6 col-md-12 pe-2">-->
|
||||
<!-- <div class="mb-3">-->
|
||||
<!-- <app-setting-item [title]="t('words-label')" [toggleOnViewClick]="false" [showEdit]="false">-->
|
||||
<!-- <ng-template #view>-->
|
||||
<!-- {{t('words-count', {num: volume.wordCount | compactNumber})}}-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-setting-item>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('words-label')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{t('words-count', {num: volume.wordCount | compactNumber})}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
|
||||
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||
import {
|
||||
NgbActiveModal,
|
||||
NgbInputDatepicker,
|
||||
@ -29,26 +29,15 @@ import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ReadTimePipe} from "../../_pipes/read-time.pipe";
|
||||
import {Action, ActionFactoryService, ActionItem} from "../../_services/action-factory.service";
|
||||
import {Volume} from "../../_models/volume";
|
||||
import {SeriesService} from "../../_services/series.service";
|
||||
import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
|
||||
import {ImageService} from "../../_services/image.service";
|
||||
import {UploadService} from "../../_services/upload.service";
|
||||
import {MetadataService} from "../../_services/metadata.service";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {ActionService} from "../../_services/action.service";
|
||||
import {DownloadService} from "../../shared/_services/download.service";
|
||||
import {Chapter} from "../../_models/chapter";
|
||||
import {LibraryType} from "../../_models/library/library";
|
||||
import {TypeaheadSettings} from "../../typeahead/_models/typeahead-settings";
|
||||
import {Tag} from "../../_models/tag";
|
||||
import {Language} from "../../_models/metadata/language";
|
||||
import {Person, PersonRole} from "../../_models/metadata/person";
|
||||
import {Genre} from "../../_models/metadata/genre";
|
||||
import {AgeRatingDto} from "../../_models/metadata/age-rating-dto";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {forkJoin, Observable, of} from "rxjs";
|
||||
import {map} from "rxjs/operators";
|
||||
import {EditChapterModalCloseResult} from "../edit-chapter-modal/edit-chapter-modal.component";
|
||||
import {PersonRole} from "../../_models/metadata/person";
|
||||
import {forkJoin} from "rxjs";
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import {MangaFile} from "../../_models/manga-file";
|
||||
import {VolumeService} from "../../_services/volume.service";
|
||||
@ -167,18 +156,16 @@ export class EditVolumeModalComponent implements OnInit {
|
||||
}
|
||||
|
||||
save() {
|
||||
const model = this.editForm.value;
|
||||
const selectedIndex = this.editForm.get('coverImageIndex')?.value || 0;
|
||||
|
||||
const apis = [];
|
||||
|
||||
|
||||
if (selectedIndex > 0 || this.coverImageReset) {
|
||||
apis.push(this.uploadService.updateVolumeCoverImage(model.id, this.selectedCover, !this.coverImageReset));
|
||||
apis.push(this.uploadService.updateVolumeCoverImage(this.volume.id, this.selectedCover, !this.coverImageReset));
|
||||
}
|
||||
|
||||
forkJoin(apis).subscribe(results => {
|
||||
this.modal.close({success: true, volume: model, coverImageUpdate: selectedIndex > 0 || this.coverImageReset, needsReload: false, isDeleted: false} as EditVolumeModalCloseResult);
|
||||
this.modal.close({success: true, volume: this.volume, coverImageUpdate: selectedIndex > 0 || this.coverImageReset, needsReload: false, isDeleted: false} as EditVolumeModalCloseResult);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ $primary-color: #0062cc;
|
||||
$action-bar-height: 38px;
|
||||
|
||||
|
||||
|
||||
// Drawer
|
||||
.control-container {
|
||||
padding-bottom: 5px;
|
||||
|
@ -482,10 +482,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
constructor(@Inject(DOCUMENT) private document: Document) {
|
||||
this.navService.hideNavBar();
|
||||
this.themeService.clearThemes();
|
||||
this.navService.hideSideNav();
|
||||
this.cdRef.markForCheck();
|
||||
this.navService.hideNavBar();
|
||||
this.navService.hideSideNav();
|
||||
this.themeService.clearThemes();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
<ul class="list-group">
|
||||
@for(collectionTag of lists | filter: filterList; let i = $index; track collectionTag.title) {
|
||||
@for(collectionTag of lists | filter: filterList; let i = $index; track collectionTag.title + collectionTag.promoted) {
|
||||
<li class="list-group-item clickable" tabindex="0" role="option" (click)="addToCollection(collectionTag)">
|
||||
{{collectionTag.title}}
|
||||
@if (collectionTag.promoted) {
|
||||
|
@ -10,15 +10,16 @@ import {
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
import {NgbActiveModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {UserCollection} from 'src/app/_models/collection-tag';
|
||||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||
import {ReadingList} from 'src/app/_models/reading-list';
|
||||
import {CollectionTagService} from 'src/app/_services/collection-tag.service';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {FilterPipe} from "../../../_pipes/filter.pipe";
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ScrobbleProvider} from "../../../_services/scrobbling.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-bulk-add-to-collection',
|
||||
@ -60,7 +61,8 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
|
||||
this.loading = true;
|
||||
this.cdRef.markForCheck();
|
||||
this.collectionService.allCollections(true).subscribe(tags => {
|
||||
this.lists = tags;
|
||||
// Don't allow Smart Collections in
|
||||
this.lists = tags.filter(t => t.source === ScrobbleProvider.Kavita);
|
||||
this.loading = false;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
|
@ -489,85 +489,215 @@
|
||||
<a ngbNavLink>{{t(tabs[TabID.Info])}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<h5>{{t('info-title')}}</h5>
|
||||
<div class="row g-0 mt-3 mb-3">
|
||||
<div class="col-md-6" *ngIf="libraryName"><span class="fw-bold text-uppercase">{{t('library-title')}}</span> {{libraryName | sentenceCase}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('format-title')}}</span> <app-tag-badge>{{series.format | mangaFormat}}</app-tag-badge></div>
|
||||
</div>
|
||||
<div class="row g-0 mt-3 mb-3">
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('created-title')}}</span> {{series.created | date:'shortDate'}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('last-read-title')}}</span> {{series.latestReadDate | defaultDate | timeAgo}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('last-added-title')}}</span> {{series.lastChapterAdded | defaultDate | timeAgo}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('last-scanned-title')}}</span> {{series.lastFolderScanned | defaultDate | timeAgo}}</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('library-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{libraryName! | sentenceCase}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('format-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
<app-tag-badge>{{series.format | mangaFormat}}</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 mt-3 mb-3">
|
||||
<div class="col-auto"><span class="fw-bold text-uppercase">{{t('folder-path-title')}}</span> {{series.folderPath | defaultValue}}</div>
|
||||
</div>
|
||||
<div class="row g-0 mt-3 mb-3" *ngIf="metadata">
|
||||
<div class="col-md-6">
|
||||
<span class="fw-bold text-uppercase">{{t('max-items-title')}}</span> {{metadata.maxCount}}
|
||||
<i class="fa fa-info-circle ms-1" placement="right" [ngbTooltip]="t('highest-count-tooltip')" role="button" tabindex="0"></i>
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('folder-path-title')" [subtitle]="t('folder-path-tooltip')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.folderPath | defaultValue}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<span class="fw-bold text-uppercase">{{t('total-items-title')}}</span> {{metadata.totalCount}}
|
||||
<i class="fa fa-info-circle ms-1" placement="right" [ngbTooltip]="t('max-issue-tooltip')" role="button" tabindex="0"></i>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('lowest-folder-path-title')" [subtitle]="t('lowest-folder-path-tooltip')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.lowestFolderPath | defaultValue}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('publication-status-title')}}</span> {{metadata.publicationStatus | publicationStatus}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('total-pages-title')}}</span> {{series.pages}}</div>
|
||||
<div class="col-md-6"><span class="fw-bold text-uppercase">{{t('size-title')}}</span> {{size | bytes}}</div>
|
||||
</div>
|
||||
<h4>Volumes</h4>
|
||||
<div class="spinner-border text-secondary" role="status" *ngIf="isLoadingVolumes">
|
||||
<span class="visually-hidden">{{t('loading')}}</span>
|
||||
</div>
|
||||
<ul class="list-unstyled" *ngIf="!isLoadingVolumes">
|
||||
<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">{{formatVolumeName(volume)}}</h5>
|
||||
<div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
{{t('added-title')}} {{volume.createdUtc | utcToLocalTime | defaultDate}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime | translocoDate: {dateStyle: 'short' } | defaultDate}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-outline-primary" (click)="collapse.toggle()"
|
||||
[attr.aria-expanded]="!volumeCollapsed[volume.name]">
|
||||
{{t('view-files')}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('pages-title')}} {{volume.pages}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="volumeCollapsed[volume.name]">
|
||||
<ul class="list-group mt-2">
|
||||
<li *ngFor="let file of volume.volumeFiles" class="list-group-item">
|
||||
<span>{{file.filePath}}</span>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
{{t('chapter-title')}} {{file.chapter}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('pages-title')}} {{file.pages}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('format-title')}} <span class="badge badge-secondary">{{utilityService.mangaFormatToText(file.format)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@if (metadata) {
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('max-items-title')" [subtitle]="t('highest-count-tooltip')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{metadata.maxCount}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('total-items-title')" [subtitle]="t('max-issue-tooltip')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{metadata.totalCount}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('publication-status-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{metadata.publicationStatus | publicationStatus}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('size-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{size | bytes}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('created-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.created | date:'shortDate'}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('last-added-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.lastChapterAdded | defaultDate | timeAgo}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('last-scanned-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.lastFolderScanned | defaultDate | timeAgo}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('last-read-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.lastChapterAdded | defaultDate | timeAgo}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('total-pages-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.pages | number}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<app-setting-item [title]="t('total-words-title')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
{{series.wordCount | number}}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Volumes</h4>
|
||||
@if (isLoadingVolumes) {
|
||||
<div class="spinner-border text-secondary" role="status" *ngIf="isLoadingVolumes">
|
||||
<span class="visually-hidden">{{t('loading')}}</span>
|
||||
</div>
|
||||
} @else {
|
||||
<ul class="list-unstyled">
|
||||
@for (volume of seriesVolumes; track volume.id) {
|
||||
<li class="d-flex my-4">
|
||||
<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">{{formatVolumeName(volume)}}</h5>
|
||||
<div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
{{t('added-title')}} {{volume.createdUtc | utcToLocalTime | defaultDate}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime | translocoDate: {dateStyle: 'short' } | defaultDate}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-outline-primary" (click)="collapse.toggle()"
|
||||
[attr.aria-expanded]="!volumeCollapsed[volume.name]">
|
||||
{{t('view-files')}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('pages-title')}} {{volume.pages}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="volumeCollapsed[volume.name]">
|
||||
<ul class="list-group mt-2">
|
||||
@for(file of volume.volumeFiles; track file.id) {
|
||||
<li class="list-group-item">
|
||||
<span>{{file.filePath}}</span>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
{{t('chapter-title')}} {{file.chapter}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('pages-title')}} {{file.pages}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{t('format-title')}} <span class="badge badge-secondary">{{utilityService.mangaFormatToText(file.format)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
|
@ -54,7 +54,6 @@ import {TranslocoDatePipe} from "@jsverse/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe";
|
||||
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";
|
||||
import {Action, ActionFactoryService, ActionItem} from "../../../_services/action-factory.service";
|
||||
@ -62,6 +61,7 @@ import {SettingButtonComponent} from "../../../settings/_components/setting-butt
|
||||
import {ActionService} from "../../../_services/action.service";
|
||||
import {DownloadService} from "../../../shared/_services/download.service";
|
||||
import {SettingItemComponent} from "../../../settings/_components/setting-item/setting-item.component";
|
||||
import {ReadTimePipe} from "../../../_pipes/read-time.pipe";
|
||||
|
||||
enum TabID {
|
||||
General = 0,
|
||||
@ -116,6 +116,7 @@ const blackList = [Action.Edit, Action.Info, Action.IncognitoRead, Action.Read,
|
||||
EditListComponent,
|
||||
SettingButtonComponent,
|
||||
SettingItemComponent,
|
||||
ReadTimePipe,
|
||||
],
|
||||
templateUrl: './edit-series-modal.component.html',
|
||||
styleUrls: ['./edit-series-modal.component.scss'],
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
@ -2,9 +2,9 @@
|
||||
<div class="card-item-container card {{selected ? 'selected-highlight' : ''}}">
|
||||
<div class="overlay" (click)="handleClick($event)">
|
||||
@if (total > 0 || suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageUrl"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageUrl"></app-image>
|
||||
} @else if (total === 0 && !suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
}
|
||||
|
||||
<div class="progress-banner">
|
||||
|
@ -2,9 +2,9 @@
|
||||
<div class="card-item-container card position-relative {{selected ? 'selected-highlight' : ''}}" >
|
||||
<div class="overlay" (click)="handleClick($event)">
|
||||
@if (chapter.pages > 0 || suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getChapterCoverImage(chapter.id)"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getChapterCoverImage(chapter.id)"></app-image>
|
||||
} @else if (chapter.pages === 0 && !suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
}
|
||||
|
||||
<div class="progress-banner">
|
||||
|
@ -55,7 +55,7 @@
|
||||
<div class="clickable col-auto"
|
||||
*ngIf="showReset" tabindex="0" (click)="reset()"
|
||||
[ngClass]="{'selected': !showApplyButton && selectedIndex === -1}">
|
||||
<app-image class="card-img-top" [title]="t('reset-cover-tooltip')" height="230px" width="158px" [imageUrl]="imageService.resetCoverImage"></app-image>
|
||||
<app-image class="card-img-top" [title]="t('reset-cover-tooltip')" height="232.91px" width="160px" [imageUrl]="imageService.resetCoverImage"></app-image>
|
||||
<ng-container *ngIf="showApplyButton">
|
||||
<br>
|
||||
<button style="width: 100%;" class="btn btn-secondary" (click)="resetImage()">{{t('reset')}}</button>
|
||||
@ -65,7 +65,7 @@
|
||||
<div class="clickable col-auto"
|
||||
*ngFor="let url of imageUrls; let idx = index;" tabindex="0" [attr.aria-label]="t('image-num', {num: idx + 1})" (click)="selectImage(idx)"
|
||||
[ngClass]="{'selected': !showApplyButton && selectedIndex === idx}">
|
||||
<app-image class="card-img-top" height="230px" width="158px" [imageUrl]="url" [processEvents]="idx > 0"></app-image>
|
||||
<app-image class="card-img-top" height="232.91px" width="160px" [imageUrl]="url" [processEvents]="idx > 0"></app-image>
|
||||
<ng-container *ngIf="showApplyButton">
|
||||
<br>
|
||||
<button class="btn btn-primary" style="width: 100%;"
|
||||
|
@ -16,7 +16,7 @@ $image-width: 160px;
|
||||
|
||||
.chooser {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ export class ExternalListItemComponent {
|
||||
@Input() imageUrl: string = '';
|
||||
|
||||
/**
|
||||
* Size of the Image Height. Defaults to 230px.
|
||||
* Size of the Image Height. Defaults to 232.91px.
|
||||
*/
|
||||
@Input() imageHeight: string = '230px';
|
||||
@Input() imageHeight: string = '232.91px';
|
||||
/**
|
||||
* Size of the Image Width Defaults to 158px.
|
||||
* Size of the Image Width Defaults to 160px.
|
||||
*/
|
||||
@Input() imageWidth: string = '158px';
|
||||
@Input() imageWidth: string = '160px';
|
||||
@Input() summary: string | null = '';
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="card-item-container card clickable position-relative">
|
||||
<div class="overlay" (click)="handleClick()">
|
||||
<ng-container>
|
||||
<app-image [styles]="{'border-radius': '.25rem .25rem 0 0'}" height="230px" width="158px" [imageUrl]="data.coverUrl"></app-image>
|
||||
<app-image [styles]="{'border-radius': '.25rem .25rem 0 0'}" height="232.91px" width="160px" [imageUrl]="data.coverUrl"></app-image>
|
||||
</ng-container>
|
||||
|
||||
|
||||
|
@ -61,13 +61,13 @@ export class ListItemComponent implements OnInit {
|
||||
@Input() seriesName: string = '';
|
||||
|
||||
/**
|
||||
* Size of the Image Height. Defaults to 230px.
|
||||
* Size of the Image Height. Defaults to 232.91px.
|
||||
*/
|
||||
@Input() imageHeight: string = '230px';
|
||||
@Input() imageHeight: string = '232.91px';
|
||||
/**
|
||||
* Size of the Image Width Defaults to 158px.
|
||||
* Size of the Image Width Defaults to 160px.
|
||||
*/
|
||||
@Input() imageWidth: string = '158px';
|
||||
@Input() imageWidth: string = '160px';
|
||||
@Input() seriesLink: string = '';
|
||||
|
||||
@Input() pagesRead: number = 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="card-item-container card">
|
||||
<div class="overlay">
|
||||
<app-image [styles]="{'border-radius': '.25rem .25rem 0 0'}" height="230px" width="158px" classes="extreme-blur"
|
||||
<app-image [styles]="{'border-radius': '.25rem .25rem 0 0'}" height="232.91px" width="160px" classes="extreme-blur"
|
||||
[imageUrl]="imageUrl"></app-image>
|
||||
|
||||
<div class="card-overlay"></div>
|
||||
|
@ -2,9 +2,9 @@
|
||||
<div class="card-item-container card position-relative {{selected ? 'selected-highlight' : ''}}">
|
||||
<div class="overlay" (click)="handleClick()">
|
||||
@if (series.pages > 0) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getSeriesCoverImage(series.id)"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getSeriesCoverImage(series.id)"></app-image>
|
||||
} @else if (series.pages === 0) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
}
|
||||
|
||||
<div class="progress-banner">
|
||||
|
@ -2,9 +2,9 @@
|
||||
<div class="card-item-container card {{selected ? 'selected-highlight' : ''}}" >
|
||||
<div class="overlay position-relative" (click)="handleClick($event)">
|
||||
@if (volume.pages > 0 || suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getVolumeCoverImage(volume.id)"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.getVolumeCoverImage(volume.id)"></app-image>
|
||||
} @else if (volume.pages === 0 && !suppressArchiveWarning) {
|
||||
<app-image height="230px" width="158px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
<app-image height="232.91px" width="160px" [styles]="{'border-radius': '.25rem .25rem 0 0'}" [imageUrl]="imageService.errorImage"></app-image>
|
||||
}
|
||||
|
||||
<div class="progress-banner">
|
||||
@ -50,11 +50,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body meta-title">
|
||||
<div class="card-content d-flex justify-content-center align-items-center text-center" style="width:100%;min-height:58px;">
|
||||
{{volume.name}}
|
||||
@if (libraryType === LibraryType.LightNovel || libraryType === LibraryType.Book) {
|
||||
<div class="card-body meta-title">
|
||||
<div class="card-content d-flex justify-content-center align-items-center text-center" style="width:100%;min-height:58px;">
|
||||
{{volume.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card-title-container">
|
||||
<span class="card-title" id="{{volume.id}}" tabindex="0">
|
||||
|
@ -216,4 +216,5 @@ export class VolumeCardComponent implements OnInit {
|
||||
this.readerService.readVolume(this.libraryId, this.seriesId, this.volume, false);
|
||||
}
|
||||
|
||||
protected readonly LibraryType = LibraryType;
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ import {
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.component";
|
||||
import {TagBadgeComponent, TagBadgeCursor} from "../shared/tag-badge/tag-badge.component";
|
||||
import {PageLayoutMode} from "../_models/page-layout-mode";
|
||||
import {TagBadgeComponent} from "../shared/tag-badge/tag-badge.component";
|
||||
import {AsyncPipe, DecimalPipe, DOCUMENT, NgStyle} from "@angular/common";
|
||||
import {CardActionablesComponent} from "../_single-module/card-actionables/card-actionables.component";
|
||||
import {CarouselReelComponent} from "../carousel/_components/carousel-reel/carousel-reel.component";
|
||||
@ -31,15 +30,12 @@ import {
|
||||
import {PersonBadgeComponent} from "../shared/person-badge/person-badge.component";
|
||||
import {ReviewCardComponent} from "../_single-module/review-card/review-card.component";
|
||||
import {SeriesCardComponent} from "../cards/series-card/series-card.component";
|
||||
import {
|
||||
SeriesMetadataDetailComponent
|
||||
} from "../series-detail/_components/series-metadata-detail/series-metadata-detail.component";
|
||||
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
|
||||
import {ActivatedRoute, Router, RouterLink} from "@angular/router";
|
||||
import {ImageService} from "../_services/image.service";
|
||||
import {ChapterService} from "../_services/chapter.service";
|
||||
import {Chapter} from "../_models/chapter";
|
||||
import {forkJoin, map, Observable, shareReplay, tap} from "rxjs";
|
||||
import {forkJoin, map, Observable, tap} from "rxjs";
|
||||
import {SeriesService} from "../_services/series.service";
|
||||
import {Series} from "../_models/series";
|
||||
import {AgeRating} from "../_models/metadata/age-rating";
|
||||
@ -68,7 +64,6 @@ import {DefaultValuePipe} from "../_pipes/default-value.pipe";
|
||||
import {ReadingList} from "../_models/reading-list";
|
||||
import {ReadingListService} from "../_services/reading-list.service";
|
||||
import {CardItemComponent} from "../cards/card-item/card-item.component";
|
||||
import {PageBookmark} from "../_models/readers/page-bookmark";
|
||||
import {RelatedTabComponent} from "../_single-modules/related-tab/related-tab.component";
|
||||
import {AgeRatingImageComponent} from "../_single-modules/age-rating-image/age-rating-image.component";
|
||||
import {CompactNumberPipe} from "../_pipes/compact-number.pipe";
|
||||
@ -76,7 +71,6 @@ import {BadgeExpanderComponent} from "../shared/badge-expander/badge-expander.co
|
||||
import {
|
||||
MetadataDetailRowComponent
|
||||
} from "../series-detail/_components/metadata-detail-row/metadata-detail-row.component";
|
||||
import {HourEstimateRange} from "../_models/series-detail/hour-estimate-range";
|
||||
import {DownloadButtonComponent} from "../series-detail/_components/download-button/download-button.component";
|
||||
import {hasAnyCast} from "../_models/common/i-has-cast";
|
||||
import {CarouselTabComponent} from "../carousel/_components/carousel-tab/carousel-tab.component";
|
||||
@ -113,7 +107,6 @@ enum TabID {
|
||||
PersonBadgeComponent,
|
||||
ReviewCardComponent,
|
||||
SeriesCardComponent,
|
||||
SeriesMetadataDetailComponent,
|
||||
TagBadgeComponent,
|
||||
VirtualScrollerModule,
|
||||
NgStyle,
|
||||
@ -308,7 +301,7 @@ export class ChapterDetailComponent implements OnInit {
|
||||
|
||||
updateUrl(activeTab: TabID) {
|
||||
const newUrl = `${this.router.url.split('#')[0]}#${activeTab}`;
|
||||
this.router.navigateByUrl(newUrl, { onSameUrlNavigation: 'ignore' });
|
||||
//this.router.navigateByUrl(newUrl, { onSameUrlNavigation: 'ignore' });
|
||||
}
|
||||
|
||||
openPerson(field: FilterField, value: number) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
.card-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@
|
||||
|
||||
<div class="col-auto ms-2 d-none d-md-block">
|
||||
<div class="card-actions" [ngbTooltip]="t('more-alt')">
|
||||
<app-card-actionables (actionHandler)="performAction($event)" [actions]="seriesActions" [labelBy]="series.name" iconClass="fa-ellipsis-h" btnClass="btn-secondary-outline btn-sm"></app-card-actionables>
|
||||
<app-card-actionables (actionHandler)="performAction($event)" [actions]="seriesActions" [labelBy]="series.name" iconClass="fa-ellipsis-h" btnClass="btn-secondary-outline btn"></app-card-actionables>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -161,37 +161,70 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (WebLinks.length > 0) {
|
||||
<div class="mt-3 mb-2">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('weblinks-title')}}</span>
|
||||
<div>
|
||||
@for(link of WebLinks; track link) {
|
||||
<a class="me-1" [href]="link | safeHtml" target="_blank" rel="noopener noreferrer" [title]="link">
|
||||
<app-image height="24px" width="24px" aria-hidden="true" [imageUrl]="imageService.getWebLinkImage(link)"
|
||||
[errorImage]="imageService.errorWebLinkImage"></app-image>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="mt-3 mb-2">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('genres-title')}}</span>
|
||||
<div>
|
||||
<app-badge-expander [items]="seriesMetadata.genres" [itemsTillExpander]="5">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx" let-last="last">
|
||||
<a href="javascript:void(0)" class="dark-exempt btn-icon" (click)="openFilter(FilterField.Genres, item.id)">{{item.title}}</a>
|
||||
@if (!last) {
|
||||
,
|
||||
}
|
||||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('publication-status-title')}}</span>
|
||||
<div>
|
||||
@if (seriesMetadata.publicationStatus | publicationStatus; as pubStatus) {
|
||||
<i class="fa-solid fa-hourglass-{{pubStatus === t('ongoing') ? 'empty' : 'end'}}" aria-hidden="true"></i>
|
||||
<a class="dark-exempt btn-icon" (click)="openFilter(FilterField.PublicationStatus, seriesMetadata.publicationStatus)"
|
||||
href="javascript:void(0);"
|
||||
[ngbTooltip]="t('publication-status-tooltip') + (seriesMetadata.totalCount === 0 ? '' : ' (' + seriesMetadata.maxCount + ' / ' + seriesMetadata.totalCount + ')')">
|
||||
{{pubStatus}}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('tags-title')}}</span>
|
||||
<div>
|
||||
<app-badge-expander [items]="seriesMetadata.tags" [itemsTillExpander]="5">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx" let-last="last">
|
||||
<a href="javascript:void(0)" class="dark-exempt btn-icon" (click)="openFilter(FilterField.Tags, item.id)">{{item.title}}</a>
|
||||
@if (!last) {
|
||||
,
|
||||
}
|
||||
</ng-template>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-3 mb-2">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('weblinks-title')}}</span>
|
||||
<div>
|
||||
@for(link of WebLinks; track link) {
|
||||
<a class="me-1" [href]="link | safeHtml" target="_blank" rel="noopener noreferrer" [title]="link">
|
||||
<app-image height="24px" width="24px" aria-hidden="true" [imageUrl]="imageService.getWebLinkImage(link)"
|
||||
[errorImage]="imageService.errorWebLinkImage"></app-image>
|
||||
</a>
|
||||
} @empty {
|
||||
{{null | defaultValue}}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<span class="fw-bold">{{t('publication-status-title')}}</span>
|
||||
<div>
|
||||
@if (seriesMetadata.publicationStatus | publicationStatus; as pubStatus) {
|
||||
<a class="dark-exempt btn-icon" (click)="openFilter(FilterField.PublicationStatus, seriesMetadata.publicationStatus)"
|
||||
href="javascript:void(0);"
|
||||
[ngbTooltip]="t('publication-status-tooltip') + (seriesMetadata.totalCount === 0 ? '' : ' (' + seriesMetadata.maxCount + ' / ' + seriesMetadata.totalCount + ')')">
|
||||
{{pubStatus}}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
//
|
||||
.card-container{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ import {VirtualScrollerModule} from '@iharbeck/ngx-virtual-scroller';
|
||||
import {BulkOperationsComponent} from '../../../cards/bulk-operations/bulk-operations.component';
|
||||
import {ReviewCardComponent} from '../../../_single-module/review-card/review-card.component';
|
||||
import {CarouselReelComponent} from '../../../carousel/_components/carousel-reel/carousel-reel.component';
|
||||
import {SeriesMetadataDetailComponent} from '../series-metadata-detail/series-metadata-detail.component';
|
||||
import {ImageComponent} from '../../../shared/image/image.component';
|
||||
import {TagBadgeComponent} from '../../../shared/tag-badge/tag-badge.component';
|
||||
import {
|
||||
@ -175,7 +174,7 @@ interface StoryLineItem {
|
||||
standalone: true,
|
||||
imports: [SideNavCompanionBarComponent, CardActionablesComponent, ReactiveFormsModule, NgStyle,
|
||||
TagBadgeComponent, ImageComponent, NgbTooltip, NgbProgressbar, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu,
|
||||
NgbDropdownItem, SeriesMetadataDetailComponent, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent,
|
||||
NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent,
|
||||
NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, CardItemComponent,
|
||||
EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
|
||||
LoadingComponent, DecimalPipe, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent,
|
||||
@ -446,7 +445,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
|
||||
|
||||
constructor(@Inject(DOCUMENT) private document: Document) {
|
||||
//this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
||||
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
||||
|
||||
|
||||
this.accountService.currentUser$.subscribe(user => {
|
||||
@ -553,8 +552,15 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
}
|
||||
|
||||
updateUrl(activeTab: TabID) {
|
||||
const newUrl = `${this.router.url.split('#')[0]}#${activeTab}`;
|
||||
this.router.navigateByUrl(newUrl, { onSameUrlNavigation: 'ignore' });
|
||||
var tokens = this.router.url.split('#');
|
||||
const newUrl = `${tokens[0]}#${activeTab}`;
|
||||
|
||||
// if (tokens.length === 1 || tokens[1] === activeTab + '') {
|
||||
// return;
|
||||
// }
|
||||
console.log('url:', newUrl);
|
||||
|
||||
//this.router.navigateByUrl(newUrl, { skipLocationChange: true, replaceUrl: true });
|
||||
}
|
||||
|
||||
handleSeriesActionCallback(action: ActionItem<Series>, series: Series) {
|
||||
@ -869,7 +875,6 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
* This assumes loadPage() has already primed all the calculations and state variables. Do not call directly.
|
||||
*/
|
||||
updateSelectedTab() {
|
||||
console.log('updateSelectedTab')
|
||||
// Book libraries only have Volumes or Specials enabled
|
||||
if (this.libraryType === LibraryType.Book || this.libraryType === LibraryType.LightNovel) {
|
||||
if (this.volumes.length === 0) {
|
||||
|
@ -1,168 +0,0 @@
|
||||
<ng-container *transloco="let t; read: 'series-metadata-detail'">
|
||||
<!-- <div class="row g-0 mt-2 mb-2">-->
|
||||
<!-- <app-read-more [text]="seriesSummary" [maxLength]="utilityService.getActiveBreakpoint() >= Breakpoint.Desktop ? 1000 : 250"></app-read-more>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- Ratings -->
|
||||
<app-metadata-detail [tags]="['']" [libraryId]="series.libraryId" [heading]="t('rating-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-external-rating [seriesId]="series.id" [ratings]="ratings" [userRating]="series.userRating" [hasUserRated]="series.hasUserRated" [libraryType]="libraryType"></app-external-rating>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<!-- Weblinks -->
|
||||
<ng-container *ngIf="WebLinks as links">
|
||||
<app-metadata-detail [tags]="links" [libraryId]="series.libraryId" [heading]="t('links-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<a class="col me-1" [href]="item | safeHtml" target="_blank" rel="noopener noreferrer" [title]="item">
|
||||
<app-image classes="favicon" width="24px" height="24px"
|
||||
[imageUrl]="imageService.getWebLinkImage(item)"
|
||||
[errorImage]="imageService.errorWebLinkImage"/>
|
||||
</a>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<!-- Genres -->
|
||||
<app-metadata-detail [tags]="seriesMetadata.genres" [libraryId]="series.libraryId" [queryParam]="FilterField.Genres" [heading]="t('genres-title')">
|
||||
<ng-template #titleTemplate let-item>{{item.title}}</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<!-- Tags -->
|
||||
<app-metadata-detail [tags]="seriesMetadata.tags" [libraryId]="series.libraryId" [queryParam]="FilterField.Tags" [heading]="t('tags-title')">
|
||||
<ng-template #titleTemplate let-item>{{item.title}}</ng-template>
|
||||
</app-metadata-detail>
|
||||
|
||||
<!-- Collections -->
|
||||
@if (collections$) {
|
||||
<app-metadata-detail [tags]="(collections$ | async)!" [libraryId]="series.libraryId" [heading]="t('collections-title')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-tag-badge a11y-click="13,32" class="col-auto" (click)="navigate('collections', item.id)" [selectionMode]="TagBadgeCursor.Clickable">
|
||||
<app-promoted-icon [promoted]="item.promoted"></app-promoted-icon> {{item.title}}
|
||||
</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
}
|
||||
|
||||
|
||||
<!-- Reading Lists -->
|
||||
<!-- <app-metadata-detail [tags]="readingLists" [libraryId]="series.libraryId" [heading]="t('reading-lists-title')">-->
|
||||
<!-- <ng-template #itemTemplate let-item>-->
|
||||
<!-- <app-tag-badge a11y-click="13,32" class="col-auto" (click)="navigate('lists', item.id)" [selectionMode]="TagBadgeCursor.Clickable">-->
|
||||
<!-- <span *ngIf="item.promoted">-->
|
||||
<!-- <i class="fa fa-angle-double-up" aria-hidden="true"></i> -->
|
||||
<!-- <span class="visually-hidden">({{t('promoted')}})</span>-->
|
||||
<!-- </span>-->
|
||||
<!-- {{item.title}}-->
|
||||
<!-- </app-tag-badge>-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
<!-- Key Person Information -->
|
||||
<!-- @if (libraryType === LibraryType.LightNovel || libraryType === LibraryType.Book) {-->
|
||||
<!-- -->
|
||||
|
||||
<!-- <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.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>-->
|
||||
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" id="extended-series-metadata">
|
||||
|
||||
|
||||
<!-- <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.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>-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
<!-- <app-metadata-detail [tags]="seriesMetadata.editors" [libraryId]="series.libraryId" [queryParam]="FilterField.Editor" [heading]="t('editors-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.inkers" [libraryId]="series.libraryId" [queryParam]="FilterField.Inker" [heading]="t('inkers-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.letterers" [libraryId]="series.libraryId" [queryParam]="FilterField.Letterer" [heading]="t('letterers-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 #titleTemplate let-item>-->
|
||||
<!-- {{item.name}}-->
|
||||
<!-- </ng-template>-->
|
||||
<!-- </app-metadata-detail>-->
|
||||
|
||||
<!-- <app-metadata-detail [tags]="seriesMetadata.locations" [libraryId]="series.libraryId" [queryParam]="FilterField.Location" [heading]="t('locations-title')">-->
|
||||
<!-- <ng-template #titleTemplate let-item>-->
|
||||
<!-- {{item.name}}-->
|
||||
<!-- </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 #titleTemplate let-item>-->
|
||||
<!-- {{item.name}}-->
|
||||
<!-- </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">
|
||||
<hr class="col mt-3" *ngIf="hasExtendedProperties" >
|
||||
<a [class.hidden]="hasExtendedProperties" *ngIf="hasExtendedProperties"
|
||||
class="col col-md-auto align-self-end read-more-link" (click)="toggleView()">
|
||||
<i aria-hidden="true" class="fa fa-caret-{{isCollapsed ? 'down' : 'up'}} me-1" aria-controls="extended-series-metadata"></i>
|
||||
{{isCollapsed ? t('see-more') : t('see-less')}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<app-series-info-cards [series]="series" [seriesMetadata]="seriesMetadata" (goTo)="handleGoTo($event)" [hasReadingProgress]="hasReadingProgress"></app-series-info-cards>
|
||||
|
||||
</ng-container>
|
@ -1,3 +0,0 @@
|
||||
.favicon {
|
||||
border-radius: 5px;
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component, DestroyRef,
|
||||
inject,
|
||||
Input,
|
||||
OnChanges, OnInit,
|
||||
SimpleChanges,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {TagBadgeComponent, TagBadgeCursor} from '../../../shared/tag-badge/tag-badge.component';
|
||||
import {FilterUtilitiesService} from '../../../shared/_services/filter-utilities.service';
|
||||
import {Breakpoint, UtilityService} from '../../../shared/_services/utility.service';
|
||||
import {MangaFormat} from '../../../_models/manga-format';
|
||||
import {ReadingList} from '../../../_models/reading-list';
|
||||
import {Series} from '../../../_models/series';
|
||||
import {SeriesMetadata} from '../../../_models/metadata/series-metadata';
|
||||
import {ImageService} from 'src/app/_services/image.service';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component";
|
||||
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
|
||||
import {ExternalRatingComponent} from "../external-rating/external-rating.component";
|
||||
import {ReadMoreComponent} from "../../../shared/read-more/read-more.component";
|
||||
import {A11yClickDirective} from "../../../shared/a11y-click.directive";
|
||||
import {PersonBadgeComponent} from "../../../shared/person-badge/person-badge.component";
|
||||
import {NgbCollapse} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {SeriesInfoCardsComponent} from "../../../cards/series-info-cards/series-info-cards.component";
|
||||
import {LibraryType} from "../../../_models/library/library";
|
||||
import {MetadataDetailComponent} from "../metadata-detail/metadata-detail.component";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
||||
import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
|
||||
import {ImageComponent} from "../../../shared/image/image.component";
|
||||
import {Rating} from "../../../_models/rating";
|
||||
import {CollectionTagService} from "../../../_services/collection-tag.service";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {shareReplay} from "rxjs/operators";
|
||||
import {PromotedIconComponent} from "../../../shared/_components/promoted-icon/promoted-icon.component";
|
||||
import {Observable} from "rxjs";
|
||||
import {UserCollection} from "../../../_models/collection-tag";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-metadata-detail',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TagBadgeComponent, BadgeExpanderComponent, SafeHtmlPipe, ExternalRatingComponent,
|
||||
ReadMoreComponent, A11yClickDirective, PersonBadgeComponent, NgbCollapse, SeriesInfoCardsComponent,
|
||||
MetadataDetailComponent, TranslocoDirective, ImageComponent, PromotedIconComponent],
|
||||
templateUrl: './series-metadata-detail.component.html',
|
||||
styleUrls: ['./series-metadata-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class SeriesMetadataDetailComponent implements OnChanges, OnInit {
|
||||
|
||||
protected readonly imageService = inject(ImageService);
|
||||
protected readonly utilityService = inject(UtilityService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
private readonly collectionTagService = inject(CollectionTagService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
protected readonly FilterField = FilterField;
|
||||
protected readonly LibraryType = LibraryType;
|
||||
protected readonly MangaFormat = MangaFormat;
|
||||
protected readonly TagBadgeCursor = TagBadgeCursor;
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
@Input({required: true}) seriesMetadata!: SeriesMetadata;
|
||||
@Input({required: true}) libraryType!: LibraryType;
|
||||
@Input() hasReadingProgress: boolean = false;
|
||||
/**
|
||||
* Reading lists with a connection to the Series
|
||||
*/
|
||||
@Input() readingLists: Array<ReadingList> = [];
|
||||
@Input({required: true}) series!: Series;
|
||||
@Input({required: true}) ratings: Array<Rating> = [];
|
||||
|
||||
isCollapsed: boolean = true;
|
||||
hasExtendedProperties: boolean = false;
|
||||
|
||||
/**
|
||||
* Html representation of Series Summary
|
||||
*/
|
||||
seriesSummary: string = '';
|
||||
collections$: Observable<UserCollection[]> | undefined;
|
||||
|
||||
get WebLinks() {
|
||||
if (this.seriesMetadata?.webLinks === '') return [];
|
||||
return this.seriesMetadata?.webLinks.split(',') || [];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// If on desktop, we can just have all the data expanded by default:
|
||||
this.isCollapsed = true; // 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.collections$ = this.collectionTagService.allCollectionsForSeries(this.series.id).pipe(
|
||||
takeUntilDestroyed(this.destroyRef), shareReplay({bufferSize: 1, refCount: 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 ||
|
||||
this.seriesMetadata.inkers.length > 0 ||
|
||||
this.seriesMetadata.letterers.length > 0 ||
|
||||
this.seriesMetadata.pencillers.length > 0 ||
|
||||
this.seriesMetadata.publishers.length > 0 ||
|
||||
this.seriesMetadata.characters.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>');
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
toggleView() {
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
handleGoTo(event: {queryParamName: FilterField, filter: any}) {
|
||||
this.goTo(event.queryParamName, event.filter);
|
||||
}
|
||||
|
||||
goTo(queryParamName: FilterField, filter: any) {
|
||||
this.filterUtilityService.applyFilter(['library', this.series.libraryId], queryParamName,
|
||||
FilterComparison.Equal, filter).subscribe();
|
||||
}
|
||||
|
||||
navigate(basePage: string, id: number) {
|
||||
this.router.navigate([basePage, id]);
|
||||
}
|
||||
}
|
@ -88,6 +88,7 @@ export class SideNavItemComponent implements OnInit {
|
||||
triggerHighlightCheck(routeUrl: string) {
|
||||
const [url, queryParams] = routeUrl.split('?');
|
||||
const [page, fragment = ''] = url.split('#');
|
||||
|
||||
this.updateHighlight(page, queryParams, url.includes('#') ? fragment : undefined);
|
||||
}
|
||||
|
||||
@ -99,7 +100,7 @@ export class SideNavItemComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page.endsWith('/') && !queryParams && this.fragment === undefined) {
|
||||
if (!page.endsWith('/') && !queryParams && this.fragment === undefined && queryParams === undefined) {
|
||||
page = page + '/';
|
||||
}
|
||||
|
||||
@ -111,8 +112,9 @@ export class SideNavItemComponent implements OnInit {
|
||||
fragmentEqual = true;
|
||||
}
|
||||
|
||||
const queryParamsEqual = this.queryParams === queryParams;
|
||||
|
||||
if (this.comparisonMethod === 'equals' && page === this.link && fragmentEqual) {
|
||||
if (this.comparisonMethod === 'equals' && page === this.link && fragmentEqual && queryParamsEqual) {
|
||||
this.highlighted = true;
|
||||
this.cdRef.markForCheck();
|
||||
return;
|
||||
|
@ -7,22 +7,6 @@
|
||||
<form [formGroup]="settingsForm">
|
||||
<h4>{{t('global-settings-title')}}</h4>
|
||||
<ng-container>
|
||||
<div class="row g-0 mt-4 mb-4">
|
||||
<app-setting-item [title]="t('page-layout-mode-label')" [subtitle]="t('page-layout-mode-tooltip')">
|
||||
<ng-template #view>
|
||||
{{settingsForm.get('globalPageLayoutMode')!.value | pageLayoutMode}}
|
||||
</ng-template>
|
||||
<ng-template #edit>
|
||||
<select class="form-select" aria-describedby="manga-header"
|
||||
formControlName="globalPageLayoutMode">
|
||||
@for (opt of pageLayoutModes; track opt) {
|
||||
<option [value]="opt.value">{{opt.value | pageLayoutMode}}</option>
|
||||
}
|
||||
</select>
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row g-0 mt-4 mb-4">
|
||||
<app-setting-item [title]="t('locale-label')" [subtitle]="t('locale-tooltip')">
|
||||
|
@ -1,6 +1,6 @@
|
||||
.chooser {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
.card-container{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-template-columns: repeat(auto-fill, 160px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -437,7 +437,7 @@ export class VolumeDetailComponent implements OnInit {
|
||||
|
||||
updateUrl(activeTab: TabID) {
|
||||
const newUrl = `${this.router.url.split('#')[0]}#${activeTab}`;
|
||||
this.router.navigateByUrl(newUrl, { onSameUrlNavigation: 'ignore' });
|
||||
//this.router.navigateByUrl(newUrl, { onSameUrlNavigation: 'ignore' });
|
||||
}
|
||||
|
||||
openPerson(field: FilterField, value: number) {
|
||||
|
@ -801,19 +801,22 @@
|
||||
"user-reviews-local": "Local Reviews",
|
||||
"user-reviews-plus": "External Reviews",
|
||||
|
||||
"writers-title": "Writers",
|
||||
"cover-artists-title": "Artists",
|
||||
"characters-title": "Characters",
|
||||
"colorists-title": "Colorists",
|
||||
"editors-title": "Editors",
|
||||
"inkers-title": "Inkers",
|
||||
"letterers-title": "Letterers",
|
||||
"translators-title": "Translators",
|
||||
"pencillers-title": "Pencillers",
|
||||
"publishers-title": "Publishers",
|
||||
"imprints-title": "Imprints",
|
||||
"teams-title": "Teams",
|
||||
"locations-title": "Locations",
|
||||
"writers-title": "{{metadata-fields.writers-title}}",
|
||||
"cover-artists-title": "{{metadata-fields.cover-artists-title}}",
|
||||
"characters-title": "{{metadata-fields.characters-title}}",
|
||||
"colorists-title": "{{metadata-fields.colorists-title}}",
|
||||
"editors-title": "{{metadata-fields.editors-title}}",
|
||||
"inkers-title": "{{metadata-fields.inkers-title}}",
|
||||
"letterers-title": "{{metadata-fields.letterers-title}}",
|
||||
"translators-title": "{{metadata-fields.translators-title}}",
|
||||
"pencillers-title": "{{metadata-fields.pencillers-title}}",
|
||||
"publishers-title": "{{metadata-fields.publishers-title}}",
|
||||
"imprints-title": "{{metadata-fields.imprints-title}}",
|
||||
"teams-title": "{{metadata-fields.teams-title}}",
|
||||
"locations-title": "{{metadata-fields.locations-title}}",
|
||||
"genres-title": "{{metadata-fields.genres-title}}",
|
||||
"tags-title": "{{metadata-fields.tags-title}}",
|
||||
"ongoing": "{{publication-status-pipe.ongoing}}",
|
||||
|
||||
"volume-num": "{{common.volume-num}}",
|
||||
"reading-lists-title": "{{side-nav.reading-lists}}",
|
||||
@ -828,16 +831,13 @@
|
||||
"publication-status-tooltip": "Publication Status"
|
||||
},
|
||||
|
||||
"series-metadata-detail": {
|
||||
"links-title": "Links",
|
||||
"rating-title": "Ratings",
|
||||
"genres-title": "Genres",
|
||||
"tags-title": "Tags",
|
||||
"metadata-fields": {
|
||||
"collections-title": "{{side-nav.collections}}",
|
||||
"reading-lists-title": "{{side-nav.reading-lists}}",
|
||||
|
||||
"genres-title": "Genres",
|
||||
"tags-title": "Tags",
|
||||
"writers-title": "Writers",
|
||||
"cover-artists-title": "Cover Artists",
|
||||
"cover-artists-title": "Artists",
|
||||
"characters-title": "Characters",
|
||||
"colorists-title": "Colorists",
|
||||
"editors-title": "Editors",
|
||||
@ -848,12 +848,7 @@
|
||||
"publishers-title": "Publishers",
|
||||
"imprints-title": "Imprints",
|
||||
"teams-title": "Teams",
|
||||
"locations-title": "Locations",
|
||||
|
||||
"promoted": "{{common.promoted}}",
|
||||
"see-more": "See More",
|
||||
"see-less": "See Less"
|
||||
|
||||
"locations-title": "Locations"
|
||||
},
|
||||
|
||||
"download-button": {
|
||||
@ -1422,7 +1417,7 @@
|
||||
"bust-cache-task-success": "Kavita+ Cache busted",
|
||||
|
||||
"bust-locale-task": "Bust Locale Cache",
|
||||
"bust-locale-task-desc": "Busts the Locale Cache. This can fix issues with strings not showing correctly after an update",
|
||||
"bust-locale-task-desc": "Busts the Locale Cache. This can fix issues with strings not showing correctly after an update. A browser refresh is needed.",
|
||||
"bust-locale-task-success": "Locale Cache busted",
|
||||
|
||||
"clear-reading-cache-task": "Clear Reading Cache",
|
||||
@ -1641,7 +1636,7 @@
|
||||
"chapters": "Chapters",
|
||||
"people": "People",
|
||||
"tags": "Tags",
|
||||
"genres": "Genres",
|
||||
"genres": "{{metadata-fields.genres-title}}",
|
||||
"bookmarks": "{{side-nav.bookmarks}}",
|
||||
"libraries": "Libraries",
|
||||
"reading-lists": "Reading Lists",
|
||||
@ -1842,8 +1837,8 @@
|
||||
"format-label": "Format",
|
||||
"libraries-label": "Libraries",
|
||||
"collections-label": "Collections",
|
||||
"genres-label": "Genres",
|
||||
"tags-label": "Tags",
|
||||
"genres-label": "{{metadata-fields.genres-title}}",
|
||||
"tags-label": "{{metadata-fields.tags-title}}",
|
||||
"cover-artist-label": "Cover Artist",
|
||||
"writer-label": "Writer",
|
||||
"publisher-label": "Publisher",
|
||||
@ -1897,8 +1892,9 @@
|
||||
"related-tab": "{{tabs.related-tab}}",
|
||||
"info-tab": "{{tabs.info-tab}}",
|
||||
"tasks-tab": "{{tabs.tasks-tab}}",
|
||||
"genres-label": "Genres",
|
||||
"tags-label": "Tags",
|
||||
|
||||
"genres-label": "{{metadata-fields.genres-title}}",
|
||||
"tags-label": "{{metadata-fields.tags-title}}",
|
||||
"cover-artist-label": "Cover Artist",
|
||||
"writer-label": "Writer",
|
||||
"publisher-label": "Publisher",
|
||||
@ -1914,6 +1910,7 @@
|
||||
"location-label": "{{filter-field-pipe.location}}",
|
||||
"language-label": "Language",
|
||||
"age-rating-label": "Age Rating",
|
||||
|
||||
"publication-status-label": "Publication Status",
|
||||
"required-field": "{{validation.required-field}}",
|
||||
"close": "{{common.close}}",
|
||||
@ -1928,24 +1925,28 @@
|
||||
"save": "{{common.save}}",
|
||||
"field-locked-alt": "Field is locked",
|
||||
"info-title": "Information",
|
||||
"library-title": "Library:",
|
||||
"format-title": "Format:",
|
||||
"created-title": "Created:",
|
||||
"last-read-title": "Last Read:",
|
||||
"last-added-title": "Last Item Added:",
|
||||
"last-scanned-title": "Last Scanned:",
|
||||
"folder-path-title": "Folder Path:",
|
||||
"publication-status-title": "Publication Status:",
|
||||
"total-pages-title": "Total Pages:",
|
||||
"total-items-title": "Total Items:",
|
||||
"max-items-title": "Max Items:",
|
||||
"size-title": "Size:",
|
||||
"library-title": "Library",
|
||||
"format-title": "Format",
|
||||
"created-title": "Created",
|
||||
"last-read-title": "Last Read",
|
||||
"last-added-title": "Last Item Added",
|
||||
"last-scanned-title": "Last Scanned",
|
||||
"folder-path-title": "Folder Path",
|
||||
"folder-path-tooltip": "Highest path from library root that contains all series files",
|
||||
"lowest-folder-path-title": "Lowest Folder Path",
|
||||
"lowest-folder-path-tooltip": "Lowest path from library root that contains all series files",
|
||||
"publication-status-title": "Publication Status",
|
||||
"total-pages-title": "Total Pages",
|
||||
"total-items-title": "Total Items",
|
||||
"max-items-title": "Max Items",
|
||||
"size-title": "Size",
|
||||
"loading": "{{common.loading}}",
|
||||
"added-title": "Added:",
|
||||
"last-modified-title": "Last Modified:",
|
||||
"added-title": "Added",
|
||||
"last-modified-title": "Last Modified",
|
||||
"view-files": "View Files",
|
||||
"pages-title": "Pages:",
|
||||
"chapter-title": "Chapter:",
|
||||
"pages-title": "Pages",
|
||||
"words-title": "Words",
|
||||
"chapter-title": "Chapter",
|
||||
"volume-num": "{{common.volume-num}}",
|
||||
"highest-count-tooltip": "Highest Count found across all ComicInfo in the Series",
|
||||
"max-issue-tooltip": "Max Issue or Volume field from all ComicInfo in the series",
|
||||
@ -2143,9 +2144,9 @@
|
||||
"genre-count": "{{num}} Genres",
|
||||
"tag-count": "{{num}} Tags",
|
||||
"people-count": "{{num}} People",
|
||||
"tags": "Tags",
|
||||
"tags": "{{metadata-fields.tags-title}}",
|
||||
"people": "People",
|
||||
"genres": "Genres"
|
||||
"genres": "{{metadata-fields.genres-title}}"
|
||||
},
|
||||
|
||||
"errors": {
|
||||
@ -2253,13 +2254,13 @@
|
||||
|
||||
"filter-field-pipe": {
|
||||
"age-rating": "Age Rating",
|
||||
"characters": "Characters",
|
||||
"characters": "{{metadata-fields.characters-title}}",
|
||||
"collection-tags": "Collection Tags",
|
||||
"colorist": "Colorist",
|
||||
"cover-artist": "Cover Artist",
|
||||
"editor": "Editor",
|
||||
"formats": "Formats",
|
||||
"genres": "Genres",
|
||||
"genres": "{{metadata-fields.genres-title}}",
|
||||
"inker": "Inker",
|
||||
"team": "Team",
|
||||
"location": "Location",
|
||||
@ -2275,10 +2276,10 @@
|
||||
"release-year": "Release Year",
|
||||
"series-name": "Series Name",
|
||||
"summary": "Summary",
|
||||
"tags": "Tags",
|
||||
"tags": "{{metadata-fields.tags-title}}",
|
||||
"translators": "Translators",
|
||||
"user-rating": "User Rating",
|
||||
"writers": "Writers",
|
||||
"writers": "{{metadata-fields.writers-title}}",
|
||||
"path": "Path",
|
||||
"file-path": "File Path",
|
||||
"want-to-read": "Want to Read",
|
||||
|
@ -1,3 +1,9 @@
|
||||
@include media-breakpoint-down(md) {
|
||||
.card-actions {
|
||||
visibility: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--card-bg-color);
|
||||
color: var(--card-text-color);
|
||||
@ -21,7 +27,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$image-height: 230px;
|
||||
$image-height: 232.91px;
|
||||
$image-filter-height: 160px;
|
||||
$image-width: 160px;
|
||||
|
||||
|
56
openapi.json
56
openapi.json
@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.1",
|
||||
"info": {
|
||||
"title": "Kavita",
|
||||
"description": "Kavita provides a set of APIs that are authenticated by JWT. JWT token can be copied from local storage. Assume all fields of a payload are required. Built against v0.8.2.3",
|
||||
"description": "Kavita provides a set of APIs that are authenticated by JWT. JWT token can be copied from local storage. Assume all fields of a payload are required. Built against v0.8.2.4",
|
||||
"license": {
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
@ -1120,17 +1120,6 @@
|
||||
"Cbl"
|
||||
],
|
||||
"summary": "The first step in a cbl import. This validates the cbl file that if an import occured, would it be successful.\r\nIf this returns errors, the cbl will always be rejected by Kavita.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "comicVineMatching",
|
||||
"in": "query",
|
||||
"description": "Use comic vine matching or not. Defaults to false",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
@ -1140,12 +1129,19 @@
|
||||
"cbl": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
},
|
||||
"comicVineMatching": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"cbl": {
|
||||
"style": "form"
|
||||
},
|
||||
"comicVineMatching": {
|
||||
"style": "form"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1181,26 +1177,6 @@
|
||||
"Cbl"
|
||||
],
|
||||
"summary": "Performs the actual import (assuming dryRun = false)",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dryRun",
|
||||
"in": "query",
|
||||
"description": "If true, will only emulate the import but not perform. This should be done to preview what will happen",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "comicVineMatching",
|
||||
"in": "query",
|
||||
"description": "Use comic vine matching or not. Defaults to false",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
@ -1210,12 +1186,26 @@
|
||||
"cbl": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
},
|
||||
"dryRun": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"comicVineMatching": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"cbl": {
|
||||
"style": "form"
|
||||
},
|
||||
"dryRun": {
|
||||
"style": "form"
|
||||
},
|
||||
"comicVineMatching": {
|
||||
"style": "form"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12778,7 +12768,7 @@
|
||||
"Upload"
|
||||
],
|
||||
"summary": "Replaces volume cover image and locks it with a base64 encoded image.",
|
||||
"description": "This is a helper API for Komf - Kavita UI does not use. Volume will find first chapter to update.",
|
||||
"description": "This will not update the underlying chapter",
|
||||
"requestBody": {
|
||||
"description": "",
|
||||
"content": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user