diff --git a/API/Controllers/CBLController.cs b/API/Controllers/CBLController.cs index e84ef00e2..79d5b4896 100644 --- a/API/Controllers/CBLController.cs +++ b/API/Controllers/CBLController.cs @@ -34,7 +34,7 @@ public class CblController : BaseApiController /// Use comic vine matching or not. Defaults to false /// [HttpPost("validate")] - public async Task> ValidateCbl(IFormFile cbl, bool comicVineMatching = false) + public async Task> ValidateCbl(IFormFile cbl, [FromForm] bool comicVineMatching = false) { var userId = User.GetUserId(); try @@ -85,7 +85,7 @@ public class CblController : BaseApiController /// Use comic vine matching or not. Defaults to false /// [HttpPost("import")] - public async Task> ImportCbl(IFormFile cbl, bool dryRun = false, bool comicVineMatching = false) + public async Task> ImportCbl(IFormFile cbl, [FromForm] bool dryRun = false, [FromForm] bool comicVineMatching = false) { try { diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index 6d0f7e8dd..d25321ad8 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -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(chapter); foreach (var mangaFile in chapter.Files) { @@ -889,7 +891,6 @@ public class OpdsController : BaseApiController chapterDto, apiKey, prefix, baseUrl)); } } - } var chapters = seriesDetail.StorylineChapters; diff --git a/API/Controllers/UploadController.cs b/API/Controllers/UploadController.cs index bfa958546..e89904deb 100644 --- a/API/Controllers/UploadController.cs +++ b/API/Controllers/UploadController.cs @@ -318,7 +318,7 @@ public class UploadController : BaseApiController /// /// Replaces volume cover image and locks it with a base64 encoded image. /// - /// This is a helper API for Komf - Kavita UI does not use. Volume will find first chapter to update. + /// This will not update the underlying chapter /// /// [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(); diff --git a/API/DTOs/SeriesDto.cs b/API/DTOs/SeriesDto.cs index abe1b5000..15fe6a9a1 100644 --- a/API/DTOs/SeriesDto.cs +++ b/API/DTOs/SeriesDto.cs @@ -59,6 +59,11 @@ public class SeriesDto : IHasReadTimeEstimate, IHasCoverImage /// public string FolderPath { get; set; } = default!; /// + /// Lowest path (that is under library root) that contains all files for the series. + /// + /// must be used before setting + public string? LowestFolderPath { get; set; } + /// /// The last time the folder for this series was scanned /// public DateTime LastFolderScanned { get; set; } diff --git a/API/Data/Repositories/VolumeRepository.cs b/API/Data/Repositories/VolumeRepository.cs index 0e1050c49..48159386d 100644 --- a/API/Data/Repositories/VolumeRepository.cs +++ b/API/Data/Repositories/VolumeRepository.cs @@ -44,6 +44,7 @@ public interface IVolumeRepository Task> GetVolumes(int seriesId); Task GetVolumeByIdAsync(int volumeId); Task> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat); + Task> GetCoverImagesForLockedVolumesAsync(); } public class VolumeRepository : IVolumeRepository { @@ -252,4 +253,17 @@ public class VolumeRepository : IVolumeRepository .Sum(p => p.PagesRead); } } + + /// + /// Returns cover images for locked chapters + /// + /// + public async Task> GetCoverImagesForLockedVolumesAsync() + { + return (await _context.Volume + .Where(c => c.CoverImageLocked) + .Select(c => c.CoverImage) + .Where(t => !string.IsNullOrEmpty(t)) + .ToListAsync())!; + } } diff --git a/API/Services/ImageService.cs b/API/Services/ImageService.cs index 22b1349d5..eb9f49263 100644 --- a/API/Services/ImageService.cs +++ b/API/Services/ImageService.cs @@ -739,6 +739,16 @@ public class ImageService : IImageService return $"v{volumeId}_c{chapterId}"; } + /// + /// Returns the name format for a volume cover image (custom) + /// + /// + /// + public static string GetVolumeFormat(int volumeId) + { + return $"v{volumeId}"; + } + /// /// Returns the name format for a library cover image /// diff --git a/API/Services/ReadingListService.cs b/API/Services/ReadingListService.cs index 00d968ec1..36048ae3d 100644 --- a/API/Services/ReadingListService.cs +++ b/API/Services/ReadingListService.cs @@ -550,6 +550,7 @@ public class ReadingListService : IReadingListService Results = new List(), SuccessfulInserts = new List() }; + if (IsCblEmpty(cblReading, importSummary, out var readingListFromCbl)) return readingListFromCbl; // Is there another reading list with the same name? diff --git a/API/Services/Tasks/BackupService.cs b/API/Services/Tasks/BackupService.cs index 60e0e8dc3..e2ed61ba1 100644 --- a/API/Services/Tasks/BackupService.cs +++ b/API/Services/Tasks/BackupService.cs @@ -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); diff --git a/UI/Web/src/_card-item-common.scss b/UI/Web/src/_card-item-common.scss index 486f2b61d..0634e574f 100644 --- a/UI/Web/src/_card-item-common.scss +++ b/UI/Web/src/_card-item-common.scss @@ -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; } } } -} \ No newline at end of file +} diff --git a/UI/Web/src/app/_models/series.ts b/UI/Web/src/app/_models/series.ts index 49930b891..ef460a9f5 100644 --- a/UI/Web/src/app/_models/series.ts +++ b/UI/Web/src/app/_models/series.ts @@ -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 */ diff --git a/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts b/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts index 3f4da39a7..6f5463cf2 100644 --- a/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts +++ b/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts @@ -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: `${result.series}`}); case CblImportReason.SeriesMissing: diff --git a/UI/Web/src/app/_services/nav.service.ts b/UI/Web/src/app/_services/nav.service.ts index 7a2fa2f5a..5364234b9 100644 --- a/UI/Web/src/app/_services/nav.service.ts +++ b/UI/Web/src/app/_services/nav.service.ts @@ -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); diff --git a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html index a8acc7a9c..b3ef54146 100644 --- a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html +++ b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html @@ -16,7 +16,7 @@ {{t('cover-image-description')}}

+ [showReset]="true" (resetClicked)="handleReset()"> @@ -34,15 +34,15 @@ - - - - - - - - - +
+
+ + + {{t('words-count', {num: volume.wordCount | compactNumber})}} + + +
+
diff --git a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.ts b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.ts index 760f38f47..92e08cfef 100644 --- a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.ts +++ b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.ts @@ -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); }); } diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss index 8d9a36f78..abab89f35 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss @@ -54,6 +54,7 @@ $primary-color: #0062cc; $action-bar-height: 38px; + // Drawer .control-container { padding-bottom: 5px; diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts index 434b6a85a..2c4337efa 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts @@ -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(); } /** diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html index 38fbff6c8..96c33bb08 100644 --- a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html +++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html @@ -17,7 +17,7 @@ }
    - @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) {
  • {{collectionTag.title}} @if (collectionTag.promoted) { diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts index b8f65061b..19fbb5257 100644 --- a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts +++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts @@ -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(); }); diff --git a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html index 1926b77dc..ec7ce2e8f 100644 --- a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html +++ b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html @@ -489,85 +489,215 @@ {{t(tabs[TabID.Info])}}
    {{t('info-title')}}
    -
    -
    {{t('library-title')}} {{libraryName | sentenceCase}}
    -
    {{t('format-title')}} {{series.format | mangaFormat}}
    -
    -
    -
    {{t('created-title')}} {{series.created | date:'shortDate'}}
    -
    {{t('last-read-title')}} {{series.latestReadDate | defaultDate | timeAgo}}
    -
    {{t('last-added-title')}} {{series.lastChapterAdded | defaultDate | timeAgo}}
    -
    {{t('last-scanned-title')}} {{series.lastFolderScanned | defaultDate | timeAgo}}
    + +
    +
    +
    + + + {{libraryName! | sentenceCase}} + + +
    +
    +
    +
    + + + {{series.format | mangaFormat}} + + +
    +
    -
    -
    {{t('folder-path-title')}} {{series.folderPath | defaultValue}}
    -
    -
    -
    - {{t('max-items-title')}} {{metadata.maxCount}} - +
    +
    +
    + + + {{series.folderPath | defaultValue}} + + +
    -
    - {{t('total-items-title')}} {{metadata.totalCount}} - +
    +
    + + + {{series.lowestFolderPath | defaultValue}} + + +
    -
    {{t('publication-status-title')}} {{metadata.publicationStatus | publicationStatus}}
    -
    {{t('total-pages-title')}} {{series.pages}}
    -
    {{t('size-title')}} {{size | bytes}}
    -

    Volumes

    -
    - {{t('loading')}} -
    -
      -
    • - -
      -
      {{formatVolumeName(volume)}}
      -
      -
      -
      - {{t('added-title')}} {{volume.createdUtc | utcToLocalTime | defaultDate}} -
      -
      - {{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime | translocoDate: {dateStyle: 'short' } | defaultDate}} -
      -
      -
      -
      - -
      -
      - {{t('pages-title')}} {{volume.pages}} -
      -
      -
      -
        -
      • - {{file.filePath}} -
        -
        - {{t('chapter-title')}} {{file.chapter}} -
        -
        - {{t('pages-title')}} {{file.pages}} -
        -
        - {{t('format-title')}} {{utilityService.mangaFormatToText(file.format)}} -
        -
        -
      • -
      -
      + @if (metadata) { +
      +
      +
      + + + {{metadata.maxCount}} + +
      -
    • -
    +
    +
    + + + {{metadata.totalCount}} + + +
    +
    +
    + +
    +
    +
    + + + {{metadata.publicationStatus | publicationStatus}} + + +
    +
    +
    +
    + + + {{size | bytes}} + + +
    +
    +
    + } + +
    +
    +
    + + + {{series.created | date:'shortDate'}} + + +
    +
    +
    +
    + + + {{series.lastChapterAdded | defaultDate | timeAgo}} + + +
    +
    +
    + +
    +
    +
    + + + {{series.lastFolderScanned | defaultDate | timeAgo}} + + +
    +
    +
    +
    + + + {{series.lastChapterAdded | defaultDate | timeAgo}} + + +
    +
    +
    + +
    +
    +
    + + + {{series.pages | number}} + + +
    +
    +
    +
    + + + {{series.wordCount | number}} + + +
    +
    +
    + +

    Volumes

    + @if (isLoadingVolumes) { +
    + {{t('loading')}} +
    + } @else { +
      + @for (volume of seriesVolumes; track volume.id) { +
    • + +
      +
      {{formatVolumeName(volume)}}
      +
      +
      +
      + {{t('added-title')}} {{volume.createdUtc | utcToLocalTime | defaultDate}} +
      +
      + {{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime | translocoDate: {dateStyle: 'short' } | defaultDate}} +
      +
      +
      +
      + +
      +
      + {{t('pages-title')}} {{volume.pages}} +
      +
      + +
      +
        + @for(file of volume.volumeFiles; track file.id) { +
      • + {{file.filePath}} +
        +
        + {{t('chapter-title')}} {{file.chapter}} +
        +
        + {{t('pages-title')}} {{file.pages}} +
        +
        + {{t('format-title')}} {{utilityService.mangaFormatToText(file.format)}} +
        +
        +
      • + } +
      +
      +
      +
      +
    • + } +
    + }
  • diff --git a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts index ad1f9ef29..a96568ed6 100644 --- a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts +++ b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts @@ -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'], diff --git a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.scss b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.scss index c6cf51fa3..b19f6a381 100644 --- a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.scss +++ b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.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%; diff --git a/UI/Web/src/app/cards/card-item/card-item.component.html b/UI/Web/src/app/cards/card-item/card-item.component.html index 47adc7573..1a666c48f 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.html +++ b/UI/Web/src/app/cards/card-item/card-item.component.html @@ -2,9 +2,9 @@
    @if (total > 0 || suppressArchiveWarning) { - + } @else if (total === 0 && !suppressArchiveWarning) { - + }
    diff --git a/UI/Web/src/app/cards/chapter-card/chapter-card.component.html b/UI/Web/src/app/cards/chapter-card/chapter-card.component.html index 61ba81ce6..c57c533ca 100644 --- a/UI/Web/src/app/cards/chapter-card/chapter-card.component.html +++ b/UI/Web/src/app/cards/chapter-card/chapter-card.component.html @@ -2,9 +2,9 @@
    @if (chapter.pages > 0 || suppressArchiveWarning) { - + } @else if (chapter.pages === 0 && !suppressArchiveWarning) { - + }
    diff --git a/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.html b/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.html index e4ad639e2..41dddfa59 100644 --- a/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.html +++ b/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.html @@ -55,7 +55,7 @@
    - +
    @@ -65,7 +65,7 @@
    - +