diff --git a/API/Controllers/ChapterController.cs b/API/Controllers/ChapterController.cs new file mode 100644 index 000000000..4872342ef --- /dev/null +++ b/API/Controllers/ChapterController.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using API.Data; +using API.Data.Repositories; +using API.DTOs; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers; + +public class ChapterController : BaseApiController +{ + private readonly IUnitOfWork _unitOfWork; + + public ChapterController(IUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + [HttpGet] + public async Task> GetChapter(int chapterId) + { + var chapter = + await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId, + ChapterIncludes.People | ChapterIncludes.Files); + + return Ok(chapter); + } + + +} diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index 0a2627e9b..b0b25fb04 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -229,22 +229,25 @@ public class SeriesController : BaseApiController { // Trigger a refresh when we are moving from a locked image to a non-locked needsRefreshMetadata = true; - series.CoverImage = string.Empty; + series.CoverImage = null; series.CoverImageLocked = updateSeries.CoverImageLocked; + series.ResetColorScape(); + } _unitOfWork.SeriesRepository.Update(series); - if (await _unitOfWork.CommitAsync()) + if (!await _unitOfWork.CommitAsync()) { - if (needsRefreshMetadata) - { - _taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id); - } - return Ok(); + return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-series-update")); } - return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-series-update")); + if (needsRefreshMetadata) + { + _taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id); + } + + return Ok(); } /// diff --git a/API/Controllers/UploadController.cs b/API/Controllers/UploadController.cs index af6b9a7cc..03841f26d 100644 --- a/API/Controllers/UploadController.cs +++ b/API/Controllers/UploadController.cs @@ -377,6 +377,7 @@ public class UploadController : BaseApiController if (string.IsNullOrEmpty(uploadFileDto.Url)) { library.CoverImage = null; + library.ResetColorScape(); _unitOfWork.LibraryRepository.Update(library); if (_unitOfWork.HasChanges()) { diff --git a/API/DTOs/ChapterDto.cs b/API/DTOs/ChapterDto.cs index b346ca57b..a42c418af 100644 --- a/API/DTOs/ChapterDto.cs +++ b/API/DTOs/ChapterDto.cs @@ -163,4 +163,10 @@ public class ChapterDto : IHasReadTimeEstimate, IHasCoverImage public string CoverImage { get; set; } public string PrimaryColor { get; set; } public string SecondaryColor { get; set; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/DTOs/Collection/AppUserCollectionDto.cs b/API/DTOs/Collection/AppUserCollectionDto.cs index fd6f21e74..4c7ba0f44 100644 --- a/API/DTOs/Collection/AppUserCollectionDto.cs +++ b/API/DTOs/Collection/AppUserCollectionDto.cs @@ -19,8 +19,8 @@ public class AppUserCollectionDto : IHasCoverImage /// public string? CoverImage { get; set; } = string.Empty; - public string PrimaryColor { get; set; } - public string SecondaryColor { get; set; } + public string PrimaryColor { get; set; } = string.Empty; + public string SecondaryColor { get; set; } = string.Empty; public bool CoverImageLocked { get; set; } /// @@ -48,4 +48,10 @@ public class AppUserCollectionDto : IHasCoverImage /// A
separated string of all missing series ///
public string? MissingSeriesFromSource { get; set; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/DTOs/ReadingLists/ReadingListDto.cs b/API/DTOs/ReadingLists/ReadingListDto.cs index a16d28871..14609b4ae 100644 --- a/API/DTOs/ReadingLists/ReadingListDto.cs +++ b/API/DTOs/ReadingLists/ReadingListDto.cs @@ -19,8 +19,8 @@ public class ReadingListDto : IHasCoverImage /// public string? CoverImage { get; set; } = string.Empty; - public string PrimaryColor { get; set; } - public string SecondaryColor { get; set; } + public string PrimaryColor { get; set; } = string.Empty; + public string SecondaryColor { get; set; } = string.Empty; /// /// Minimum Year the Reading List starts @@ -39,4 +39,10 @@ public class ReadingListDto : IHasCoverImage /// public int EndingMonth { get; set; } + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } + } diff --git a/API/DTOs/SeriesDto.cs b/API/DTOs/SeriesDto.cs index e4dfcf303..abe1b5000 100644 --- a/API/DTOs/SeriesDto.cs +++ b/API/DTOs/SeriesDto.cs @@ -66,4 +66,10 @@ public class SeriesDto : IHasReadTimeEstimate, IHasCoverImage public string? CoverImage { get; set; } public string PrimaryColor { get; set; } public string SecondaryColor { get; set; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/DTOs/VolumeDto.cs b/API/DTOs/VolumeDto.cs index ffb72ff6a..bceccd43a 100644 --- a/API/DTOs/VolumeDto.cs +++ b/API/DTOs/VolumeDto.cs @@ -66,4 +66,10 @@ public class VolumeDto : IHasReadTimeEstimate, IHasCoverImage public string CoverImage { get; set; } public string PrimaryColor { get; set; } public string SecondaryColor { get; set; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/Entities/AppUserCollection.cs b/API/Entities/AppUserCollection.cs index 21d707c2f..2a6d8faff 100644 --- a/API/Entities/AppUserCollection.cs +++ b/API/Entities/AppUserCollection.cs @@ -59,6 +59,12 @@ public class AppUserCollection : IEntityDate, IHasCoverImage /// public string? MissingSeriesFromSource { get; set; } + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } + // Relationship public AppUser AppUser { get; set; } = null!; public int AppUserId { get; set; } diff --git a/API/Entities/Chapter.cs b/API/Entities/Chapter.cs index 0c779d52e..fb894121c 100644 --- a/API/Entities/Chapter.cs +++ b/API/Entities/Chapter.cs @@ -193,4 +193,10 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate, IHasCoverImage { return MinNumber.Is(Parser.DefaultChapterNumber) && !IsSpecial; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/Entities/Interfaces/IHasCoverImage.cs b/API/Entities/Interfaces/IHasCoverImage.cs index 4df55587c..5570e37eb 100644 --- a/API/Entities/Interfaces/IHasCoverImage.cs +++ b/API/Entities/Interfaces/IHasCoverImage.cs @@ -1,5 +1,7 @@ namespace API.Entities.Interfaces; +#nullable enable + public interface IHasCoverImage { /// @@ -16,4 +18,9 @@ public interface IHasCoverImage /// Secondary color derived from the Cover Image /// public string? SecondaryColor { get; set; } + + /// + /// Nulls out the ColorScape properties + /// + void ResetColorScape(); } diff --git a/API/Entities/Library.cs b/API/Entities/Library.cs index 9fbc4a592..7a413e3f4 100644 --- a/API/Entities/Library.cs +++ b/API/Entities/Library.cs @@ -80,4 +80,10 @@ public class Library : IEntityDate, IHasCoverImage LastScanned = (DateTime) time; } } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/Entities/ReadingList.cs b/API/Entities/ReadingList.cs index 359e576e6..4a11845af 100644 --- a/API/Entities/ReadingList.cs +++ b/API/Entities/ReadingList.cs @@ -59,4 +59,10 @@ public class ReadingList : IEntityDate, IHasCoverImage // Relationships public int AppUserId { get; set; } public AppUser AppUser { get; set; } = null!; + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/Entities/Series.cs b/API/Entities/Series.cs index 65a5330bc..b33700e05 100644 --- a/API/Entities/Series.cs +++ b/API/Entities/Series.cs @@ -145,4 +145,10 @@ public class Series : IEntityDate, IHasReadTimeEstimate, IHasCoverImage NormalizedName == localizedNameNormalized || NormalizedLocalizedName == localizedNameNormalized; } + + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } } diff --git a/API/Entities/Volume.cs b/API/Entities/Volume.cs index bf7312d3d..17c15b978 100644 --- a/API/Entities/Volume.cs +++ b/API/Entities/Volume.cs @@ -73,4 +73,10 @@ public class Volume : IEntityDate, IHasReadTimeEstimate, IHasCoverImage return $"{MinNumber}-{MaxNumber}"; } + public void ResetColorScape() + { + PrimaryColor = string.Empty; + SecondaryColor = string.Empty; + } + } diff --git a/UI/Web/package-lock.json b/UI/Web/package-lock.json index 2b6eedf49..5c4a5c95b 100644 --- a/UI/Web/package-lock.json +++ b/UI/Web/package-lock.json @@ -21,13 +21,13 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@iharbeck/ngx-virtual-scroller": "^17.0.2", "@iplab/ngx-file-upload": "^17.1.0", + "@jsverse/transloco": "^7.4.3", + "@jsverse/transloco-locale": "^7.0.1", + "@jsverse/transloco-persist-lang": "^7.0.1", + "@jsverse/transloco-persist-translations": "^7.0.1", + "@jsverse/transloco-preload-langs": "^7.0.1", "@microsoft/signalr": "^7.0.14", "@ng-bootstrap/ng-bootstrap": "^16.0.0", - "@ngneat/transloco": "^6.0.4", - "@ngneat/transloco-locale": "^5.1.2", - "@ngneat/transloco-persist-lang": "^5.0.0", - "@ngneat/transloco-persist-translations": "^5.0.0", - "@ngneat/transloco-preload-langs": "^5.0.1", "@popperjs/core": "^2.11.7", "@swimlane/ngx-charts": "^20.5.0", "@tweenjs/tween.js": "^23.1.1", @@ -3258,6 +3258,85 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsverse/transloco": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@jsverse/transloco/-/transloco-7.4.3.tgz", + "integrity": "sha512-QVzpbsfMN4oB01OfiGBz0f9/cw6nczF2EHIlhJG0455bMjiaR/tQTVGFmAGnm267iQFPtOL36yQyaHznXxPaqw==", + "dependencies": { + "@jsverse/transloco-utils": "^7.0.0", + "flat": "6.0.1", + "fs-extra": "^11.0.0", + "glob": "^10.0.0", + "lodash.kebabcase": "^4.1.1", + "ora": "^5.4.1", + "replace-in-file": "^7.0.1", + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=16.0.0" + } + }, + "node_modules/@jsverse/transloco-locale": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@jsverse/transloco-locale/-/transloco-locale-7.0.1.tgz", + "integrity": "sha512-mx43h2FKMKxx+Er18qArBJMxmGGW2+EShkH+xueAp+VC/ivBNQDyXWpg8hOsfNFqFQAjzlCAie1mXpbGmbM0uw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=16.0.0", + "@jsverse/transloco": ">=7.0.0", + "rxjs": ">=6.0.0" + } + }, + "node_modules/@jsverse/transloco-persist-lang": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@jsverse/transloco-persist-lang/-/transloco-persist-lang-7.0.1.tgz", + "integrity": "sha512-bCH5aECb6d/NbS3/oiTqgbWrMIzo1kJzJTOVF2uazZeMa8M4xcx1Am+cX/Fo4gxAFrrTLmI4yC2dde8JB/qdCA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=16.0.0", + "@jsverse/transloco": ">=7.0.0" + } + }, + "node_modules/@jsverse/transloco-persist-translations": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@jsverse/transloco-persist-translations/-/transloco-persist-translations-7.0.1.tgz", + "integrity": "sha512-BUGpcD4MrIBUbo7/G06yGdkWuVTKXVESyAJp107yUbE34Ami0+4BEK7vfLTl09ARwhBQsNKIzZgTAIpzrlK98A==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=16.0.0", + "@jsverse/transloco": ">=7.0.0" + } + }, + "node_modules/@jsverse/transloco-preload-langs": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@jsverse/transloco-preload-langs/-/transloco-preload-langs-7.0.1.tgz", + "integrity": "sha512-J9G+r9g8UnLWsEdf0XTUhSIX/CFoKEPP6bEfyXQ7f36FFVu3raPRoEXnqE8gQGCPiyFPG0J8YSf7lyJtUHIgHA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=16.0.0", + "@jsverse/transloco": ">=7.0.0" + } + }, + "node_modules/@jsverse/transloco-utils": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@jsverse/transloco-utils/-/transloco-utils-7.0.2.tgz", + "integrity": "sha512-zud1M68mMC/Pu6irEba+Z2SzmwmmPzUPnBzMKlcGdIhzUe1z41cqQutK1M0QaQpY4h4yhumXcNaY/Ot6piv6QQ==", + "dependencies": { + "cosmiconfig": "^8.1.3", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -3304,85 +3383,6 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@ngneat/transloco": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@ngneat/transloco/-/transloco-6.0.4.tgz", - "integrity": "sha512-hQSPdmzuxJIu2SBwvoiwjoUjxSnUGFyCOkJnV8IwzzmBSdgQxqMMci5WXg/bQeCYggA+RyXpUjjTudEvkWy5Rw==", - "dependencies": { - "@ngneat/transloco-utils": "^5.0.0", - "flat": "6.0.1", - "fs-extra": "^11.0.0", - "glob": "^10.0.0", - "lodash.kebabcase": "^4.1.1", - "ora": "^5.4.1", - "replace-in-file": "^7.0.1", - "tslib": "^2.2.0" - }, - "peerDependencies": { - "@angular/core": ">=16.0.0" - } - }, - "node_modules/@ngneat/transloco-locale": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@ngneat/transloco-locale/-/transloco-locale-5.1.2.tgz", - "integrity": "sha512-lIEW9rjpxamXyk39kGSykR6rEbVF/Fifvp62L/8eb18X9R0quPR4YnCCkAdioZvTX2EG2tgcNWvOD2fxdgxvlQ==", - "dependencies": { - "tslib": "^2.2.0" - }, - "peerDependencies": { - "@angular/core": ">=13.0.0", - "@ngneat/transloco": ">=4.0.0", - "rxjs": ">=6.0.0" - } - }, - "node_modules/@ngneat/transloco-persist-lang": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@ngneat/transloco-persist-lang/-/transloco-persist-lang-5.0.0.tgz", - "integrity": "sha512-vBpHQqTeKZT+V+uvIIEv+KyCq+8HFkCa7lnjvWwcgGupSYjTvZp4PxUm+KOLLmaTIzJDL1OQEaszQ84EzX6Mzg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "peerDependencies": { - "@angular/core": ">=16.0.0", - "@ngneat/transloco": ">=5.0.0" - } - }, - "node_modules/@ngneat/transloco-persist-translations": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@ngneat/transloco-persist-translations/-/transloco-persist-translations-5.0.0.tgz", - "integrity": "sha512-QLM9X9aDRPLZhNK8f8h/4eqjhSJvHoGHRSQ+CoS3qkOXteEdOQXeYzWPHSmvDHc5lN3zNRy6sjHrBQEiZQLCKw==", - "dependencies": { - "tslib": "^2.2.0" - }, - "peerDependencies": { - "@angular/core": ">=16.0.0", - "@ngneat/transloco": ">=5.0.0" - } - }, - "node_modules/@ngneat/transloco-preload-langs": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@ngneat/transloco-preload-langs/-/transloco-preload-langs-5.0.1.tgz", - "integrity": "sha512-+HDsEtBCFTD8YY31VX9N0dPcVp/CozxmcHXTvqjJ3M0BEkkygZIoiTQwaOPiJziNjFKl8FRhAvovWVV/t8hd8g==", - "dependencies": { - "tslib": "^2.2.0" - }, - "peerDependencies": { - "@angular/core": ">=16.0.0", - "@ngneat/transloco": ">=5.0.0" - } - }, - "node_modules/@ngneat/transloco-utils": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@ngneat/transloco-utils/-/transloco-utils-5.0.0.tgz", - "integrity": "sha512-e0S+GWyBTmLix9KfYWW/rScYdqQz3z3znNSb+foaA5T3jWs4CPLVo+PV0No7kGjqom8Wy8H3lLvztfhHxYSLyA==", - "dependencies": { - "cosmiconfig": "^8.1.3", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@ngtools/webpack": { "version": "17.3.4", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.4.tgz", diff --git a/UI/Web/package.json b/UI/Web/package.json index 27a72daea..1f3ee529c 100644 --- a/UI/Web/package.json +++ b/UI/Web/package.json @@ -28,13 +28,13 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@iharbeck/ngx-virtual-scroller": "^17.0.2", "@iplab/ngx-file-upload": "^17.1.0", + "@jsverse/transloco": "^7.4.3", + "@jsverse/transloco-locale": "^7.0.1", + "@jsverse/transloco-persist-lang": "^7.0.1", + "@jsverse/transloco-persist-translations": "^7.0.1", + "@jsverse/transloco-preload-langs": "^7.0.1", "@microsoft/signalr": "^7.0.14", "@ng-bootstrap/ng-bootstrap": "^16.0.0", - "@ngneat/transloco": "^6.0.4", - "@ngneat/transloco-locale": "^5.1.2", - "@ngneat/transloco-persist-lang": "^5.0.0", - "@ngneat/transloco-persist-translations": "^5.0.0", - "@ngneat/transloco-preload-langs": "^5.0.1", "@popperjs/core": "^2.11.7", "@swimlane/ngx-charts": "^20.5.0", "@tweenjs/tween.js": "^23.1.1", diff --git a/UI/Web/src/app/_guards/admin.guard.ts b/UI/Web/src/app/_guards/admin.guard.ts index 9ab6dea95..ade795609 100644 --- a/UI/Web/src/app/_guards/admin.guard.ts +++ b/UI/Web/src/app/_guards/admin.guard.ts @@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { AccountService } from '../_services/account.service'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Injectable({ providedIn: 'root' diff --git a/UI/Web/src/app/_guards/auth.guard.ts b/UI/Web/src/app/_guards/auth.guard.ts index 5d403469e..41a8b1eef 100644 --- a/UI/Web/src/app/_guards/auth.guard.ts +++ b/UI/Web/src/app/_guards/auth.guard.ts @@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { AccountService } from '../_services/account.service'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Injectable({ providedIn: 'root' diff --git a/UI/Web/src/app/_interceptors/error.interceptor.ts b/UI/Web/src/app/_interceptors/error.interceptor.ts index 8f9c31fed..39b123562 100644 --- a/UI/Web/src/app/_interceptors/error.interceptor.ts +++ b/UI/Web/src/app/_interceptors/error.interceptor.ts @@ -10,7 +10,7 @@ import { Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; import { catchError } from 'rxjs/operators'; import { AccountService } from '../_services/account.service'; -import {translate, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoService} from "@jsverse/transloco"; @Injectable() export class ErrorInterceptor implements HttpInterceptor { diff --git a/UI/Web/src/app/_models/chapter.ts b/UI/Web/src/app/_models/chapter.ts index 3e1c8324c..db846e911 100644 --- a/UI/Web/src/app/_models/chapter.ts +++ b/UI/Web/src/app/_models/chapter.ts @@ -80,6 +80,6 @@ export interface Chapter { teams: Array; locations: Array; - primaryColor?: string; - secondaryColor?: string; + primaryColor: string; + secondaryColor: string; } diff --git a/UI/Web/src/app/_pipes/age-rating.pipe.ts b/UI/Web/src/app/_pipes/age-rating.pipe.ts index 44db8d8e9..15554cf05 100644 --- a/UI/Web/src/app/_pipes/age-rating.pipe.ts +++ b/UI/Web/src/app/_pipes/age-rating.pipe.ts @@ -2,7 +2,7 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { Observable, of } from 'rxjs'; import { AgeRating } from '../_models/metadata/age-rating'; import { AgeRatingDto } from '../_models/metadata/age-rating-dto'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'ageRating', diff --git a/UI/Web/src/app/_pipes/book-page-layout-mode.pipe.ts b/UI/Web/src/app/_pipes/book-page-layout-mode.pipe.ts index beef289b4..f56e31194 100644 --- a/UI/Web/src/app/_pipes/book-page-layout-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/book-page-layout-mode.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {BookPageLayoutMode} from "../_models/readers/book-page-layout-mode"; @Pipe({ 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 47cbf1865..3f4da39a7 100644 --- a/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts +++ b/UI/Web/src/app/_pipes/cbl-conflict-reason.pipe.ts @@ -1,7 +1,7 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { CblBookResult } from 'src/app/_models/reading-list/cbl/cbl-book-result'; import { CblImportReason } from 'src/app/_models/reading-list/cbl/cbl-import-reason.enum'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; const failIcon = ''; const successIcon = ''; diff --git a/UI/Web/src/app/_pipes/cbl-import-result.pipe.ts b/UI/Web/src/app/_pipes/cbl-import-result.pipe.ts index 274dbf33b..c168ebcb2 100644 --- a/UI/Web/src/app/_pipes/cbl-import-result.pipe.ts +++ b/UI/Web/src/app/_pipes/cbl-import-result.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { CblImportResult } from 'src/app/_models/reading-list/cbl/cbl-import-result.enum'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'cblImportResult', diff --git a/UI/Web/src/app/_pipes/cover-image-size.pipe.ts b/UI/Web/src/app/_pipes/cover-image-size.pipe.ts index d0fca1a53..8f54e269e 100644 --- a/UI/Web/src/app/_pipes/cover-image-size.pipe.ts +++ b/UI/Web/src/app/_pipes/cover-image-size.pipe.ts @@ -1,6 +1,6 @@ import {Pipe, PipeTransform} from '@angular/core'; import {CoverImageSize} from "../admin/_models/cover-image-size"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'coverImageSize', diff --git a/UI/Web/src/app/_pipes/day-of-week.pipe.ts b/UI/Web/src/app/_pipes/day-of-week.pipe.ts index f2fd9c0fe..30bd478c9 100644 --- a/UI/Web/src/app/_pipes/day-of-week.pipe.ts +++ b/UI/Web/src/app/_pipes/day-of-week.pipe.ts @@ -1,6 +1,6 @@ import {Pipe, PipeTransform} from '@angular/core'; import { DayOfWeek } from 'src/app/_services/statistics.service'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'dayOfWeek', diff --git a/UI/Web/src/app/_pipes/default-date.pipe.ts b/UI/Web/src/app/_pipes/default-date.pipe.ts index 61e7c5e68..7cd541e0b 100644 --- a/UI/Web/src/app/_pipes/default-date.pipe.ts +++ b/UI/Web/src/app/_pipes/default-date.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'defaultDate', diff --git a/UI/Web/src/app/_pipes/device-platform.pipe.ts b/UI/Web/src/app/_pipes/device-platform.pipe.ts index e95d43788..4f2090329 100644 --- a/UI/Web/src/app/_pipes/device-platform.pipe.ts +++ b/UI/Web/src/app/_pipes/device-platform.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { DevicePlatform } from 'src/app/_models/device/device-platform'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'devicePlatform', diff --git a/UI/Web/src/app/_pipes/file-type-group.pipe.ts b/UI/Web/src/app/_pipes/file-type-group.pipe.ts index e996eafde..88d595420 100644 --- a/UI/Web/src/app/_pipes/file-type-group.pipe.ts +++ b/UI/Web/src/app/_pipes/file-type-group.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {FileTypeGroup} from "../_models/library/file-type-group.enum"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'fileTypeGroup', diff --git a/UI/Web/src/app/_pipes/filter-comparison.pipe.ts b/UI/Web/src/app/_pipes/filter-comparison.pipe.ts index 33a7c960e..df4600a38 100644 --- a/UI/Web/src/app/_pipes/filter-comparison.pipe.ts +++ b/UI/Web/src/app/_pipes/filter-comparison.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import { FilterComparison } from 'src/app/_models/metadata/v2/filter-comparison'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'filterComparison', diff --git a/UI/Web/src/app/_pipes/filter-field.pipe.ts b/UI/Web/src/app/_pipes/filter-field.pipe.ts index c28cd4813..acd0993f7 100644 --- a/UI/Web/src/app/_pipes/filter-field.pipe.ts +++ b/UI/Web/src/app/_pipes/filter-field.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import { FilterField } from 'src/app/_models/metadata/v2/filter-field'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'filterField', diff --git a/UI/Web/src/app/_pipes/layout-mode.pipe.ts b/UI/Web/src/app/_pipes/layout-mode.pipe.ts index 9987347fe..1e0e51c84 100644 --- a/UI/Web/src/app/_pipes/layout-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/layout-mode.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {LayoutMode} from "../manga-reader/_models/layout-mode"; @Pipe({ diff --git a/UI/Web/src/app/_pipes/library-type.pipe.ts b/UI/Web/src/app/_pipes/library-type.pipe.ts index b43f5e2c1..74a62647f 100644 --- a/UI/Web/src/app/_pipes/library-type.pipe.ts +++ b/UI/Web/src/app/_pipes/library-type.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { LibraryType } from '../_models/library/library'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; /** * Returns the name of the LibraryType diff --git a/UI/Web/src/app/_pipes/manga-format.pipe.ts b/UI/Web/src/app/_pipes/manga-format.pipe.ts index 9b526ae10..60672271b 100644 --- a/UI/Web/src/app/_pipes/manga-format.pipe.ts +++ b/UI/Web/src/app/_pipes/manga-format.pipe.ts @@ -1,6 +1,6 @@ import {Pipe, PipeTransform} from '@angular/core'; import { MangaFormat } from '../_models/manga-format'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; /** * Returns the string name for the format diff --git a/UI/Web/src/app/_pipes/page-layout-mode.pipe.ts b/UI/Web/src/app/_pipes/page-layout-mode.pipe.ts index a0c3a9d66..45928d66b 100644 --- a/UI/Web/src/app/_pipes/page-layout-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/page-layout-mode.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {PageLayoutMode} from "../_models/page-layout-mode"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'pageLayoutMode', diff --git a/UI/Web/src/app/_pipes/page-split-option.pipe.ts b/UI/Web/src/app/_pipes/page-split-option.pipe.ts index a5436255d..0720a9a9b 100644 --- a/UI/Web/src/app/_pipes/page-split-option.pipe.ts +++ b/UI/Web/src/app/_pipes/page-split-option.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {PageSplitOption} from "../_models/preferences/page-split-option"; @Pipe({ diff --git a/UI/Web/src/app/_pipes/pdf-scroll-mode.pipe.ts b/UI/Web/src/app/_pipes/pdf-scroll-mode.pipe.ts index 281cd977a..71ea22b1f 100644 --- a/UI/Web/src/app/_pipes/pdf-scroll-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/pdf-scroll-mode.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {PdfScrollMode} from "../_models/preferences/pdf-scroll-mode"; @Pipe({ diff --git a/UI/Web/src/app/_pipes/pdf-spread-mode.pipe.ts b/UI/Web/src/app/_pipes/pdf-spread-mode.pipe.ts index 04584d256..88b9b1c3f 100644 --- a/UI/Web/src/app/_pipes/pdf-spread-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/pdf-spread-mode.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {PdfSpreadMode} from "../_models/preferences/pdf-spread-mode"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'pdfSpreadMode', diff --git a/UI/Web/src/app/_pipes/pdf-theme.pipe.ts b/UI/Web/src/app/_pipes/pdf-theme.pipe.ts index d5aa0d7cc..7fb0c2d7e 100644 --- a/UI/Web/src/app/_pipes/pdf-theme.pipe.ts +++ b/UI/Web/src/app/_pipes/pdf-theme.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {PdfTheme} from "../_models/preferences/pdf-theme"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'pdfTheme', diff --git a/UI/Web/src/app/_pipes/person-role.pipe.ts b/UI/Web/src/app/_pipes/person-role.pipe.ts index 6845f0a2a..e6f4f4cb8 100644 --- a/UI/Web/src/app/_pipes/person-role.pipe.ts +++ b/UI/Web/src/app/_pipes/person-role.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { PersonRole } from '../_models/metadata/person'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'personRole', diff --git a/UI/Web/src/app/_pipes/publication-status.pipe.ts b/UI/Web/src/app/_pipes/publication-status.pipe.ts index f4d5a621d..98a62a2b6 100644 --- a/UI/Web/src/app/_pipes/publication-status.pipe.ts +++ b/UI/Web/src/app/_pipes/publication-status.pipe.ts @@ -1,6 +1,6 @@ import {Pipe, PipeTransform} from '@angular/core'; import { PublicationStatus } from '../_models/metadata/publication-status'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'publicationStatus', diff --git a/UI/Web/src/app/_pipes/reading-direction.pipe.ts b/UI/Web/src/app/_pipes/reading-direction.pipe.ts index 0927d8d42..7626a6cab 100644 --- a/UI/Web/src/app/_pipes/reading-direction.pipe.ts +++ b/UI/Web/src/app/_pipes/reading-direction.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {ReadingDirection} from "../_models/preferences/reading-direction"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'readingDirection', @@ -11,7 +11,7 @@ export class ReadingDirectionPipe implements PipeTransform { transform(value: ReadingDirection): string { switch (value) { case ReadingDirection.LeftToRight: return translate('preferences.left-to-right'); - case ReadingDirection.RightToLeft: return translate('preferences.right-to-right'); + case ReadingDirection.RightToLeft: return translate('preferences.right-to-left'); } } diff --git a/UI/Web/src/app/_pipes/reading-mode.pipe.ts b/UI/Web/src/app/_pipes/reading-mode.pipe.ts index b805c2fa4..f38f0779a 100644 --- a/UI/Web/src/app/_pipes/reading-mode.pipe.ts +++ b/UI/Web/src/app/_pipes/reading-mode.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {ReaderMode} from "../_models/preferences/reader-mode"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'readerMode', @@ -10,7 +10,7 @@ export class ReaderModePipe implements PipeTransform { transform(value: ReaderMode): string { switch (value) { - case ReaderMode.UpDown: return translate('preferences.up-down'); + case ReaderMode.UpDown: return translate('preferences.up-to-down'); case ReaderMode.Webtoon: return translate('preferences.webtoon'); case ReaderMode.LeftRight: return translate('preferences.left-to-right'); } diff --git a/UI/Web/src/app/_pipes/relationship.pipe.ts b/UI/Web/src/app/_pipes/relationship.pipe.ts index 7535ffea0..2764cbb56 100644 --- a/UI/Web/src/app/_pipes/relationship.pipe.ts +++ b/UI/Web/src/app/_pipes/relationship.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { RelationKind } from '../_models/series-detail/relation-kind'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'relationship', diff --git a/UI/Web/src/app/_pipes/scaling-option.pipe.ts b/UI/Web/src/app/_pipes/scaling-option.pipe.ts index b150cbaad..6dc25c2ca 100644 --- a/UI/Web/src/app/_pipes/scaling-option.pipe.ts +++ b/UI/Web/src/app/_pipes/scaling-option.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {ScalingOption} from "../_models/preferences/scaling-option"; @Pipe({ diff --git a/UI/Web/src/app/_pipes/scrobble-event-type.pipe.ts b/UI/Web/src/app/_pipes/scrobble-event-type.pipe.ts index 08e0b2996..7597b7f38 100644 --- a/UI/Web/src/app/_pipes/scrobble-event-type.pipe.ts +++ b/UI/Web/src/app/_pipes/scrobble-event-type.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import {ScrobbleEventType} from "../_models/scrobbling/scrobble-event"; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'scrobbleEventType', diff --git a/UI/Web/src/app/_pipes/setting-fragment.pipe.ts b/UI/Web/src/app/_pipes/setting-fragment.pipe.ts index 8de6576a5..14425d21c 100644 --- a/UI/Web/src/app/_pipes/setting-fragment.pipe.ts +++ b/UI/Web/src/app/_pipes/setting-fragment.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {SettingsTabId} from "../sidenav/preference-nav/preference-nav.component"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; /** * Translates the fragment for Settings to a User title diff --git a/UI/Web/src/app/_pipes/site-theme-provider.pipe.ts b/UI/Web/src/app/_pipes/site-theme-provider.pipe.ts index bb7ff09b3..6899d8d51 100644 --- a/UI/Web/src/app/_pipes/site-theme-provider.pipe.ts +++ b/UI/Web/src/app/_pipes/site-theme-provider.pipe.ts @@ -1,6 +1,6 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; import { ThemeProvider } from 'src/app/_models/preferences/site-theme'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ diff --git a/UI/Web/src/app/_pipes/sort-field.pipe.ts b/UI/Web/src/app/_pipes/sort-field.pipe.ts index 8044631b3..13ff4f758 100644 --- a/UI/Web/src/app/_pipes/sort-field.pipe.ts +++ b/UI/Web/src/app/_pipes/sort-field.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import {SortField} from "../_models/metadata/series-filter"; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; @Pipe({ name: 'sortField', diff --git a/UI/Web/src/app/_pipes/stream-name.pipe.ts b/UI/Web/src/app/_pipes/stream-name.pipe.ts index 632beb0e7..c15974b6b 100644 --- a/UI/Web/src/app/_pipes/stream-name.pipe.ts +++ b/UI/Web/src/app/_pipes/stream-name.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Pipe({ name: 'streamName', diff --git a/UI/Web/src/app/_pipes/time-ago.pipe.ts b/UI/Web/src/app/_pipes/time-ago.pipe.ts index a66c255f5..99039126c 100644 --- a/UI/Web/src/app/_pipes/time-ago.pipe.ts +++ b/UI/Web/src/app/_pipes/time-ago.pipe.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, NgZone, OnDestroy, Pipe, PipeTransform} from '@angular/core'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; /** * MIT License diff --git a/UI/Web/src/app/_pipes/time-duration.pipe.ts b/UI/Web/src/app/_pipes/time-duration.pipe.ts index c608ff51a..1d23bae4a 100644 --- a/UI/Web/src/app/_pipes/time-duration.pipe.ts +++ b/UI/Web/src/app/_pipes/time-duration.pipe.ts @@ -1,5 +1,5 @@ import {inject, Pipe, PipeTransform} from '@angular/core'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; /** * Converts hours -> days, months, years, etc diff --git a/UI/Web/src/app/_pipes/writing-style.pipe.ts b/UI/Web/src/app/_pipes/writing-style.pipe.ts index f1680bb72..8136595d6 100644 --- a/UI/Web/src/app/_pipes/writing-style.pipe.ts +++ b/UI/Web/src/app/_pipes/writing-style.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {WritingStyle} from "../_models/preferences/writing-style"; @Pipe({ diff --git a/UI/Web/src/app/_services/action-factory.service.ts b/UI/Web/src/app/_services/action-factory.service.ts index c4eba8ca9..3a231d347 100644 --- a/UI/Web/src/app/_services/action-factory.service.ts +++ b/UI/Web/src/app/_services/action-factory.service.ts @@ -11,6 +11,7 @@ import { AccountService } from './account.service'; import { DeviceService } from './device.service'; import {SideNavStream} from "../_models/sidenav/sidenav-stream"; import {SmartFilter} from "../_models/metadata/v2/smart-filter"; +import {translate} from "@jsverse/transloco"; export enum Action { Submenu = -1, @@ -117,6 +118,7 @@ export type ActionAllowedCallback = (action: ActionItem) => boolean; export interface ActionItem { title: string; + description: string; action: Action; callback: ActionCallback; requiresAdmin: boolean; @@ -208,15 +210,6 @@ export class ActionFactoryService { return this.applyCallbackToList(this.bookmarkActions, callback); } - getMetadataFilterActions(callback: ActionCallback) { - const actions = [ - {title: 'add-rule-group-and', action: Action.AddRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback}, - {title: 'add-rule-group-or', action: Action.AddRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback}, - {title: 'remove-rule-group', action: Action.RemoveRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback}, - ]; - return this.applyCallbackToList(actions, callback); - } - dummyCallback(action: ActionItem, data: any) {} filterSendToAction(actions: Array>, chapter: Chapter) { @@ -227,11 +220,44 @@ export class ActionFactoryService { return actions; } + getActionablesForSettingsPage(actions: Array>, blacklist: Array = []) { + const tasks = []; + + let actionItem; + for (let parent of actions) { + if (parent.action === Action.SendTo) continue; + + if (parent.children.length === 0) { + actionItem = {...parent}; + actionItem.title = translate('actionable.' + actionItem.title); + if (actionItem.description !== '') { + actionItem.description = translate('actionable.' + actionItem.description); + } + + tasks.push(actionItem); + continue; + } + + for (let child of parent.children) { + actionItem = {...child}; + actionItem.title = translate('actionable.' + actionItem.title); + if (actionItem.description !== '') { + actionItem.description = translate('actionable.' + actionItem.description); + } + tasks.push(actionItem); + } + } + + // Filter out tasks that don't make sense + return tasks.filter(t => !blacklist.includes(t.action)); + } + private _resetActions() { this.libraryActions = [ { action: Action.Scan, title: 'scan-library', + description: 'scan-library-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -239,12 +265,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'others', + description: '', callback: this.dummyCallback, requiresAdmin: true, children: [ { action: Action.RefreshMetadata, title: 'refresh-covers', + description: 'refresh-covers-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -252,6 +280,7 @@ export class ActionFactoryService { { action: Action.GenerateColorScape, title: 'generate-colorscape', + description: 'generate-colorscape-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -259,6 +288,7 @@ export class ActionFactoryService { { action: Action.AnalyzeFiles, title: 'analyze-files', + description: 'analyze-files-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -266,6 +296,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'delete', + description: 'delete-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -275,6 +306,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'settings', + description: 'settings-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -285,6 +317,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'edit', + description: 'edit-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -292,6 +325,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'delete', + description: 'delete-tooltip', callback: this.dummyCallback, requiresAdmin: false, class: 'danger', @@ -300,6 +334,7 @@ export class ActionFactoryService { { action: Action.Promote, title: 'promote', + description: 'promote-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -307,6 +342,7 @@ export class ActionFactoryService { { action: Action.UnPromote, title: 'unpromote', + description: 'unpromote-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -317,6 +353,7 @@ export class ActionFactoryService { { action: Action.MarkAsRead, title: 'mark-as-read', + description: 'mark-as-read-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -324,6 +361,7 @@ export class ActionFactoryService { { action: Action.MarkAsUnread, title: 'mark-as-unread', + description: 'mark-as-unread-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -331,6 +369,7 @@ export class ActionFactoryService { { action: Action.Scan, title: 'scan-series', + description: 'scan-series-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -338,12 +377,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'add-to', + description: '', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.AddToWantToReadList, title: 'add-to-want-to-read', + description: 'add-to-want-to-read-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -351,6 +392,7 @@ export class ActionFactoryService { { action: Action.RemoveFromWantToReadList, title: 'remove-from-want-to-read', + description: 'remove-to-want-to-read-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -358,6 +400,7 @@ export class ActionFactoryService { { action: Action.AddToReadingList, title: 'add-to-reading-list', + description: 'add-to-reading-list-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -365,6 +408,7 @@ export class ActionFactoryService { { action: Action.AddToCollection, title: 'add-to-collection', + description: 'add-to-collection-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -374,12 +418,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'send-to', + description: 'send-to-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.SendTo, title: '', + description: '', callback: this.dummyCallback, requiresAdmin: false, dynamicList: this.deviceService.devices$.pipe(map((devices: Array) => devices.map(d => { @@ -392,12 +438,22 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'others', + description: '', callback: this.dummyCallback, requiresAdmin: true, children: [ { action: Action.RefreshMetadata, title: 'refresh-covers', + description: 'refresh-covers-tooltip', + callback: this.dummyCallback, + requiresAdmin: true, + children: [], + }, + { + action: Action.GenerateColorScape, + title: 'generate-colorscape', + description: 'generate-colorscape-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -405,6 +461,7 @@ export class ActionFactoryService { { action: Action.AnalyzeFiles, title: 'analyze-files', + description: 'analyze-files-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -412,6 +469,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'delete', + description: 'delete-tooltip', callback: this.dummyCallback, requiresAdmin: true, class: 'danger', @@ -422,6 +480,7 @@ export class ActionFactoryService { { action: Action.Download, title: 'download', + description: 'download-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -429,6 +488,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'edit', + description: 'edit-tooltip', callback: this.dummyCallback, requiresAdmin: true, children: [], @@ -439,6 +499,7 @@ export class ActionFactoryService { { action: Action.IncognitoRead, title: 'read-incognito', + description: 'read-incognito-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -446,6 +507,7 @@ export class ActionFactoryService { { action: Action.MarkAsRead, title: 'mark-as-read', + description: 'mark-as-read-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -453,6 +515,7 @@ export class ActionFactoryService { { action: Action.MarkAsUnread, title: 'mark-as-unread', + description: 'mark-as-unread-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -460,12 +523,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'add-to', + description: '=', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.AddToReadingList, title: 'add-to-reading-list', + description: 'add-to-reading-list-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -475,12 +540,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'send-to', + description: 'send-to-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.SendTo, title: '', + description: '', callback: this.dummyCallback, requiresAdmin: false, dynamicList: this.deviceService.devices$.pipe(map((devices: Array) => devices.map(d => { @@ -493,6 +560,7 @@ export class ActionFactoryService { { action: Action.Download, title: 'download', + description: 'download-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -500,6 +568,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'details', + description: 'edit-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -510,6 +579,7 @@ export class ActionFactoryService { { action: Action.IncognitoRead, title: 'read-incognito', + description: 'read-incognito-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -517,6 +587,7 @@ export class ActionFactoryService { { action: Action.MarkAsRead, title: 'mark-as-read', + description: 'mark-as-read-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -524,6 +595,7 @@ export class ActionFactoryService { { action: Action.MarkAsUnread, title: 'mark-as-unread', + description: 'mark-as-unread-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -531,12 +603,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'add-to', + description: '', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.AddToReadingList, title: 'add-to-reading-list', + description: 'add-to-reading-list-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -546,12 +620,14 @@ export class ActionFactoryService { { action: Action.Submenu, title: 'send-to', + description: 'send-to-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [ { action: Action.SendTo, title: '', + description: '', callback: this.dummyCallback, requiresAdmin: false, dynamicList: this.deviceService.devices$.pipe(map((devices: Array) => devices.map(d => { @@ -565,6 +641,7 @@ export class ActionFactoryService { { action: Action.Download, title: 'download', + description: 'download-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -572,6 +649,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'details', + description: 'edit-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -582,6 +660,7 @@ export class ActionFactoryService { { action: Action.Edit, title: 'edit', + description: 'edit-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -589,6 +668,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'delete', + description: 'delete-tooltip', callback: this.dummyCallback, requiresAdmin: false, class: 'danger', @@ -597,6 +677,7 @@ export class ActionFactoryService { { action: Action.Promote, title: 'promote', + description: 'promote-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -604,6 +685,7 @@ export class ActionFactoryService { { action: Action.UnPromote, title: 'unpromote', + description: 'unpromote-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -614,6 +696,7 @@ export class ActionFactoryService { { action: Action.ViewSeries, title: 'view-series', + description: 'view-series-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -621,6 +704,7 @@ export class ActionFactoryService { { action: Action.DownloadBookmark, title: 'download', + description: 'download-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -628,6 +712,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'clear', + description: 'delete-tooltip', callback: this.dummyCallback, class: 'danger', requiresAdmin: false, @@ -639,6 +724,7 @@ export class ActionFactoryService { { action: Action.MarkAsVisible, title: 'mark-visible', + description: 'mark-visible-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -646,6 +732,7 @@ export class ActionFactoryService { { action: Action.MarkAsInvisible, title: 'mark-invisible', + description: 'mark-invisible-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], @@ -656,6 +743,7 @@ export class ActionFactoryService { { action: Action.Delete, title: 'delete', + description: 'delete-tooltip', callback: this.dummyCallback, requiresAdmin: false, children: [], diff --git a/UI/Web/src/app/_services/action.service.ts b/UI/Web/src/app/_services/action.service.ts index 3d290f9c5..149751929 100644 --- a/UI/Web/src/app/_services/action.service.ts +++ b/UI/Web/src/app/_services/action.service.ts @@ -19,7 +19,7 @@ import { LibraryService } from './library.service'; import { MemberService } from './member.service'; import { ReaderService } from './reader.service'; import { SeriesService } from './series.service'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {UserCollection} from "../_models/collection-tag"; import {CollectionTagService} from "./collection-tag.service"; import {SmartFilter} from "../_models/metadata/v2/smart-filter"; @@ -87,7 +87,7 @@ export class ActionService implements OnDestroy { * @param forceUpdate Optional Should we force * @returns */ - async refreshMetadata(library: Partial, callback?: LibraryActionCallback, forceUpdate: boolean = true) { + async refreshLibraryMetadata(library: Partial, callback?: LibraryActionCallback, forceUpdate: boolean = true) { if (!library.hasOwnProperty('id') || library.id === undefined) { return; } @@ -102,8 +102,11 @@ export class ActionService implements OnDestroy { } } + const message = forceUpdate ? 'toasts.refresh-covers-queued' : 'toasts.generate-colorscape-queued'; + this.libraryService.refreshMetadata(library?.id, forceUpdate).subscribe((res: any) => { - this.toastr.info(translate('toasts.scan-queued', {name: library.name})); + this.toastr.info(translate(message, {name: library.name})); + if (callback) { callback(library); } @@ -226,17 +229,24 @@ export class ActionService implements OnDestroy { * Start a metadata refresh for a Series * @param series Series, must have libraryId, id and name populated * @param callback Optional callback to perform actions after API completes + * @param forceUpdate If cache should be checked or not */ - async refreshMetdata(series: Series, callback?: SeriesActionCallback) { - if (!await this.confirmService.confirm(translate('toasts.confirm-regen-covers'))) { - if (callback) { - callback(series); + async refreshSeriesMetadata(series: Series, callback?: SeriesActionCallback, forceUpdate: boolean = true) { + + // Prompt the user if we are doing a forced call + if (forceUpdate) { + if (!await this.confirmService.confirm(translate('toasts.confirm-regen-covers'))) { + if (callback) { + callback(series); + } + return; } - return; } - this.seriesService.refreshMetadata(series).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(translate('toasts.refresh-covers-queued', {name: series.name})); + const message = forceUpdate ? 'toasts.refresh-covers-queued' : 'toasts.generate-colorscape-queued'; + + this.seriesService.refreshMetadata(series, forceUpdate).pipe(take(1)).subscribe((res: any) => { + this.toastr.info(translate(message, {name: series.name})); if (callback) { callback(series); } diff --git a/UI/Web/src/app/_services/chapter.service.ts b/UI/Web/src/app/_services/chapter.service.ts new file mode 100644 index 000000000..8588b914f --- /dev/null +++ b/UI/Web/src/app/_services/chapter.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import {environment} from "../../environments/environment"; +import {HttpClient} from "@angular/common/http"; +import {AccountService} from "./account.service"; +import {UserCollection} from "../_models/collection-tag"; +import {Chapter} from "../_models/chapter"; +import {HourEstimateRange} from "../_models/series-detail/hour-estimate-range"; + +@Injectable({ + providedIn: 'root' +}) +export class ChapterService { + + baseUrl = environment.apiUrl; + + constructor(private httpClient: HttpClient) { } + + getChapterMetadata(chapterId: number) { + return this.httpClient.get(this.baseUrl + 'chapter/?chapterId=' + chapterId); + } + +} diff --git a/UI/Web/src/app/_services/series.service.ts b/UI/Web/src/app/_services/series.service.ts index 4320305d2..e10a6744e 100644 --- a/UI/Web/src/app/_services/series.service.ts +++ b/UI/Web/src/app/_services/series.service.ts @@ -143,8 +143,8 @@ export class SeriesService { } - refreshMetadata(series: Series) { - return this.httpClient.post(this.baseUrl + 'series/refresh-metadata', {libraryId: series.libraryId, seriesId: series.id}); + refreshMetadata(series: Series, force = true) { + return this.httpClient.post(this.baseUrl + 'series/refresh-metadata', {libraryId: series.libraryId, seriesId: series.id, forceUpdate: force}); } scan(libraryId: number, seriesId: number, force = false) { diff --git a/UI/Web/src/app/_services/statistics.service.ts b/UI/Web/src/app/_services/statistics.service.ts index f729084c5..1a0984f2b 100644 --- a/UI/Web/src/app/_services/statistics.service.ts +++ b/UI/Web/src/app/_services/statistics.service.ts @@ -13,7 +13,7 @@ import { StatCount } from '../statistics/_models/stat-count'; import { PublicationStatus } from '../_models/metadata/publication-status'; import { MangaFormat } from '../_models/manga-format'; import { TextResonse } from '../_types/text-response'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; import {KavitaPlusMetadataBreakdown} from "../statistics/_models/kavitaplus-metadata-breakdown"; import {throttleTime} from "rxjs/operators"; import {DEBOUNCE_TIME} from "../shared/_services/download.service"; diff --git a/UI/Web/src/app/_services/theme.service.ts b/UI/Web/src/app/_services/theme.service.ts index b977e22ba..6991136cb 100644 --- a/UI/Web/src/app/_services/theme.service.ts +++ b/UI/Web/src/app/_services/theme.service.ts @@ -19,7 +19,7 @@ import {SiteTheme, ThemeProvider} from '../_models/preferences/site-theme'; import {TextResonse} from '../_types/text-response'; import {EVENTS, MessageHubService} from './message-hub.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {DownloadableSiteTheme} from "../_models/theme/downloadable-site-theme"; import {NgxFileDropEntry} from "ngx-file-drop"; import {SiteThemeUpdatedEvent} from "../_models/events/site-theme-updated-event"; diff --git a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts index 57003c3a9..2bf7dae4b 100644 --- a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts +++ b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts @@ -12,7 +12,7 @@ import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle} from ' import { AccountService } from 'src/app/_services/account.service'; import { Action, ActionItem } from 'src/app/_services/action-factory.service'; import {AsyncPipe, CommonModule, NgTemplateOutlet} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {DynamicListPipe} from "./_pipes/dynamic-list.pipe"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; diff --git a/UI/Web/src/app/_single-module/review-card-modal/review-card-modal.component.ts b/UI/Web/src/app/_single-module/review-card-modal/review-card-modal.component.ts index 4926ac5e8..d1329e0f3 100644 --- a/UI/Web/src/app/_single-module/review-card-modal/review-card-modal.component.ts +++ b/UI/Web/src/app/_single-module/review-card-modal/review-card-modal.component.ts @@ -13,7 +13,7 @@ import {ReactiveFormsModule} from "@angular/forms"; import {UserReview} from "../review-card/user-review"; import {SpoilerComponent} from "../spoiler/spoiler.component"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {ProviderImagePipe} from "../../_pipes/provider-image.pipe"; diff --git a/UI/Web/src/app/_single-module/review-card/review-card.component.html b/UI/Web/src/app/_single-module/review-card/review-card.component.html index f9f8c53c3..54338740b 100644 --- a/UI/Web/src/app/_single-module/review-card/review-card.component.html +++ b/UI/Web/src/app/_single-module/review-card/review-card.component.html @@ -1,5 +1,5 @@ -
+
diff --git a/UI/Web/src/app/_single-module/review-card/review-card.component.scss b/UI/Web/src/app/_single-module/review-card/review-card.component.scss index 64485c3dd..0dd2f9f93 100644 --- a/UI/Web/src/app/_single-module/review-card/review-card.component.scss +++ b/UI/Web/src/app/_single-module/review-card/review-card.component.scss @@ -1,3 +1,10 @@ +.review-card { + max-width: 320px; + max-height: 160px; + height: 160px; + width: 320px; +} + .profile-image { font-size: 1.2rem; padding: 20px; diff --git a/UI/Web/src/app/_single-module/review-card/review-card.component.ts b/UI/Web/src/app/_single-module/review-card/review-card.component.ts index 8b791afb3..c7ba47ad8 100644 --- a/UI/Web/src/app/_single-module/review-card/review-card.component.ts +++ b/UI/Web/src/app/_single-module/review-card/review-card.component.ts @@ -22,7 +22,7 @@ import {ReadMoreComponent} from "../../shared/read-more/read-more.component"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {ImageComponent} from "../../shared/image/image.component"; import {ProviderImagePipe} from "../../_pipes/provider-image.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ScrobbleProvider} from "../../_services/scrobbling.service"; @Component({ diff --git a/UI/Web/src/app/_single-module/review-series-modal/review-series-modal.component.ts b/UI/Web/src/app/_single-module/review-series-modal/review-series-modal.component.ts index f7158b27c..227938b25 100644 --- a/UI/Web/src/app/_single-module/review-series-modal/review-series-modal.component.ts +++ b/UI/Web/src/app/_single-module/review-series-modal/review-series-modal.component.ts @@ -12,7 +12,7 @@ import {NgbActiveModal, NgbRating} from '@ng-bootstrap/ng-bootstrap'; import { SeriesService } from 'src/app/_services/series.service'; import {UserReview} from "../review-card/user-review"; import {CommonModule} from "@angular/common"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {ConfirmService} from "../../shared/confirm.service"; import {ToastrService} from "ngx-toastr"; diff --git a/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.ts b/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.ts index f2eeefbf5..bb68620fd 100644 --- a/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.ts +++ b/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.ts @@ -1,6 +1,6 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core'; import {CommonModule, NgOptimizedImage} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {NgbActiveOffcanvas, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {ExternalSeriesDetail, SeriesStaff} from "../../_models/series-detail/external-series-detail"; import {SeriesService} from "../../_services/series.service"; diff --git a/UI/Web/src/app/_single-module/spoiler/spoiler.component.ts b/UI/Web/src/app/_single-module/spoiler/spoiler.component.ts index 6eefdcb70..4c5021479 100644 --- a/UI/Web/src/app/_single-module/spoiler/spoiler.component.ts +++ b/UI/Web/src/app/_single-module/spoiler/spoiler.component.ts @@ -9,7 +9,7 @@ import { } from '@angular/core'; import {CommonModule} from '@angular/common'; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-spoiler', diff --git a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts index 34dcad9f1..2d275a27c 100644 --- a/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts +++ b/UI/Web/src/app/_single-module/user-scrobble-history/user-scrobble-history.component.ts @@ -11,9 +11,9 @@ import {debounceTime, take} from "rxjs/operators"; import {PaginatedResult, Pagination} from "../../_models/pagination"; import {SortableHeader, SortEvent} from "../table/_directives/sortable-header.directive"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; -import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; +import {TranslocoLocaleModule} from "@jsverse/transloco-locale"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {ToastrService} from "ngx-toastr"; import {LooseLeafOrDefaultNumber, SpecialVolumeNumber} from "../../_models/chapter"; diff --git a/UI/Web/src/app/admin/_modals/directory-picker/directory-picker.component.ts b/UI/Web/src/app/admin/_modals/directory-picker/directory-picker.component.ts index 6fa717720..3a5ba05e5 100644 --- a/UI/Web/src/app/admin/_modals/directory-picker/directory-picker.component.ts +++ b/UI/Web/src/app/admin/_modals/directory-picker/directory-picker.component.ts @@ -6,7 +6,7 @@ import { DirectoryDto } from 'src/app/_models/system/directory-dto'; import { LibraryService } from '../../../_services/library.service'; import { NgIf, NgFor, NgClass } from '@angular/common'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {WikiLink} from "../../../_models/wiki"; diff --git a/UI/Web/src/app/admin/_modals/library-access-modal/library-access-modal.component.ts b/UI/Web/src/app/admin/_modals/library-access-modal/library-access-modal.component.ts index 5ea4ac0e9..ab6965b77 100644 --- a/UI/Web/src/app/admin/_modals/library-access-modal/library-access-modal.component.ts +++ b/UI/Web/src/app/admin/_modals/library-access-modal/library-access-modal.component.ts @@ -5,7 +5,7 @@ import {Member} from 'src/app/_models/auth/member'; import {LibraryService} from 'src/app/_services/library.service'; import {NgFor, NgIf} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SelectionModel} from "../../../typeahead/_models/selection-model"; @Component({ diff --git a/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.ts b/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.ts index a764b3885..5c0f554f3 100644 --- a/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.ts +++ b/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.ts @@ -5,7 +5,7 @@ import { Member } from 'src/app/_models/auth/member'; import { AccountService } from 'src/app/_services/account.service'; import { SentenceCasePipe } from '../../../_pipes/sentence-case.pipe'; import { NgIf } from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {ToastrService} from "ngx-toastr"; @Component({ diff --git a/UI/Web/src/app/admin/edit-user/edit-user.component.ts b/UI/Web/src/app/admin/edit-user/edit-user.component.ts index 658c0abe6..b460e82f8 100644 --- a/UI/Web/src/app/admin/edit-user/edit-user.component.ts +++ b/UI/Web/src/app/admin/edit-user/edit-user.component.ts @@ -10,7 +10,7 @@ import {RestrictionSelectorComponent} from '../../user-settings/restriction-sele import {LibrarySelectorComponent} from '../library-selector/library-selector.component'; import {RoleSelectorComponent} from '../role-selector/role-selector.component'; import {NgIf} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; const AllowedUsernameCharacters = /^[\sa-zA-Z0-9\-._@+/\s]*$/; diff --git a/UI/Web/src/app/admin/invite-user/invite-user.component.ts b/UI/Web/src/app/admin/invite-user/invite-user.component.ts index 1e6a64605..1c5e0b6fa 100644 --- a/UI/Web/src/app/admin/invite-user/invite-user.component.ts +++ b/UI/Web/src/app/admin/invite-user/invite-user.component.ts @@ -12,7 +12,7 @@ import { RestrictionSelectorComponent } from '../../user-settings/restriction-se import { LibrarySelectorComponent } from '../library-selector/library-selector.component'; import { RoleSelectorComponent } from '../role-selector/role-selector.component'; import { NgIf } from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; @Component({ diff --git a/UI/Web/src/app/admin/library-selector/library-selector.component.ts b/UI/Web/src/app/admin/library-selector/library-selector.component.ts index 3293d6162..8f7502869 100644 --- a/UI/Web/src/app/admin/library-selector/library-selector.component.ts +++ b/UI/Web/src/app/admin/library-selector/library-selector.component.ts @@ -12,7 +12,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {Library} from 'src/app/_models/library/library'; import {Member} from 'src/app/_models/auth/member'; import {LibraryService} from 'src/app/_services/library.service'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {LoadingComponent} from "../../shared/loading/loading.component"; import {SelectionModel} from "../../typeahead/_models/selection-model"; diff --git a/UI/Web/src/app/admin/license/license.component.ts b/UI/Web/src/app/admin/license/license.component.ts index c050f0ca0..0a42b636e 100644 --- a/UI/Web/src/app/admin/license/license.component.ts +++ b/UI/Web/src/app/admin/license/license.component.ts @@ -13,7 +13,7 @@ import { LoadingComponent } from '../../shared/loading/loading.component'; import { NgbTooltip, NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; import { NgIf } from '@angular/common'; import {environment} from "../../../environments/environment"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {catchError} from "rxjs"; import {WikiLink} from "../../_models/wiki"; import {RouterLink} from "@angular/router"; diff --git a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.html b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.html index 1b96d1f27..20315fb8a 100644 --- a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.html +++ b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.html @@ -89,9 +89,7 @@
-
- -
+
@@ -142,9 +140,7 @@
-
- -
+
@@ -154,8 +150,6 @@
- -
diff --git a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts index a14353c99..f3b872c36 100644 --- a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts +++ b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts @@ -1,7 +1,7 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import {ToastrService} from 'ngx-toastr'; -import {take} from 'rxjs'; +import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs'; import {SettingsService} from '../settings.service'; import {ServerSettings} from '../_models/server-settings'; import { @@ -9,13 +9,14 @@ import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import {NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common'; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; import {ManageMediaIssuesComponent} from "../manage-media-issues/manage-media-issues.component"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {BytesPipe} from "../../_pipes/bytes.pipe"; +import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; @Component({ selector: 'app-manage-email-settings', @@ -31,6 +32,7 @@ export class ManageEmailSettingsComponent implements OnInit { private readonly cdRef = inject(ChangeDetectorRef); private readonly settingsService = inject(SettingsService); private readonly toastr = inject(ToastrService); + private readonly destroyRef = inject(DestroyRef); serverSettings!: ServerSettings; settingsForm: FormGroup = new FormGroup({}); @@ -50,6 +52,23 @@ export class ManageEmailSettingsComponent implements OnInit { this.settingsForm.addControl('sizeLimit', new FormControl(this.serverSettings.smtpConfig.sizeLimit, [Validators.min(1)])); this.settingsForm.addControl('customizedTemplates', new FormControl(this.serverSettings.smtpConfig.customizedTemplates, [Validators.min(1)])); + // Automatically save settings as we edit them + this.settingsForm.valueChanges.pipe( + distinctUntilChanged(), + debounceTime(100), + filter(_ => this.settingsForm.valid), + takeUntilDestroyed(this.destroyRef), + switchMap(_ => { + const data = this.packData(); + return this.settingsService.updateServerSettings(data); + }), + tap(settings => { + this.serverSettings = settings; + this.resetForm(); + this.cdRef.markForCheck(); + }) + ).subscribe(); + this.cdRef.markForCheck(); }); } @@ -57,15 +76,15 @@ export class ManageEmailSettingsComponent implements OnInit { resetForm() { this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName); - this.settingsForm.addControl('host', new FormControl(this.serverSettings.smtpConfig.host, [])); - this.settingsForm.addControl('port', new FormControl(this.serverSettings.smtpConfig.port, [])); - this.settingsForm.addControl('userName', new FormControl(this.serverSettings.smtpConfig.userName, [])); - this.settingsForm.addControl('enableSsl', new FormControl(this.serverSettings.smtpConfig.enableSsl, [])); - this.settingsForm.addControl('password', new FormControl(this.serverSettings.smtpConfig.password, [])); - this.settingsForm.addControl('senderAddress', new FormControl(this.serverSettings.smtpConfig.senderAddress, [])); - this.settingsForm.addControl('senderDisplayName', new FormControl(this.serverSettings.smtpConfig.senderDisplayName, [])); - this.settingsForm.addControl('sizeLimit', new FormControl(this.serverSettings.smtpConfig.sizeLimit, [Validators.min(1)])); - this.settingsForm.addControl('customizedTemplates', new FormControl(this.serverSettings.smtpConfig.customizedTemplates, [Validators.min(1)])); + this.settingsForm.get('host')?.setValue(this.serverSettings.smtpConfig.host, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('port')?.setValue(this.serverSettings.smtpConfig.port, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('userName')?.setValue(this.serverSettings.smtpConfig.userName, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('enableSsl')?.setValue(this.serverSettings.smtpConfig.enableSsl, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('password')?.setValue(this.serverSettings.smtpConfig.password, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('senderAddress')?.setValue(this.serverSettings.smtpConfig.senderAddress, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('senderDisplayName')?.setValue(this.serverSettings.smtpConfig.senderDisplayName, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('sizeLimit')?.setValue(this.serverSettings.smtpConfig.sizeLimit, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('customizedTemplates')?.setValue(this.serverSettings.smtpConfig.customizedTemplates, {onlySelf: true, emitEvent: false}); this.settingsForm.markAsPristine(); this.cdRef.markForCheck(); } @@ -88,7 +107,7 @@ export class ManageEmailSettingsComponent implements OnInit { this.cdRef.markForCheck(); } - async saveSettings() { + packData() { const modelSettings = Object.assign({}, this.serverSettings); modelSettings.emailServiceUrl = this.settingsForm.get('emailServiceUrl')?.value; modelSettings.hostName = this.settingsForm.get('hostName')?.value; @@ -103,6 +122,12 @@ export class ManageEmailSettingsComponent implements OnInit { modelSettings.smtpConfig.sizeLimit = this.settingsForm.get('sizeLimit')?.value; modelSettings.smtpConfig.customizedTemplates = this.settingsForm.get('customizedTemplates')?.value; + return modelSettings; + } + + async saveSettings() { + const modelSettings = this.packData(); + this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => { this.serverSettings = settings; this.resetForm(); diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.ts b/UI/Web/src/app/admin/manage-library/manage-library.component.ts index df06da44b..06df1b0bf 100644 --- a/UI/Web/src/app/admin/manage-library/manage-library.component.ts +++ b/UI/Web/src/app/admin/manage-library/manage-library.component.ts @@ -21,7 +21,7 @@ import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe'; import { TimeAgoPipe } from '../../_pipes/time-ago.pipe'; import { LibraryTypePipe } from '../../_pipes/library-type.pipe'; import { RouterLink } from '@angular/router'; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; import {DefaultDatePipe} from "../../_pipes/default-date.pipe"; import {AsyncPipe, TitleCasePipe} from "@angular/common"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; @@ -164,10 +164,10 @@ export class ManageLibraryComponent implements OnInit { await this.actionService.scanLibrary(library); break; case(Action.RefreshMetadata): - await this.actionService.refreshMetadata(library); + await this.actionService.refreshLibraryMetadata(library); break; case(Action.GenerateColorScape): - await this.actionService.refreshMetadata(library, undefined, false); + await this.actionService.refreshLibraryMetadata(library, undefined, false); break; case(Action.Edit): this.editLibrary(library) diff --git a/UI/Web/src/app/admin/manage-media-issues/manage-media-issues.component.ts b/UI/Web/src/app/admin/manage-media-issues/manage-media-issues.component.ts index a9240a804..4ca6358e1 100644 --- a/UI/Web/src/app/admin/manage-media-issues/manage-media-issues.component.ts +++ b/UI/Web/src/app/admin/manage-media-issues/manage-media-issues.component.ts @@ -19,7 +19,7 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { FilterPipe } from '../../_pipes/filter.pipe'; import { LoadingComponent } from '../../shared/loading/loading.component'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {WikiLink} from "../../_models/wiki"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {DefaultDatePipe} from "../../_pipes/default-date.pipe"; diff --git a/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.html b/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.html index 08effc096..923052f4c 100644 --- a/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.html +++ b/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.html @@ -71,8 +71,6 @@
- -
diff --git a/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.ts b/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.ts index 63a66c68d..b6a8d7e59 100644 --- a/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.ts +++ b/UI/Web/src/app/admin/manage-media-settings/manage-media-settings.component.ts @@ -1,7 +1,7 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import {ToastrService} from 'ngx-toastr'; -import {take} from 'rxjs'; +import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs'; import {SettingsService} from '../settings.service'; import {ServerSettings} from '../_models/server-settings'; import {DirectoryPickerComponent, DirectoryPickerResult} from '../_modals/directory-picker/directory-picker.component'; @@ -20,7 +20,7 @@ import { import {allEncodeFormats} from '../_models/encode-format'; import {ManageMediaIssuesComponent} from '../manage-media-issues/manage-media-issues.component'; import {NgFor, NgIf, NgTemplateOutlet} from '@angular/common'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {allCoverImageSizes} from '../_models/cover-image-size'; import {pageLayoutModes} from "../../_models/preferences/preferences"; import {PageLayoutModePipe} from "../../_pipes/page-layout-mode.pipe"; @@ -28,6 +28,7 @@ import {SettingItemComponent} from "../../settings/_components/setting-item/sett import {EncodeFormatPipe} from "../../_pipes/encode-format.pipe"; import {CoverImageSizePipe} from "../../_pipes/cover-image-size.pipe"; import {ConfirmService} from "../../shared/confirm.service"; +import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; @Component({ selector: 'app-manage-media-settings', @@ -47,6 +48,7 @@ export class ManageMediaSettingsComponent implements OnInit { private readonly settingsService = inject(SettingsService); private readonly toastr = inject(ToastrService); private readonly modalService = inject(NgbModal); + private readonly destroyRef = inject(DestroyRef); protected readonly allEncodeFormats = allEncodeFormats; protected readonly allCoverImageSizes = allCoverImageSizes; @@ -61,33 +63,46 @@ export class ManageMediaSettingsComponent implements OnInit { this.settingsForm.addControl('encodeMediaAs', new FormControl(this.serverSettings.encodeMediaAs, [Validators.required])); this.settingsForm.addControl('bookmarksDirectory', new FormControl(this.serverSettings.bookmarksDirectory, [Validators.required])); this.settingsForm.addControl('coverImageSize', new FormControl(this.serverSettings.coverImageSize, [Validators.required])); + + // Automatically save settings as we edit them + this.settingsForm.valueChanges.pipe( + distinctUntilChanged(), + debounceTime(100), + filter(_ => this.settingsForm.valid), + takeUntilDestroyed(this.destroyRef), + switchMap(_ => { + const data = this.packData(); + return this.settingsService.updateServerSettings(data); + }), + tap(settings => { + this.serverSettings = settings; + this.resetForm(); + this.cdRef.markForCheck(); + }) + ).subscribe(); + this.cdRef.markForCheck(); }); } resetForm() { - this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs); - this.settingsForm.get('bookmarksDirectory')?.setValue(this.serverSettings.bookmarksDirectory); - this.settingsForm.get('coverImageSize')?.setValue(this.serverSettings.coverImageSize); + this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookmarksDirectory')?.setValue(this.serverSettings.bookmarksDirectory, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('coverImageSize')?.setValue(this.serverSettings.coverImageSize, {onlySelf: true, emitEvent: false}); this.settingsForm.markAsPristine(); this.cdRef.markForCheck(); } - saveSettings() { + packData() { const modelSettings = Object.assign({}, this.serverSettings); modelSettings.encodeMediaAs = parseInt(this.settingsForm.get('encodeMediaAs')?.value, 10); modelSettings.bookmarksDirectory = this.settingsForm.get('bookmarksDirectory')?.value; modelSettings.coverImageSize = parseInt(this.settingsForm.get('coverImageSize')?.value, 10); - this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe(async (settings: ServerSettings) => { - this.serverSettings = settings; - this.resetForm(); - this.toastr.success(this.translocoService.translate('toasts.server-settings-updated')); - }, (err: any) => { - console.error('error: ', err); - }); + return modelSettings; } + async resetToDefaults() { if (!await this.confirmService.confirm(translate('toasts.confirm-reset-server-settings'))) return; diff --git a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts index 37bb49666..13c8164e0 100644 --- a/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts +++ b/UI/Web/src/app/admin/manage-scrobble-errors/manage-scrobble-errors.component.ts @@ -25,10 +25,10 @@ import {EditSeriesModalComponent} from "../../cards/_modals/edit-series-modal/ed import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; import {FilterPipe} from "../../_pipes/filter.pipe"; import {LoadingComponent} from "../../shared/loading/loading.component"; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; import {DefaultDatePipe} from "../../_pipes/default-date.pipe"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; -import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; +import {TranslocoLocaleModule} from "@jsverse/transloco-locale"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; @Component({ diff --git a/UI/Web/src/app/admin/manage-settings/manage-settings.component.html b/UI/Web/src/app/admin/manage-settings/manage-settings.component.html index 2ed5ca8f4..4a98830b7 100644 --- a/UI/Web/src/app/admin/manage-settings/manage-settings.component.html +++ b/UI/Web/src/app/admin/manage-settings/manage-settings.component.html @@ -318,8 +318,6 @@
- -
diff --git a/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts b/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts index 8c6164286..bd5789435 100644 --- a/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts +++ b/UI/Web/src/app/admin/manage-settings/manage-settings.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import {ToastrService} from 'ngx-toastr'; import {take} from 'rxjs/operators'; @@ -7,13 +7,15 @@ import {SettingsService} from '../settings.service'; import {ServerSettings} from '../_models/server-settings'; import {NgbTooltip} from '@ng-bootstrap/ng-bootstrap'; import {NgTemplateOutlet, TitleCasePipe} from '@angular/common'; -import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco"; import {WikiLink} from "../../_models/wiki"; import {PageLayoutModePipe} from "../../_pipes/page-layout-mode.pipe"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; import {ConfirmService} from "../../shared/confirm.service"; +import {debounceTime, distinctUntilChanged, filter, switchMap, tap} from "rxjs"; +import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; const ValidIpAddress = /^(\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*\,)*\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*$/i; @@ -33,6 +35,7 @@ export class ManageSettingsComponent implements OnInit { private readonly toastr = inject(ToastrService); private readonly serverService = inject(ServerService); private readonly confirmService = inject(ConfirmService); + private readonly destroyRef = inject(DestroyRef); protected readonly WikiLink = WikiLink; serverSettings!: ServerSettings; @@ -76,6 +79,23 @@ export class ManageSettingsComponent implements OnInit { this.settingsForm.addControl('onDeckProgressDays', new FormControl(this.serverSettings.onDeckProgressDays, [Validators.required])); this.settingsForm.addControl('onDeckUpdateDays', new FormControl(this.serverSettings.onDeckUpdateDays, [Validators.required])); + // Automatically save settings as we edit them + this.settingsForm.valueChanges.pipe( + distinctUntilChanged(), + debounceTime(100), + filter(_ => this.settingsForm.valid), + takeUntilDestroyed(this.destroyRef), + switchMap(_ => { + const data = this.packData(); + return this.settingsService.updateServerSettings(data); + }), + tap(settings => { + this.serverSettings = settings; + this.resetForm(); + this.cdRef.markForCheck(); + }) + ).subscribe(); + this.serverService.getServerInfo().subscribe(info => { if (info.isDocker) { this.settingsForm.get('ipAddresses')?.disable(); @@ -89,42 +109,35 @@ export class ManageSettingsComponent implements OnInit { } resetForm() { - this.settingsForm.get('cacheDirectory')?.setValue(this.serverSettings.cacheDirectory); - this.settingsForm.get('scanTask')?.setValue(this.serverSettings.taskScan); - this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup); - this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup); - this.settingsForm.get('ipAddresses')?.setValue(this.serverSettings.ipAddresses); - this.settingsForm.get('port')?.setValue(this.serverSettings.port); - this.settingsForm.get('loggingLevel')?.setValue(this.serverSettings.loggingLevel); - this.settingsForm.get('allowStatCollection')?.setValue(this.serverSettings.allowStatCollection); - this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds); - this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl); - this.settingsForm.get('emailServiceUrl')?.setValue(this.serverSettings.emailServiceUrl); - this.settingsForm.get('totalBackups')?.setValue(this.serverSettings.totalBackups); - this.settingsForm.get('totalLogs')?.setValue(this.serverSettings.totalLogs); - this.settingsForm.get('enableFolderWatching')?.setValue(this.serverSettings.enableFolderWatching); - this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs); - this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName); - this.settingsForm.get('cacheSize')?.setValue(this.serverSettings.cacheSize); - this.settingsForm.get('onDeckProgressDays')?.setValue(this.serverSettings.onDeckProgressDays); - this.settingsForm.get('onDeckUpdateDays')?.setValue(this.serverSettings.onDeckUpdateDays); + this.settingsForm.get('cacheDirectory')?.setValue(this.serverSettings.cacheDirectory, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('scanTask')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('ipAddresses')?.setValue(this.serverSettings.ipAddresses, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('port')?.setValue(this.serverSettings.port, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('loggingLevel')?.setValue(this.serverSettings.loggingLevel, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('allowStatCollection')?.setValue(this.serverSettings.allowStatCollection, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('emailServiceUrl')?.setValue(this.serverSettings.emailServiceUrl, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('totalBackups')?.setValue(this.serverSettings.totalBackups, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('totalLogs')?.setValue(this.serverSettings.totalLogs, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('enableFolderWatching')?.setValue(this.serverSettings.enableFolderWatching, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('cacheSize')?.setValue(this.serverSettings.cacheSize, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('onDeckProgressDays')?.setValue(this.serverSettings.onDeckProgressDays, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('onDeckUpdateDays')?.setValue(this.serverSettings.onDeckUpdateDays, {onlySelf: true, emitEvent: false}); this.settingsForm.markAsPristine(); this.cdRef.markForCheck(); } - async saveSettings() { + packData() { const modelSettings = this.settingsForm.value; modelSettings.bookmarksDirectory = this.serverSettings.bookmarksDirectory; modelSettings.smtpConfig = this.serverSettings.smtpConfig; - - this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => { - this.serverSettings = settings; - this.resetForm(); - this.toastr.success(this.translocoService.translate('toasts.server-settings-updated')); - }, (err: any) => { - console.error('error: ', err); - }); + return modelSettings; } async resetToDefaults() { diff --git a/UI/Web/src/app/admin/manage-system/manage-system.component.ts b/UI/Web/src/app/admin/manage-system/manage-system.component.ts index 27f129c8b..a5042fb6d 100644 --- a/UI/Web/src/app/admin/manage-system/manage-system.component.ts +++ b/UI/Web/src/app/admin/manage-system/manage-system.component.ts @@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} f import {ServerService} from 'src/app/_services/server.service'; import {ServerInfoSlim} from '../_models/server-info'; import {DatePipe, NgIf} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ChangelogComponent} from "../../announcements/_components/changelog/changelog.component"; import {DefaultDatePipe} from "../../_pipes/default-date.pipe"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; diff --git a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html index 5146cab4e..c265e875c 100644 --- a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html +++ b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html @@ -125,8 +125,6 @@
- -
diff --git a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts index 3b43d8056..c0a11eb59 100644 --- a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts +++ b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.ts @@ -4,7 +4,7 @@ import {ToastrService} from 'ngx-toastr'; import {SettingsService} from '../settings.service'; import {ServerSettings} from '../_models/server-settings'; import {shareReplay, take} from 'rxjs/operators'; -import {debounceTime, defer, forkJoin, Observable, of, switchMap, tap} from 'rxjs'; +import {debounceTime, defer, distinctUntilChanged, filter, forkJoin, Observable, of, switchMap, tap} from 'rxjs'; import {ServerService} from 'src/app/_services/server.service'; import {Job} from 'src/app/_models/job/job'; import {UpdateNotificationModalComponent} from 'src/app/shared/update-notification/update-notification-modal.component'; @@ -12,8 +12,8 @@ import {NgbModal, NgbTooltip} from '@ng-bootstrap/ng-bootstrap'; import {DownloadService} from 'src/app/shared/_services/download.service'; import {DefaultValuePipe} from '../../_pipes/default-value.pipe'; import {AsyncPipe, DatePipe, NgFor, NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common'; -import {translate, TranslocoModule} from "@ngneat/transloco"; -import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; +import {translate, TranslocoModule} from "@jsverse/transloco"; +import {TranslocoLocaleModule} from "@jsverse/transloco-locale"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; @@ -136,6 +136,7 @@ export class ManageTasksSettingsComponent implements OnInit { this.logLevels = result.levels; this.serverSettings = result.settings; + this.settingsForm.addControl('taskScan', new FormControl(this.serverSettings.taskScan, [Validators.required])); this.settingsForm.addControl('taskBackup', new FormControl(this.serverSettings.taskBackup, [Validators.required])); this.settingsForm.addControl('taskCleanup', new FormControl(this.serverSettings.taskCleanup, [Validators.required])); @@ -203,6 +204,24 @@ export class ManageTasksSettingsComponent implements OnInit { takeUntilDestroyed(this.destroyRef) ).subscribe(); + // Automatically save settings as we edit them + this.settingsForm.valueChanges.pipe( + distinctUntilChanged(), + debounceTime(100), + filter(_ => this.settingsForm.valid), + takeUntilDestroyed(this.destroyRef), + switchMap(_ => { + const data = this.packData(); + return this.settingsService.updateServerSettings(data); + }), + tap(settings => { + this.serverSettings = settings; + this.resetForm(); + this.recurringTasks$ = this.serverService.getRecurringJobs().pipe(shareReplay()); + this.cdRef.markForCheck(); + }) + ).subscribe(); + this.cdRef.markForCheck(); }); @@ -212,33 +231,33 @@ export class ManageTasksSettingsComponent implements OnInit { resetForm() { - this.settingsForm.get('taskScan')?.setValue(this.serverSettings.taskScan); - this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup); - this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup); + this.settingsForm.get('taskScan')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false}); if (!this.taskFrequencies.includes(this.serverSettings.taskScan)) { - this.settingsForm.get('taskScanCustom')?.setValue(this.serverSettings.taskScan); + this.settingsForm.get('taskScanCustom')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false}); } else { - this.settingsForm.get('taskScanCustom')?.setValue(''); + this.settingsForm.get('taskScanCustom')?.setValue('', {onlySelf: true, emitEvent: false}); } if (!this.taskFrequencies.includes(this.serverSettings.taskBackup)) { - this.settingsForm.get('taskBackupCustom')?.setValue(this.serverSettings.taskBackup); + this.settingsForm.get('taskBackupCustom')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false}); } else { - this.settingsForm.get('taskBackupCustom')?.setValue(''); + this.settingsForm.get('taskBackupCustom')?.setValue('', {onlySelf: true, emitEvent: false}); } if (!this.taskFrequencies.includes(this.serverSettings.taskCleanup)) { - this.settingsForm.get('taskCleanupCustom')?.setValue(this.serverSettings.taskCleanup); + this.settingsForm.get('taskCleanupCustom')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false}); } else { - this.settingsForm.get('taskCleanupCustom')?.setValue(''); + this.settingsForm.get('taskCleanupCustom')?.setValue('', {onlySelf: true, emitEvent: false}); } this.settingsForm.markAsPristine(); this.cdRef.markForCheck(); } - async saveSettings() { + packData() { const modelSettings = Object.assign({}, this.serverSettings); modelSettings.taskBackup = this.settingsForm.get('taskBackup')?.value; modelSettings.taskScan = this.settingsForm.get('taskScan')?.value; @@ -256,18 +275,10 @@ export class ManageTasksSettingsComponent implements OnInit { modelSettings.taskCleanup = this.settingsForm.get('taskCleanupCustom')?.value; } - - this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => { - this.serverSettings = settings; - this.resetForm(); - this.recurringTasks$ = this.serverService.getRecurringJobs().pipe(shareReplay()); - this.toastr.success(translate('toasts.server-settings-updated')); - this.cdRef.markForCheck(); - }, (err: any) => { - console.error('error: ', err); - }); + return modelSettings; } + async resetToDefaults() { if (!await this.confirmService.confirm(translate('toasts.confirm-reset-server-settings'))) return; diff --git a/UI/Web/src/app/admin/manage-users/manage-users.component.ts b/UI/Web/src/app/admin/manage-users/manage-users.component.ts index 1de33f6fc..850204e07 100644 --- a/UI/Web/src/app/admin/manage-users/manage-users.component.ts +++ b/UI/Web/src/app/admin/manage-users/manage-users.component.ts @@ -13,7 +13,7 @@ import {EditUserComponent} from '../edit-user/edit-user.component'; import {Router} from '@angular/router'; import {TagBadgeComponent} from '../../shared/tag-badge/tag-badge.component'; import {AsyncPipe, DatePipe, NgClass, NgIf, TitleCasePipe} from '@angular/common'; -import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco"; import {DefaultDatePipe} from "../../_pipes/default-date.pipe"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {ReadMoreComponent} from "../../shared/read-more/read-more.component"; diff --git a/UI/Web/src/app/admin/role-selector/role-selector.component.ts b/UI/Web/src/app/admin/role-selector/role-selector.component.ts index 05d938a54..54ffe4ca5 100644 --- a/UI/Web/src/app/admin/role-selector/role-selector.component.ts +++ b/UI/Web/src/app/admin/role-selector/role-selector.component.ts @@ -13,7 +13,7 @@ import { User } from 'src/app/_models/user'; import {AccountService} from 'src/app/_services/account.service'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { NgFor } from '@angular/common'; -import {TranslocoDirective,} from "@ngneat/transloco"; +import {TranslocoDirective,} from "@jsverse/transloco"; import {SelectionModel} from "../../typeahead/_models/selection-model"; @Component({ diff --git a/UI/Web/src/app/all-filters/all-filters.component.ts b/UI/Web/src/app/all-filters/all-filters.component.ts index bb9ec0df8..021c76faa 100644 --- a/UI/Web/src/app/all-filters/all-filters.component.ts +++ b/UI/Web/src/app/all-filters/all-filters.component.ts @@ -1,8 +1,7 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core'; import {CommonModule} from '@angular/common'; import {JumpKey} from "../_models/jumpbar/jump-key"; -import {EVENTS, Message, MessageHubService} from "../_services/message-hub.service"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {CardItemComponent} from "../cards/card-item/card-item.component"; import { SideNavCompanionBarComponent diff --git a/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts b/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts index be68010f9..a13b3577f 100644 --- a/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts +++ b/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts @@ -29,7 +29,7 @@ import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/car import { BulkOperationsComponent } from '../../../cards/bulk-operations/bulk-operations.component'; import { NgIf, DecimalPipe } from '@angular/common'; import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2"; diff --git a/UI/Web/src/app/announcements/_components/announcements/announcements.component.ts b/UI/Web/src/app/announcements/_components/announcements/announcements.component.ts index c4d2a7245..b8cafec9c 100644 --- a/UI/Web/src/app/announcements/_components/announcements/announcements.component.ts +++ b/UI/Web/src/app/announcements/_components/announcements/announcements.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { ChangelogComponent } from '../changelog/changelog.component'; import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-announcements', diff --git a/UI/Web/src/app/announcements/_components/changelog/changelog.component.ts b/UI/Web/src/app/announcements/_components/changelog/changelog.component.ts index 132b347a6..2ac5212bc 100644 --- a/UI/Web/src/app/announcements/_components/changelog/changelog.component.ts +++ b/UI/Web/src/app/announcements/_components/changelog/changelog.component.ts @@ -4,7 +4,7 @@ import {ServerService} from 'src/app/_services/server.service'; import {LoadingComponent} from '../../../shared/loading/loading.component'; import {ReadMoreComponent} from '../../../shared/read-more/read-more.component'; import {AsyncPipe, DatePipe} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {AccountService} from "../../../_services/account.service"; @Component({ diff --git a/UI/Web/src/app/announcements/_components/out-of-date-modal/out-of-date-modal.component.ts b/UI/Web/src/app/announcements/_components/out-of-date-modal/out-of-date-modal.component.ts index 41e6f36d9..e5258bb09 100644 --- a/UI/Web/src/app/announcements/_components/out-of-date-modal/out-of-date-modal.component.ts +++ b/UI/Web/src/app/announcements/_components/out-of-date-modal/out-of-date-modal.component.ts @@ -2,7 +2,7 @@ import {Component, DestroyRef, inject, Input} from '@angular/core'; import {FormsModule} from "@angular/forms"; import {AsyncPipe, NgForOf, NgIf} from "@angular/common"; import {NgbActiveModal, NgbHighlight, NgbModal, NgbTypeahead} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ServerService} from "../../../_services/server.service"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {map} from "rxjs/operators"; diff --git a/UI/Web/src/app/app-routing.module.ts b/UI/Web/src/app/app-routing.module.ts index 1d4b26077..09a53815d 100644 --- a/UI/Web/src/app/app-routing.module.ts +++ b/UI/Web/src/app/app-routing.module.ts @@ -61,6 +61,11 @@ const routes: Routes = [ pathMatch: 'full', loadComponent: () => import('../app/series-detail/_components/series-detail/series-detail.component').then(c => c.SeriesDetailComponent) }, + { + path: ':libraryId/series/:seriesId/chapter/:chapterId', + pathMatch: 'full', + loadComponent: () => import('./chapter-detail/chapter-detail.component').then(c => c.ChapterDetailComponent) + }, { path: ':libraryId/series/:seriesId/manga', loadChildren: () => import('./_routes/manga-reader.router.module').then(m => m.routes) diff --git a/UI/Web/src/app/app.component.html b/UI/Web/src/app/app.component.html index 7e28223a9..f346b5602 100644 --- a/UI/Web/src/app/app.component.html +++ b/UI/Web/src/app/app.component.html @@ -1,6 +1,6 @@
@if (accountService.currentUser$ | async; as currentUser) { - @if (currentUser) { + @if (currentUser && (navService.navbarVisible$ | async) === true) {
diff --git a/UI/Web/src/app/book-reader/_components/book-line-overlay/book-line-overlay.component.ts b/UI/Web/src/app/book-reader/_components/book-line-overlay/book-line-overlay.component.ts index 9a02dd58b..67a56903f 100644 --- a/UI/Web/src/app/book-reader/_components/book-line-overlay/book-line-overlay.component.ts +++ b/UI/Web/src/app/book-reader/_components/book-line-overlay/book-line-overlay.component.ts @@ -14,7 +14,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {ReaderService} from "../../../_services/reader.service"; import {ToastrService} from "ngx-toastr"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {KEY_CODES} from "../../../shared/_services/utility.service"; enum BookLineOverlayMode { 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 7a672693b..434b6a85a 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 @@ -51,7 +51,7 @@ import { PersonalTableOfContentsComponent, PersonalToCEvent } from "../personal-table-of-contents/personal-table-of-contents.component"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; enum TabID { diff --git a/UI/Web/src/app/book-reader/_components/personal-table-of-contents/personal-table-of-contents.component.ts b/UI/Web/src/app/book-reader/_components/personal-table-of-contents/personal-table-of-contents.component.ts index 476d7e559..f57065530 100644 --- a/UI/Web/src/app/book-reader/_components/personal-table-of-contents/personal-table-of-contents.component.ts +++ b/UI/Web/src/app/book-reader/_components/personal-table-of-contents/personal-table-of-contents.component.ts @@ -13,7 +13,7 @@ import {ReaderService} from "../../../_services/reader.service"; import {PersonalToC} from "../../../_models/readers/personal-toc"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; export interface PersonalToCEvent { pageNum: number; diff --git a/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts b/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts index b4ea74806..7e3f3110f 100644 --- a/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts +++ b/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts @@ -26,7 +26,7 @@ import { BookWhiteTheme } from '../../_models/book-white-theme'; import { BookPaperTheme } from '../../_models/book-paper-theme'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; /** * Used for book reader. Do not use for other components diff --git a/UI/Web/src/app/book-reader/_components/table-of-contents/table-of-contents.component.ts b/UI/Web/src/app/book-reader/_components/table-of-contents/table-of-contents.component.ts index 86d558ecc..ecc6997c7 100644 --- a/UI/Web/src/app/book-reader/_components/table-of-contents/table-of-contents.component.ts +++ b/UI/Web/src/app/book-reader/_components/table-of-contents/table-of-contents.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { BookChapterItem } from '../../_models/book-chapter-item'; import { NgIf, NgFor } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-table-of-contents', diff --git a/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts b/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts index 68775efd9..e4800a127 100644 --- a/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts +++ b/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts @@ -30,7 +30,7 @@ import { CardItemComponent } from '../../../cards/card-item/card-item.component' import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/card-detail-layout.component'; import { BulkOperationsComponent } from '../../../cards/bulk-operations/bulk-operations.component'; import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2"; import {Title} from "@angular/platform-browser"; import {WikiLink} from "../../../_models/wiki"; 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 2a40ebc35..b8f65061b 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 @@ -18,7 +18,7 @@ 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 "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; @Component({ selector: 'app-bulk-add-to-collection', diff --git a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts index ba52bb021..df6cbf14b 100644 --- a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts +++ b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts @@ -25,7 +25,7 @@ import {UploadService} from 'src/app/_services/upload.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {DatePipe, DecimalPipe, NgIf, NgTemplateOutlet} from "@angular/common"; import {CoverImageChooserComponent} from "../../cover-image-chooser/cover-image-chooser.component"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {ScrobbleProvider} from "../../../_services/scrobbling.service"; import {FilterPipe} from "../../../_pipes/filter.pipe"; import {AccountService} from "../../../_services/account.service"; 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 7725aa776..e0b6ebc2c 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 @@ -401,12 +401,14 @@ +
  • {{t(tabs[TabID.Related])}}
  • +
  • {{t(tabs[TabID.Info])}} @@ -492,6 +494,19 @@
  • + +
  • + {{t(tabs[TabID.Tasks])}} + + @for(task of tasks; track task.action) { +
    + + + +
    + } +
    +
  • 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 8593c1857..2beea308c 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 @@ -49,14 +49,18 @@ import {PublicationStatusPipe} from "../../../_pipes/publication-status.pipe"; import {BytesPipe} from "../../../_pipes/bytes.pipe"; import {ImageComponent} from "../../../shared/image/image.component"; import {DefaultValuePipe} from "../../../_pipes/default-value.pipe"; -import {translate, TranslocoModule} from "@ngneat/transloco"; -import {TranslocoDatePipe} from "@ngneat/transloco-locale"; +import {translate, TranslocoModule} from "@jsverse/transloco"; +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"; +import {SettingButtonComponent} from "../../../settings/_components/setting-button/setting-button.component"; +import {ActionService} from "../../../_services/action.service"; +import {DownloadService} from "../../../shared/_services/download.service"; enum TabID { General = 0, @@ -66,6 +70,7 @@ enum TabID { CoverImage = 4, Related = 5, Info = 6, + Tasks = 7 } export interface EditSeriesModalCloseResult { @@ -75,6 +80,10 @@ export interface EditSeriesModalCloseResult { updateExternal: boolean } +const blackList = [Action.Edit, Action.Info, Action.IncognitoRead, Action.Read, Action.SendTo, + Action.AddToWantToReadList, Action.AddToCollection, Action.AddToReadingList, Action.RemoveFromWantToReadList, + Action.RemoveFromWantToReadList]; + @Component({ selector: 'app-edit-series-modal', standalone: true, @@ -104,6 +113,7 @@ export interface EditSeriesModalCloseResult { TranslocoDatePipe, UtcToLocalTimePipe, EditListComponent, + SettingButtonComponent, ], templateUrl: './edit-series-modal.component.html', styleUrls: ['./edit-series-modal.component.scss'], @@ -123,10 +133,14 @@ export class EditSeriesModalComponent implements OnInit { public readonly accountService = inject(AccountService); private readonly destroyRef = inject(DestroyRef); private readonly toastr = inject(ToastrService); + private readonly actionFactoryService = inject(ActionFactoryService); + private readonly actionService = inject(ActionService); + private readonly downloadService = inject(DownloadService); protected readonly TabID = TabID; protected readonly PersonRole = PersonRole; protected readonly Breakpoint = Breakpoint; + protected readonly Action = Action; @Input({required: true}) series!: Series; @@ -137,9 +151,9 @@ export class EditSeriesModalComponent implements OnInit { * A copy of the series from init. This is used to compare values for name fields to see if lock was modified */ initSeries!: Series; - + tasks = this.actionFactoryService.getActionablesForSettingsPage(this.actionFactoryService.getSeriesActions(this.runTask.bind(this)), blackList); volumeCollapsed: any = {}; - tabs = ['general-tab', 'metadata-tab', 'people-tab', 'web-links-tab', 'cover-image-tab', 'related-tab', 'info-tab']; + tabs = ['general-tab', 'metadata-tab', 'people-tab', 'web-links-tab', 'cover-image-tab', 'related-tab', 'info-tab', 'tasks-tab']; active = this.tabs[0]; editSeriesForm!: FormGroup; libraryName: string | undefined = undefined; @@ -646,4 +660,32 @@ export class EditSeriesModalComponent implements OnInit { this.cdRef.markForCheck(); } + async runTask(action: ActionItem) { + switch (action.action) { + case Action.Scan: + await this.actionService.scanSeries(this.series); + break; + case Action.RefreshMetadata: + await this.actionService.refreshSeriesMetadata(this.series); + break; + case Action.GenerateColorScape: + await this.actionService.refreshSeriesMetadata(this.series, undefined, false); + break; + case Action.AnalyzeFiles: + this.actionService.analyzeFilesForSeries(this.series); + break; + case Action.MarkAsRead: + this.actionService.markSeriesAsRead(this.series); + break; + case Action.MarkAsUnread: + this.actionService.markSeriesAsUnread(this.series); + break; + case Action.Delete: + await this.actionService.deleteSeries(this.series); + break; + case Action.Download: + this.downloadService.download('series', this.series); + break; + } + } } diff --git a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts index 4e41b54c6..78b9a4bed 100644 --- a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts +++ b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts @@ -11,7 +11,7 @@ import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/acti import { BulkSelectionService } from '../bulk-selection.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {AsyncPipe, CommonModule} from "@angular/common"; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; diff --git a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts index 9dcc458ee..bfa283e49 100644 --- a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts +++ b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts @@ -45,7 +45,7 @@ import {BytesPipe} from "../../_pipes/bytes.pipe"; import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component"; import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component"; import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; import {EditChapterProgressComponent} from "../edit-chapter-progress/edit-chapter-progress.component"; @@ -144,7 +144,7 @@ export class CardDetailDrawerComponent implements OnInit { this.chapterActions = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this)) .filter(item => item.action !== Action.Edit); - this.chapterActions.push({title: 'read', action: Action.Read, callback: this.handleChapterActionCallback.bind(this), requiresAdmin: false, children: []}); + this.chapterActions.push({title: 'read', description: 'read-tooltip', action: Action.Read, callback: this.handleChapterActionCallback.bind(this), requiresAdmin: false, children: []}); if (this.isChapter) { const chapter = this.utilityService.asChapter(this.data); this.chapterActions = this.actionFactoryService.filterSendToAction(this.chapterActions, chapter); diff --git a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.ts b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.ts index 600283a30..d6b719d73 100644 --- a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.ts +++ b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.ts @@ -33,7 +33,7 @@ import {LoadingComponent} from "../../shared/loading/loading.component"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {MetadataFilterComponent} from "../../metadata-filter/metadata-filter.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; import {SeriesFilterV2} from "../../_models/metadata/v2/series-filter-v2"; diff --git a/UI/Web/src/app/cards/card-item/card-item.component.ts b/UI/Web/src/app/cards/card-item/card-item.component.ts index c2e8dd46a..6a9f246b4 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.ts +++ b/UI/Web/src/app/cards/card-item/card-item.component.ts @@ -39,7 +39,7 @@ import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe"; import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe"; import {DecimalPipe, NgTemplateOutlet} from "@angular/common"; import {RouterLink, RouterLinkActive} from "@angular/router"; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; diff --git a/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.ts b/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.ts index 1221460f0..9f829352f 100644 --- a/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.ts +++ b/UI/Web/src/app/cards/chapter-metadata-detail/chapter-metadata-detail.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import {CommonModule} from "@angular/common"; import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component"; import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {Chapter} from "../../_models/chapter"; @Component({ diff --git a/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.ts b/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.ts index d0a20798d..d0f6806c1 100644 --- a/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.ts +++ b/UI/Web/src/app/cards/cover-image-chooser/cover-image-chooser.component.ts @@ -19,7 +19,7 @@ import { KEY_CODES } from 'src/app/shared/_services/utility.service'; import { UploadService } from 'src/app/_services/upload.service'; import {CommonModule, DOCUMENT} from '@angular/common'; import {ImageComponent} from "../../shared/image/image.component"; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; @Component({ selector: 'app-cover-image-chooser', diff --git a/UI/Web/src/app/cards/download-indicator/download-indicator.component.ts b/UI/Web/src/app/cards/download-indicator/download-indicator.component.ts index aeb541a67..c54ab90bb 100644 --- a/UI/Web/src/app/cards/download-indicator/download-indicator.component.ts +++ b/UI/Web/src/app/cards/download-indicator/download-indicator.component.ts @@ -4,7 +4,7 @@ import { Download } from 'src/app/shared/_models/download'; import { DownloadEvent } from 'src/app/shared/_services/download.service'; import {CommonModule} from "@angular/common"; import {CircularLoaderComponent} from "../../shared/circular-loader/circular-loader.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-download-indicator', diff --git a/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.ts b/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.ts index 8e497ef4e..0f0ee3906 100644 --- a/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.ts +++ b/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.ts @@ -5,7 +5,7 @@ import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {FullProgress} from "../../_models/readers/full-progress"; import {ReaderService} from "../../_services/reader.service"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe"; diff --git a/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts b/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts index 99bcd46b2..d14541415 100644 --- a/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts +++ b/UI/Web/src/app/cards/edit-series-relation/edit-series-relation.component.ts @@ -22,7 +22,7 @@ import { SeriesService } from 'src/app/_services/series.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {TypeaheadComponent} from "../../typeahead/_components/typeahead.component"; import {CommonModule} from "@angular/common"; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; import {RelationshipPipe} from "../../_pipes/relationship.pipe"; import {WikiLink} from "../../_models/wiki"; diff --git a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts index ce0448fc9..4541fa021 100644 --- a/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts +++ b/UI/Web/src/app/cards/entity-info-cards/entity-info-cards.component.ts @@ -23,8 +23,8 @@ import {CompactNumberPipe} from "../../_pipes/compact-number.pipe"; import {AgeRatingPipe} from "../../_pipes/age-rating.pipe"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component"; -import {TranslocoModule} from "@ngneat/transloco"; -import {TranslocoLocaleModule} from "@ngneat/transloco-locale"; +import {TranslocoModule} from "@jsverse/transloco"; +import {TranslocoLocaleModule} from "@jsverse/transloco-locale"; import {FilterField} from "../../_models/metadata/v2/filter-field"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {ImageComponent} from "../../shared/image/image.component"; diff --git a/UI/Web/src/app/cards/entity-title/entity-title.component.ts b/UI/Web/src/app/cards/entity-title/entity-title.component.ts index 82e60ed11..669260611 100644 --- a/UI/Web/src/app/cards/entity-title/entity-title.component.ts +++ b/UI/Web/src/app/cards/entity-title/entity-title.component.ts @@ -4,7 +4,7 @@ import { Chapter, LooseLeafOrDefaultNumber } from 'src/app/_models/chapter'; import { LibraryType } from 'src/app/_models/library/library'; import { Volume } from 'src/app/_models/volume'; import {CommonModule, NgSwitch} from "@angular/common"; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; /** * This is primarily used for list item diff --git a/UI/Web/src/app/cards/external-series-card/external-series-card.component.ts b/UI/Web/src/app/cards/external-series-card/external-series-card.component.ts index d979d8528..10b99c238 100644 --- a/UI/Web/src/app/cards/external-series-card/external-series-card.component.ts +++ b/UI/Web/src/app/cards/external-series-card/external-series-card.component.ts @@ -11,7 +11,7 @@ import {RouterLinkActive} from "@angular/router"; import {ImageComponent} from "../../shared/image/image.component"; import {NgbOffcanvas, NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {ReactiveFormsModule} from "@angular/forms"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component"; import {ProviderImagePipe} from "../../_pipes/provider-image.pipe"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; diff --git a/UI/Web/src/app/cards/list-item/list-item.component.ts b/UI/Web/src/app/cards/list-item/list-item.component.ts index b2dd10473..1b6731b09 100644 --- a/UI/Web/src/app/cards/list-item/list-item.component.ts +++ b/UI/Web/src/app/cards/list-item/list-item.component.ts @@ -25,7 +25,7 @@ import {ImageComponent} from "../../shared/image/image.component"; import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component"; import {EntityInfoCardsComponent} from "../entity-info-cards/entity-info-cards.component"; import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; @Component({ diff --git a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts index 2db715860..7dca289e8 100644 --- a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts +++ b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts @@ -4,7 +4,7 @@ import {ImageComponent} from "../../shared/image/image.component"; import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter"; import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; @Component({ selector: 'app-next-expected-card', diff --git a/UI/Web/src/app/cards/series-card/series-card.component.ts b/UI/Web/src/app/cards/series-card/series-card.component.ts index ad22e971c..50d8dc401 100644 --- a/UI/Web/src/app/cards/series-card/series-card.component.ts +++ b/UI/Web/src/app/cards/series-card/series-card.component.ts @@ -22,7 +22,7 @@ import {CommonModule} from "@angular/common"; import {CardItemComponent} from "../card-item/card-item.component"; import {RelationshipPipe} from "../../_pipes/relationship.pipe"; import {Device} from "../../_models/device/device"; -import {translate, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoService} from "@jsverse/transloco"; import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component"; function deepClone(obj: any): any { @@ -122,6 +122,7 @@ export class SeriesCardComponent implements OnInit, OnChanges { othersAction.children.push({ action: Action.RemoveFromOnDeck, title: 'remove-from-on-deck', + description: '', callback: (action: ActionItem, series: Series) => this.handleSeriesActionCallback(action, series), class: 'danger', requiresAdmin: false, @@ -148,6 +149,9 @@ export class SeriesCardComponent implements OnInit, OnChanges { case(Action.RefreshMetadata): this.refreshMetadata(series); break; + case(Action.GenerateColorScape): + this.refreshMetadata(series, false); + break; case(Action.Delete): this.deleteSeries(series); break; @@ -199,8 +203,8 @@ export class SeriesCardComponent implements OnInit, OnChanges { }); } - async refreshMetadata(series: Series) { - await this.actionService.refreshMetdata(series); + async refreshMetadata(series: Series, forceUpdate = false) { + await this.actionService.refreshSeriesMetadata(series, undefined, forceUpdate); } async scanLibrary(series: Series) { diff --git a/UI/Web/src/app/cards/series-info-cards/series-info-cards.component.ts b/UI/Web/src/app/cards/series-info-cards/series-info-cards.component.ts index 900a7704e..8795019bf 100644 --- a/UI/Web/src/app/cards/series-info-cards/series-info-cards.component.ts +++ b/UI/Web/src/app/cards/series-info-cards/series-info-cards.component.ts @@ -34,7 +34,7 @@ import {TimeAgoPipe} from "../../_pipes/time-ago.pipe"; import {CompactNumberPipe} from "../../_pipes/compact-number.pipe"; import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-series-info-cards', diff --git a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts index 5b8417f37..63c44ac4a 100644 --- a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts +++ b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Ev import { Swiper, SwiperEvents } from 'swiper/types'; import { SwiperModule } from 'swiper/angular'; import { NgClass, NgTemplateOutlet } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-carousel-reel', diff --git a/UI/Web/src/app/chapter-detail/chapter-detail.component.html b/UI/Web/src/app/chapter-detail/chapter-detail.component.html new file mode 100644 index 000000000..e9f0997cb --- /dev/null +++ b/UI/Web/src/app/chapter-detail/chapter-detail.component.html @@ -0,0 +1,71 @@ + + + +
    + + @if (chapter) { +
    +
    + + + + + + + + + + +
    +
    +

    + {{series?.name}} +

    +
    + Issue #{{chapter.minNumber}} + @if (chapter.titleName) { + {{chapter.titleName}} + } +
    + +
    + @if (chapter.publishers.length > 0) { + {{chapter.publishers[0]}} + } + @if (chapter.ageRating !== AgeRating.Unknown) { + + {{chapter.ageRating | ageRating}} + + } @else { + + + + } + + + {{chapter.avgHoursToRead | timeDuration }} + + • {{chapter.pages}} pages +
    + + + @if (libraryType !== null && series) { +
    + + +
    + } + + +
    +
    + } + + + + +
    diff --git a/UI/Web/src/app/chapter-detail/chapter-detail.component.scss b/UI/Web/src/app/chapter-detail/chapter-detail.component.scss new file mode 100644 index 000000000..f1d2c0357 --- /dev/null +++ b/UI/Web/src/app/chapter-detail/chapter-detail.component.scss @@ -0,0 +1,8 @@ + +.title { + color: white; +} + +.subtitle { + color: lightgrey; +} diff --git a/UI/Web/src/app/chapter-detail/chapter-detail.component.ts b/UI/Web/src/app/chapter-detail/chapter-detail.component.ts new file mode 100644 index 000000000..e420d6d11 --- /dev/null +++ b/UI/Web/src/app/chapter-detail/chapter-detail.component.ts @@ -0,0 +1,170 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + inject, + OnInit, + 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 {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"; +import {ExternalListItemComponent} from "../cards/external-list-item/external-list-item.component"; +import {ExternalSeriesCardComponent} from "../cards/external-series-card/external-series-card.component"; +import {ImageComponent} from "../shared/image/image.component"; +import {LoadingComponent} from "../shared/loading/loading.component"; +import { + NgbDropdown, + NgbDropdownItem, + NgbDropdownMenu, + NgbDropdownToggle, + NgbNav, + NgbNavContent, + NgbNavLink, + NgbProgressbar, + NgbTooltip +} from "@ng-bootstrap/ng-bootstrap"; +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} from "@angular/router"; +import {ImageService} from "../_services/image.service"; +import {ChapterService} from "../_services/chapter.service"; +import {Chapter} from "../_models/chapter"; +import {forkJoin} from "rxjs"; +import {SeriesService} from "../_services/series.service"; +import {Series} from "../_models/series"; +import {AgeRating} from "../_models/metadata/age-rating"; +import {AgeRatingPipe} from "../_pipes/age-rating.pipe"; +import {HourEstimateRange} from "../_models/series-detail/hour-estimate-range"; +import {TimeDurationPipe} from "../_pipes/time-duration.pipe"; +import {ExternalRatingComponent} from "../series-detail/_components/external-rating/external-rating.component"; +import {LibraryType} from "../_models/library/library"; +import {LibraryService} from "../_services/library.service"; +import {ThemeService} from "../_services/theme.service"; + +@Component({ + selector: 'app-chapter-detail', + standalone: true, + imports: [ + BulkOperationsComponent, + AsyncPipe, + CardActionablesComponent, + CarouselReelComponent, + DecimalPipe, + ExternalListItemComponent, + ExternalSeriesCardComponent, + ImageComponent, + LoadingComponent, + NgbDropdown, + NgbDropdownItem, + NgbDropdownMenu, + NgbDropdownToggle, + NgbNav, + NgbNavContent, + NgbNavLink, + NgbProgressbar, + NgbTooltip, + PersonBadgeComponent, + ReviewCardComponent, + SeriesCardComponent, + SeriesMetadataDetailComponent, + TagBadgeComponent, + VirtualScrollerModule, + NgStyle, + AgeRatingPipe, + TimeDurationPipe, + ExternalRatingComponent + ], + templateUrl: './chapter-detail.component.html', + styleUrl: './chapter-detail.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ChapterDetailComponent implements OnInit { + + private readonly document = inject(DOCUMENT); + private readonly route = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly cdRef = inject(ChangeDetectorRef); + private readonly imageService = inject(ImageService); + private readonly chapterService = inject(ChapterService); + private readonly seriesService = inject(SeriesService); + protected readonly libraryService = inject(LibraryService); + protected readonly themeService = inject(ThemeService); + + + protected readonly TagBadgeCursor = TagBadgeCursor; + protected readonly PageLayoutMode = PageLayoutMode; + protected readonly AgeRating = AgeRating; + + @ViewChild('scrollingBlock') scrollingBlock: ElementRef | undefined; + @ViewChild('companionBar') companionBar: ElementRef | undefined; + + isLoading: boolean = true; + coverImage: string = ''; + chapterId: number = 0; + seriesId: number = 0; + chapter: Chapter | null = null; + series: Series | null = null; + libraryType: LibraryType | null = null; + + + + get ScrollingBlockHeight() { + if (this.scrollingBlock === undefined) return 'calc(var(--vh)*100)'; + const navbar = this.document.querySelector('.navbar') as HTMLElement; + if (navbar === null) return 'calc(var(--vh)*100)'; + + const companionHeight = this.companionBar?.nativeElement.offsetHeight || 0; + const navbarHeight = navbar.offsetHeight; + const totalHeight = companionHeight + navbarHeight + 21; //21px to account for padding + return 'calc(var(--vh)*100 - ' + totalHeight + 'px)'; + } + + ngOnInit() { + const seriesId = this.route.snapshot.paramMap.get('seriesId'); + const libraryId = this.route.snapshot.paramMap.get('libraryId'); + const chapterId = this.route.snapshot.paramMap.get('chapterId'); + if (seriesId === null || libraryId === null || chapterId === null) { + this.router.navigateByUrl('/home'); + return; + } + + this.seriesId = parseInt(seriesId, 10); + this.chapterId = parseInt(chapterId, 10); + + forkJoin({ + series: this.seriesService.getSeries(this.seriesId), + chapter: this.chapterService.getChapterMetadata(this.chapterId), + libraryType: this.libraryService.getLibraryType(parseInt(libraryId, 10)) + }).subscribe(results => { + this.series = results.series; + this.chapter = results.chapter; + this.libraryType = results.libraryType; + + + this.themeService.setColorScape(this.chapter.primaryColor, this.chapter.secondaryColor); + + this.isLoading = false; + this.cdRef.markForCheck(); + }); + + this.chapterService.getChapterMetadata(this.chapterId).subscribe(metadata => { + this.chapter = metadata; + this.isLoading = false; + console.log('chapter metadata: ', this.chapter); + this.cdRef.markForCheck(); + }); + + this.coverImage = this.imageService.getChapterCoverImage(this.chapterId); + this.cdRef.markForCheck(); + } +} diff --git a/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts b/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts index 54b52596d..4df8fa91b 100644 --- a/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts +++ b/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts @@ -29,7 +29,7 @@ import {CardDetailLayoutComponent} from '../../../cards/card-detail-layout/card- import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {ToastrService} from "ngx-toastr"; import {ScrobbleProvider} from "../../../_services/scrobbling.service"; import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; diff --git a/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts index f6a671cda..667264f8d 100644 --- a/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts +++ b/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts @@ -47,7 +47,7 @@ import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component"; import {FilterField} from "../../../_models/metadata/v2/filter-field"; import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison"; @@ -56,7 +56,7 @@ import {AccountService} from "../../../_services/account.service"; import {User} from "../../../_models/user"; import {ScrobbleProvider} from "../../../_services/scrobbling.service"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; -import {TranslocoDatePipe} from "@ngneat/transloco-locale"; +import {TranslocoDatePipe} from "@jsverse/transloco-locale"; import {DefaultDatePipe} from "../../../_pipes/default-date.pipe"; import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe"; diff --git a/UI/Web/src/app/collections/_components/collection-owner/collection-owner.component.ts b/UI/Web/src/app/collections/_components/collection-owner/collection-owner.component.ts index 3b9d21d0f..f6fecdf3d 100644 --- a/UI/Web/src/app/collections/_components/collection-owner/collection-owner.component.ts +++ b/UI/Web/src/app/collections/_components/collection-owner/collection-owner.component.ts @@ -3,7 +3,7 @@ import {ScrobbleProvider} from "../../../_services/scrobbling.service"; import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe"; import {UserCollection} from "../../../_models/collection-tag"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {AsyncPipe, JsonPipe} from "@angular/common"; import {AccountService} from "../../../_services/account.service"; import {ImageComponent} from "../../../shared/image/image.component"; diff --git a/UI/Web/src/app/collections/_components/import-mal-collection/import-mal-collection.component.ts b/UI/Web/src/app/collections/_components/import-mal-collection/import-mal-collection.component.ts index 639d2f32a..cfed70bb9 100644 --- a/UI/Web/src/app/collections/_components/import-mal-collection/import-mal-collection.component.ts +++ b/UI/Web/src/app/collections/_components/import-mal-collection/import-mal-collection.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {CollectionTagService} from "../../../_services/collection-tag.service"; import {ToastrService} from "ngx-toastr"; import {ScrobbleProvider, ScrobblingService} from "../../../_services/scrobbling.service"; diff --git a/UI/Web/src/app/dashboard/_components/dashboard.component.ts b/UI/Web/src/app/dashboard/_components/dashboard.component.ts index 645c06256..8296b92d6 100644 --- a/UI/Web/src/app/dashboard/_components/dashboard.component.ts +++ b/UI/Web/src/app/dashboard/_components/dashboard.component.ts @@ -20,7 +20,7 @@ import {AsyncPipe, NgForOf, NgTemplateOutlet} from '@angular/common'; import { SideNavCompanionBarComponent } from '../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {FilterField} from "../../_models/metadata/v2/filter-field"; import {FilterComparison} from "../../_models/metadata/v2/filter-comparison"; import {DashboardService} from "../../_services/dashboard.service"; diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index e3290351b..0961182c0 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -38,7 +38,7 @@ import {NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavOut import { SideNavCompanionBarComponent } from '../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SeriesFilterV2} from "../_models/metadata/v2/series-filter-v2"; import {FilterComparison} from "../_models/metadata/v2/filter-comparison"; import {FilterField} from "../_models/metadata/v2/filter-field"; @@ -260,10 +260,10 @@ export class LibraryDetailComponent implements OnInit { await this.actionService.scanLibrary(library); break; case(Action.RefreshMetadata): - await this.actionService.refreshMetadata(library); + await this.actionService.refreshLibraryMetadata(library); break; case(Action.GenerateColorScape): - await this.actionService.refreshMetadata(library, undefined, false); + await this.actionService.refreshLibraryMetadata(library, undefined, false); break; case(Action.Edit): this.actionService.editLibrary(library); @@ -279,7 +279,10 @@ export class LibraryDetailComponent implements OnInit { await this.actionService.scanLibrary(lib); break; case(Action.RefreshMetadata): - await this.actionService.refreshMetadata(lib); + await this.actionService.refreshLibraryMetadata(lib); + break; + case(Action.GenerateColorScape): + await this.actionService.refreshLibraryMetadata(lib, undefined, false); break; case(Action.Edit): this.actionService.editLibrary(lib); diff --git a/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.ts b/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.ts index 802d0d3fa..6e1a88432 100644 --- a/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.ts +++ b/UI/Web/src/app/manga-reader/_components/infinite-scroller/infinite-scroller.component.ts @@ -24,7 +24,7 @@ import { PAGING_DIRECTION } from '../../_models/reader-enums'; import { WebtoonImage } from '../../_models/webtoon-image'; import { ManagaReaderService } from '../../_service/managa-reader.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {InfiniteScrollModule} from "ngx-infinite-scroll"; import {ReaderSetting} from "../../_models/reader-setting"; import {SafeStylePipe} from "../../../_pipes/safe-style.pipe"; diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts index c9d10881a..64df0772d 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts @@ -68,7 +68,7 @@ import {FittingIconPipe} from '../../../_pipes/fitting-icon.pipe'; import {InfiniteScrollerComponent} from '../infinite-scroller/infinite-scroller.component'; import {SwipeDirective} from '../../../ng-swipe/ng-swipe.directive'; import {LoadingComponent} from '../../../shared/loading/loading.component'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {shareReplay} from "rxjs/operators"; diff --git a/UI/Web/src/app/metadata-filter/_components/metadata-builder/metadata-builder.component.ts b/UI/Web/src/app/metadata-filter/_components/metadata-builder/metadata-builder.component.ts index 0426fdcd6..f446bd54a 100644 --- a/UI/Web/src/app/metadata-filter/_components/metadata-builder/metadata-builder.component.ts +++ b/UI/Web/src/app/metadata-filter/_components/metadata-builder/metadata-builder.component.ts @@ -22,7 +22,7 @@ import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities import {allFields} from "../../../_models/metadata/v2/filter-field"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {distinctUntilChanged, tap} from "rxjs/operators"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-metadata-builder', diff --git a/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts b/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts index 3969bbd89..ecc926cdb 100644 --- a/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts +++ b/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts @@ -32,7 +32,7 @@ import { NgbInputDatepicker, NgbTooltip } from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; enum PredicateType { Text = 1, diff --git a/UI/Web/src/app/metadata-filter/metadata-filter.component.ts b/UI/Web/src/app/metadata-filter/metadata-filter.component.ts index bff38701e..a6469a0a3 100644 --- a/UI/Web/src/app/metadata-filter/metadata-filter.component.ts +++ b/UI/Web/src/app/metadata-filter/metadata-filter.component.ts @@ -22,7 +22,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {TypeaheadComponent} from '../typeahead/_components/typeahead.component'; import {DrawerComponent} from '../shared/drawer/drawer.component'; import {AsyncPipe, NgClass, NgForOf, NgIf, NgTemplateOutlet} from '@angular/common'; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; import {SortFieldPipe} from "../_pipes/sort-field.pipe"; import {MetadataBuilderComponent} from "./_components/metadata-builder/metadata-builder.component"; import {allFields} from "../_models/metadata/v2/filter-field"; diff --git a/UI/Web/src/app/nav/_components/events-widget/events-widget.component.ts b/UI/Web/src/app/nav/_components/events-widget/events-widget.component.ts index 536138e19..4235dc407 100644 --- a/UI/Web/src/app/nav/_components/events-widget/events-widget.component.ts +++ b/UI/Web/src/app/nav/_components/events-widget/events-widget.component.ts @@ -25,7 +25,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { SentenceCasePipe } from '../../../_pipes/sentence-case.pipe'; import { CircularLoaderComponent } from '../../../shared/circular-loader/circular-loader.component'; import { NgClass, NgStyle, AsyncPipe } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-nav-events-toggle', diff --git a/UI/Web/src/app/nav/_components/grouped-typeahead/grouped-typeahead.component.ts b/UI/Web/src/app/nav/_components/grouped-typeahead/grouped-typeahead.component.ts index 1ed50d18e..ee3e21810 100644 --- a/UI/Web/src/app/nav/_components/grouped-typeahead/grouped-typeahead.component.ts +++ b/UI/Web/src/app/nav/_components/grouped-typeahead/grouped-typeahead.component.ts @@ -19,7 +19,7 @@ import { KEY_CODES } from 'src/app/shared/_services/utility.service'; import { SearchResultGroup } from 'src/app/_models/search/search-result-group'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {AsyncPipe, NgClass, NgTemplateOutlet} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {LoadingComponent} from "../../../shared/loading/loading.component"; import {map, startWith, tap} from "rxjs"; import {AccountService} from "../../../_services/account.service"; diff --git a/UI/Web/src/app/nav/_components/nav-header/nav-header.component.html b/UI/Web/src/app/nav/_components/nav-header/nav-header.component.html index a1db1c436..621edac8f 100644 --- a/UI/Web/src/app/nav/_components/nav-header/nav-header.component.html +++ b/UI/Web/src/app/nav/_components/nav-header/nav-header.component.html @@ -159,40 +159,50 @@ } + @if (!searchFocused) { - @if (backToTopNeeded) { -
    - -
    + @if((breakpoint$ | async)! > Breakpoint.Tablet) { + @if (backToTopNeeded) { +
    + +
    + } } @if (accountService.currentUser$ | async; as user) { - - - @if (series) { @@ -204,7 +195,10 @@ @if (showVolumeTab) {
  • - {{UseBookLogic ? t('books-tab') : t('volumes-tab')}} + + {{UseBookLogic ? t('books-tab') : t('volumes-tab')}} + {{volumes.length}} + @@ -231,7 +225,10 @@ @if (showChapterTab) {
  • - {{utilityService.formatChapterName(libraryType) + 's'}} + + {{utilityService.formatChapterName(libraryType) + 's'}} + {{chapters.length}} + @switch (renderMode) { @@ -256,7 +253,10 @@ @if (hasSpecials) {
  • - {{t('specials-tab')}} + + {{t('specials-tab')}} + {{specials.length}} + @switch (renderMode) { @@ -280,7 +280,10 @@ @if (hasRelations) {
  • - {{t('related-tab')}} + + {{t('related-tab')}} + {{relations.length}} +
    @@ -313,7 +316,7 @@ @case (PageLayoutMode.List) { @for(item of scroll.viewPortItems; let idx = $index; track idx) { @if (!item.hasOwnProperty('coverUrl')) { - + {{item.name}} @@ -337,6 +340,111 @@
  • } +
  • + {{t('reviews-tab')}} + + +
    + + + + + +
    + +
    + + + + + +
    + +
    +
  • + + @if (seriesMetadata) { +
  • + {{t('cast-tab')}} + +
    + + + + + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    + + + + + +
    + +
    +
  • + }
    @@ -344,6 +452,7 @@
    + @if (nextExpectedChapter) { @switch (tabId) { @@ -368,26 +477,22 @@ - @if (!item.isSpecial) { - - - } + + - @if (item.number !== LooseLeafOrSpecialNumber) { - - - } + + @@ -401,34 +506,30 @@ - @if (!item.isSpecial) { - - - - - - } + + + + + - @if (item.number !== LooseLeafOrSpecialNumber) { - - - - - - } + + + + + + - - + = []; + plusReviews: Array = []; ratings: Array = []; libraryType: LibraryType = LibraryType.Manga; seriesMetadata: SeriesMetadata | null = null; @@ -494,7 +498,10 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked { this.actionService.scanSeries(series); break; case(Action.RefreshMetadata): - this.actionService.refreshMetdata(series); + this.actionService.refreshSeriesMetadata(series); + break; + case(Action.GenerateColorScape): + this.actionService.refreshSeriesMetadata(series, undefined, false); break; case(Action.Delete): this.deleteSeries(series); @@ -818,7 +825,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked { } // Reviews - this.reviews = [...data.reviews]; + //this.reviews = [...data.reviews]; + this.plusReviews = data.reviews; + if (data.ratings) { this.ratings = [...data.ratings]; } diff --git a/UI/Web/src/app/series-detail/_components/series-metadata-detail/series-metadata-detail.component.ts b/UI/Web/src/app/series-detail/_components/series-metadata-detail/series-metadata-detail.component.ts index ee7a1de2d..316c33c35 100644 --- a/UI/Web/src/app/series-detail/_components/series-metadata-detail/series-metadata-detail.component.ts +++ b/UI/Web/src/app/series-detail/_components/series-metadata-detail/series-metadata-detail.component.ts @@ -28,7 +28,7 @@ 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 "@ngneat/transloco"; +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"; diff --git a/UI/Web/src/app/settings/_components/setting-button/setting-button.component.ts b/UI/Web/src/app/settings/_components/setting-button/setting-button.component.ts index 880e60b10..526dcf10d 100644 --- a/UI/Web/src/app/settings/_components/setting-button/setting-button.component.ts +++ b/UI/Web/src/app/settings/_components/setting-button/setting-button.component.ts @@ -1,6 +1,6 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; /** * Use with btn-sm diff --git a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html index f9c596abc..0b9794397 100644 --- a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html +++ b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html @@ -29,7 +29,9 @@ @if (isEditMode) { } @else { - + + + } diff --git a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.ts b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.ts index b66af7228..7a50d98e6 100644 --- a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.ts +++ b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.ts @@ -7,7 +7,7 @@ import { Input, Output, TemplateRef } from '@angular/core'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {NgTemplateOutlet} from "@angular/common"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {filter, fromEvent, tap} from "rxjs"; diff --git a/UI/Web/src/app/settings/_components/setting-switch/setting-switch.component.ts b/UI/Web/src/app/settings/_components/setting-switch/setting-switch.component.ts index 80b0418b4..6e3cf4d8c 100644 --- a/UI/Web/src/app/settings/_components/setting-switch/setting-switch.component.ts +++ b/UI/Web/src/app/settings/_components/setting-switch/setting-switch.component.ts @@ -7,7 +7,7 @@ import { TemplateRef } from '@angular/core'; import {NgTemplateOutlet} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; @Component({ diff --git a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.ts b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.ts index 4757732dd..3b6f03242 100644 --- a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.ts +++ b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.ts @@ -8,7 +8,7 @@ import { Output, TemplateRef } from '@angular/core'; import {NgTemplateOutlet} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-setting-title', diff --git a/UI/Web/src/app/settings/_components/settings/settings.component.ts b/UI/Web/src/app/settings/_components/settings/settings.component.ts index 7ba448274..20866b68e 100644 --- a/UI/Web/src/app/settings/_components/settings/settings.component.ts +++ b/UI/Web/src/app/settings/_components/settings/settings.component.ts @@ -18,7 +18,7 @@ import { SideNavCompanionBarComponent } from "../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component"; import {ThemeManagerComponent} from "../../../user-settings/theme-manager/theme-manager.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ScrobblingHoldsComponent} from "../../../user-settings/user-holds/scrobbling-holds.component"; import { UserScrobbleHistoryComponent diff --git a/UI/Web/src/app/shared/_components/carousel-modal/preview-image-modal.component.ts b/UI/Web/src/app/shared/_components/carousel-modal/preview-image-modal.component.ts index 663f94f6d..cab610986 100644 --- a/UI/Web/src/app/shared/_components/carousel-modal/preview-image-modal.component.ts +++ b/UI/Web/src/app/shared/_components/carousel-modal/preview-image-modal.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {CarouselReelComponent} from "../../../carousel/_components/carousel-reel/carousel-reel.component"; import {ImageComponent} from "../../image/image.component"; import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap"; diff --git a/UI/Web/src/app/shared/_components/promoted-icon/promoted-icon.component.ts b/UI/Web/src/app/shared/_components/promoted-icon/promoted-icon.component.ts index 747d5445d..11e32281b 100644 --- a/UI/Web/src/app/shared/_components/promoted-icon/promoted-icon.component.ts +++ b/UI/Web/src/app/shared/_components/promoted-icon/promoted-icon.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-promoted-icon', diff --git a/UI/Web/src/app/shared/_services/download.service.ts b/UI/Web/src/app/shared/_services/download.service.ts index 352f3c431..90747c1d7 100644 --- a/UI/Web/src/app/shared/_services/download.service.ts +++ b/UI/Web/src/app/shared/_services/download.service.ts @@ -19,7 +19,7 @@ import { PageBookmark } from 'src/app/_models/readers/page-bookmark'; import {switchMap, take, takeWhile, throttleTime} from 'rxjs/operators'; import { AccountService } from 'src/app/_services/account.service'; import { BytesPipe } from 'src/app/_pipes/bytes.pipe'; -import {translate} from "@ngneat/transloco"; +import {translate} from "@jsverse/transloco"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {SAVER, Saver} from "../../_providers/saver.provider"; import {UtilityService} from "./utility.service"; diff --git a/UI/Web/src/app/shared/_services/utility.service.ts b/UI/Web/src/app/shared/_services/utility.service.ts index 04a23a330..b3c8a7434 100644 --- a/UI/Web/src/app/shared/_services/utility.service.ts +++ b/UI/Web/src/app/shared/_services/utility.service.ts @@ -6,7 +6,7 @@ import { MangaFormat } from 'src/app/_models/manga-format'; import { PaginatedResult } from 'src/app/_models/pagination'; import { Series } from 'src/app/_models/series'; import { Volume } from 'src/app/_models/volume'; -import {TranslocoService} from "@ngneat/transloco"; +import {TranslocoService} from "@jsverse/transloco"; export enum KEY_CODES { RIGHT_ARROW = 'ArrowRight', diff --git a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts index db56231f2..1beb8a2ea 100644 --- a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts +++ b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts @@ -9,7 +9,7 @@ import { TemplateRef } from '@angular/core'; import {CommonModule} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-badge-expander', diff --git a/UI/Web/src/app/shared/confirm-dialog/confirm-dialog.component.ts b/UI/Web/src/app/shared/confirm-dialog/confirm-dialog.component.ts index 46f3375c8..3b2d36479 100644 --- a/UI/Web/src/app/shared/confirm-dialog/confirm-dialog.component.ts +++ b/UI/Web/src/app/shared/confirm-dialog/confirm-dialog.component.ts @@ -4,7 +4,7 @@ import { ConfirmButton } from './_models/confirm-button'; import { ConfirmConfig } from './_models/confirm-config'; import {CommonModule} from "@angular/common"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-confirm-dialog', diff --git a/UI/Web/src/app/shared/drawer/drawer.component.ts b/UI/Web/src/app/shared/drawer/drawer.component.ts index 48b2624ae..9fd915ab6 100644 --- a/UI/Web/src/app/shared/drawer/drawer.component.ts +++ b/UI/Web/src/app/shared/drawer/drawer.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import {CommonModule} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; export class DrawerOptions { /** diff --git a/UI/Web/src/app/shared/edit-list/edit-list.component.ts b/UI/Web/src/app/shared/edit-list/edit-list.component.ts index 4d5ce1c7d..2bfa4eec5 100644 --- a/UI/Web/src/app/shared/edit-list/edit-list.component.ts +++ b/UI/Web/src/app/shared/edit-list/edit-list.component.ts @@ -11,7 +11,7 @@ import { import {CommonModule} from '@angular/common'; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {Select2Module} from "ng-select2-component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {debounceTime, distinctUntilChanged, tap} from "rxjs/operators"; diff --git a/UI/Web/src/app/shared/loading/loading.component.ts b/UI/Web/src/app/shared/loading/loading.component.ts index d45607e7e..3c472bb56 100644 --- a/UI/Web/src/app/shared/loading/loading.component.ts +++ b/UI/Web/src/app/shared/loading/loading.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import {CommonModule} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-loading', diff --git a/UI/Web/src/app/shared/read-more/read-more.component.ts b/UI/Web/src/app/shared/read-more/read-more.component.ts index 7045989e0..82a0cc837 100644 --- a/UI/Web/src/app/shared/read-more/read-more.component.ts +++ b/UI/Web/src/app/shared/read-more/read-more.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges } from '@angular/core'; import {CommonModule} from "@angular/common"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-read-more', diff --git a/UI/Web/src/app/shared/update-notification/update-notification-modal.component.ts b/UI/Web/src/app/shared/update-notification/update-notification-modal.component.ts index 9b77f4dbc..3504e55e8 100644 --- a/UI/Web/src/app/shared/update-notification/update-notification-modal.component.ts +++ b/UI/Web/src/app/shared/update-notification/update-notification-modal.component.ts @@ -3,7 +3,7 @@ import {NgbActiveModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap'; import { UpdateVersionEvent } from 'src/app/_models/events/update-version-event'; import {CommonModule} from "@angular/common"; import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {WikiLink} from "../../_models/wiki"; diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html deleted file mode 100644 index 95076c6fd..000000000 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss deleted file mode 100644 index 384ca5114..000000000 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.modal-body { - overflow: hidden; - - .tab-content { - max-height: calc(var(--vh) * 100 - 235px); - overflow: auto; - } -} diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.ts b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.ts deleted file mode 100644 index 97e16c100..000000000 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; -import {NgbActiveModal, NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavOutlet} from "@ng-bootstrap/ng-bootstrap"; -import { - DraggableOrderedListComponent, -} from "../../../reading-list/_components/draggable-ordered-list/draggable-ordered-list.component"; -import { - ReadingListItemComponent -} from "../../../reading-list/_components/reading-list-item/reading-list-item.component"; -import {DashboardStreamListItemComponent} from "../dashboard-stream-list-item/dashboard-stream-list-item.component"; -import {Breakpoint, UtilityService} from "../../../shared/_services/utility.service"; -import {CustomizeDashboardStreamsComponent} from "../customize-dashboard-streams/customize-dashboard-streams.component"; -import {CustomizeSidenavStreamsComponent} from "../customize-sidenav-streams/customize-sidenav-streams.component"; -import {ManageExternalSourcesComponent} from "../manage-external-sources/manage-external-sources.component"; -import {ManageSmartFiltersComponent} from "../manage-smart-filters/manage-smart-filters.component"; -import {WikiLink} from "../../../_models/wiki"; - -enum TabID { - Dashboard = 'dashboard', - SideNav = 'sidenav', - SmartFilters = 'smart-filters', - ExternalSources = 'external-sources' -} - -@Component({ - selector: 'app-customize-dashboard-modal', - standalone: true, - imports: [CommonModule, SafeHtmlPipe, TranslocoDirective, DraggableOrderedListComponent, ReadingListItemComponent, DashboardStreamListItemComponent, - NgbNav, NgbNavContent, NgbNavLink, NgbNavItem, NgbNavOutlet, CustomizeDashboardStreamsComponent, CustomizeSidenavStreamsComponent, - ManageExternalSourcesComponent, ManageSmartFiltersComponent], - templateUrl: './customize-dashboard-modal.component.html', - styleUrls: ['./customize-dashboard-modal.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class CustomizeDashboardModalComponent { - - private readonly cdRef = inject(ChangeDetectorRef); - public readonly utilityService = inject(UtilityService); - private readonly modal = inject(NgbActiveModal); - - protected readonly TabID = TabID; - protected readonly Breakpoint = Breakpoint; - protected readonly WikiLink = WikiLink; - - activeTab = TabID.SideNav; - - close() { - this.modal.close(); - } -} diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.ts b/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.ts index 45fae2a03..1088897f6 100644 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.ts +++ b/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.ts @@ -10,7 +10,7 @@ import {DashboardService} from "../../../_services/dashboard.service"; import {FilterService} from "../../../_services/filter.service"; import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap"; import {forkJoin} from "rxjs"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {FilterPipe} from "../../../_pipes/filter.pipe"; import {Breakpoint, UtilityService} from "../../../shared/_services/utility.service"; diff --git a/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.ts b/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.ts index 141d74267..1835fe931 100644 --- a/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.ts +++ b/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.ts @@ -19,7 +19,7 @@ import { import {SideNavStream} from "../../../_models/sidenav/sidenav-stream"; import {NavService} from "../../../_services/nav.service"; import {DashboardStreamListItemComponent} from "../dashboard-stream-list-item/dashboard-stream-list-item.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SidenavStreamListItemComponent} from "../sidenav-stream-list-item/sidenav-stream-list-item.component"; import {ExternalSourceService} from "../../../_services/external-source.service"; import {ExternalSource} from "../../../_models/sidenav/external-source"; diff --git a/UI/Web/src/app/sidenav/_components/dashboard-stream-list-item/dashboard-stream-list-item.component.ts b/UI/Web/src/app/sidenav/_components/dashboard-stream-list-item/dashboard-stream-list-item.component.ts index e1e516a16..17c1f2704 100644 --- a/UI/Web/src/app/sidenav/_components/dashboard-stream-list-item/dashboard-stream-list-item.component.ts +++ b/UI/Web/src/app/sidenav/_components/dashboard-stream-list-item/dashboard-stream-list-item.component.ts @@ -10,7 +10,7 @@ import {ImageComponent} from "../../../shared/image/image.component"; import {MangaFormatIconPipe} from "../../../_pipes/manga-format-icon.pipe"; import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe"; import {NgbProgressbar} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {DashboardStream} from "../../../_models/dashboard/dashboard-stream"; import {StreamNamePipe} from "../../../_pipes/stream-name.pipe"; diff --git a/UI/Web/src/app/sidenav/_components/edit-external-source-item/edit-external-source-item.component.ts b/UI/Web/src/app/sidenav/_components/edit-external-source-item/edit-external-source-item.component.ts index 7074e2c38..862ddf62e 100644 --- a/UI/Web/src/app/sidenav/_components/edit-external-source-item/edit-external-source-item.component.ts +++ b/UI/Web/src/app/sidenav/_components/edit-external-source-item/edit-external-source-item.component.ts @@ -2,7 +2,7 @@ import {ChangeDetectorRef, Component, DestroyRef, EventEmitter, inject, Input, O import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {ExternalSource} from "../../../_models/sidenav/external-source"; import {NgbCollapse} from "@ng-bootstrap/ng-bootstrap"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {ExternalSourceService} from "../../../_services/external-source.service"; import {ToastrService} from "ngx-toastr"; diff --git a/UI/Web/src/app/sidenav/_components/manage-customization/manage-customization.component.ts b/UI/Web/src/app/sidenav/_components/manage-customization/manage-customization.component.ts index c55641ffe..cf6a4df26 100644 --- a/UI/Web/src/app/sidenav/_components/manage-customization/manage-customization.component.ts +++ b/UI/Web/src/app/sidenav/_components/manage-customization/manage-customization.component.ts @@ -5,7 +5,7 @@ import {CustomizeSidenavStreamsComponent} from "../customize-sidenav-streams/cus import {ManageExternalSourcesComponent} from "../manage-external-sources/manage-external-sources.component"; import {ManageSmartFiltersComponent} from "../manage-smart-filters/manage-smart-filters.component"; import {NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavOutlet} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {WikiLink} from 'src/app/_models/wiki'; enum TabID { @@ -42,5 +42,5 @@ export class ManageCustomizationComponent { protected readonly Breakpoint = Breakpoint; protected readonly WikiLink = WikiLink; - activeTab = TabID.SideNav; + activeTab = TabID.Dashboard; } diff --git a/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.ts b/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.ts index 743fecd8f..99c399714 100644 --- a/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.ts +++ b/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.ts @@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, injec import {CommonModule, NgOptimizedImage} from '@angular/common'; import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms"; import {NgbCollapse, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {AccountService} from "../../../_services/account.service"; import {ToastrService} from "ngx-toastr"; import {EditExternalSourceItemComponent} from "../edit-external-source-item/edit-external-source-item.component"; diff --git a/UI/Web/src/app/sidenav/_components/manage-smart-filters/manage-smart-filters.component.ts b/UI/Web/src/app/sidenav/_components/manage-smart-filters/manage-smart-filters.component.ts index 4a09c1a92..49fcebe65 100644 --- a/UI/Web/src/app/sidenav/_components/manage-smart-filters/manage-smart-filters.component.ts +++ b/UI/Web/src/app/sidenav/_components/manage-smart-filters/manage-smart-filters.component.ts @@ -1,7 +1,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject} from '@angular/core'; import {FilterService} from "../../../_services/filter.service"; import {SmartFilter} from "../../../_models/metadata/v2/smart-filter"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {FilterPipe} from "../../../_pipes/filter.pipe"; import {ActionService} from "../../../_services/action.service"; diff --git a/UI/Web/src/app/sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component.ts b/UI/Web/src/app/sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component.ts index 811d9f84d..3ccfca1fa 100644 --- a/UI/Web/src/app/sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component.ts +++ b/UI/Web/src/app/sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component.ts @@ -14,8 +14,7 @@ import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.ser import { NavService } from 'src/app/_services/nav.service'; import { ToggleService } from 'src/app/_services/toggle.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {CommonModule} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {tap} from "rxjs"; /** diff --git a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts index 56a569233..25798d70a 100644 --- a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts +++ b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts @@ -1,18 +1,11 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - DestroyRef, - inject, - Input, - OnInit -} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core'; import {NavigationEnd, Router, RouterLink} from '@angular/router'; import {filter, map, tap} from 'rxjs'; -import { NavService } from 'src/app/_services/nav.service'; +import {NavService} from 'src/app/_services/nav.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {AsyncPipe, NgClass, NgOptimizedImage, NgTemplateOutlet} from "@angular/common"; import {ImageComponent} from "../../../shared/image/image.component"; +import {Breakpoint, UtilityService} from "../../../shared/_services/utility.service"; @Component({ @@ -28,6 +21,7 @@ export class SideNavItemComponent implements OnInit { private readonly router = inject(Router); private readonly cdRef = inject(ChangeDetectorRef); protected readonly navService = inject(NavService); + protected readonly utilityService = inject(UtilityService); /** * Id for automatic scrolling to. @@ -141,6 +135,8 @@ export class SideNavItemComponent implements OnInit { } openLink() { + this.collapseNavIfApplicable(); + if (Object.keys(this.queryParams).length !== 0) { this.router.navigateByUrl(this.link + '?' + this.queryParams); return; @@ -152,4 +148,11 @@ export class SideNavItemComponent implements OnInit { this.router.navigateByUrl(this.link!); } + // If on mobile, automatically collapse the side nav after making a selection + collapseNavIfApplicable() { + if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) { + this.navService.collapseSideNav(true); + } + } + } diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html index fd5505291..e6315f135 100644 --- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html +++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html @@ -9,6 +9,7 @@ @if (navStreams$ | async; as streams) { @if (showAll) { + @if (streams.length > ItemLimit && (navService.sideNavCollapsed$ | async) === false) {
    diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts index 3c3beeba5..3b65b7797 100644 --- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts +++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts @@ -23,12 +23,13 @@ import {AsyncPipe, NgClass} from "@angular/common"; import {SideNavItemComponent} from "../side-nav-item/side-nav-item.component"; import {FilterPipe} from "../../../_pipes/filter.pipe"; import {FormsModule} from "@angular/forms"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component"; import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe"; import {SideNavStream} from "../../../_models/sidenav/sidenav-stream"; import {SideNavStreamType} from "../../../_models/sidenav/sidenav-stream-type.enum"; import {WikiLink} from "../../../_models/wiki"; +import {SettingsTabId} from "../../preference-nav/preference-nav.component"; @Component({ selector: 'app-side-nav', @@ -148,10 +149,10 @@ export class SideNavComponent implements OnInit { await this.actionService.scanLibrary(library); break; case(Action.RefreshMetadata): - await this.actionService.refreshMetadata(library); + await this.actionService.refreshLibraryMetadata(library); break; case(Action.GenerateColorScape): - await this.actionService.refreshMetadata(library, undefined, false); + await this.actionService.refreshLibraryMetadata(library, undefined, false); break; case (Action.AnalyzeFiles): await this.actionService.analyzeFiles(library); @@ -206,4 +207,6 @@ export class SideNavComponent implements OnInit { this.cdRef.markForCheck(); this.showAllSubject.next(false); } + + protected readonly SettingsTabId = SettingsTabId; } diff --git a/UI/Web/src/app/sidenav/_components/sidenav-stream-list-item/sidenav-stream-list-item.component.ts b/UI/Web/src/app/sidenav/_components/sidenav-stream-list-item/sidenav-stream-list-item.component.ts index 50620e9ad..569004e7f 100644 --- a/UI/Web/src/app/sidenav/_components/sidenav-stream-list-item/sidenav-stream-list-item.component.ts +++ b/UI/Web/src/app/sidenav/_components/sidenav-stream-list-item/sidenav-stream-list-item.component.ts @@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@ import {CommonModule} from '@angular/common'; import {SideNavStream} from "../../../_models/sidenav/sidenav-stream"; import {StreamNamePipe} from "../../../_pipes/stream-name.pipe"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SideNavStreamType} from "../../../_models/sidenav/sidenav-stream-type.enum"; @Component({ diff --git a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.html b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.html index ffd343bd8..692c9d8f2 100644 --- a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.html +++ b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.html @@ -111,13 +111,10 @@
  • {{t(TabID.Advanced)}} -
    -
    -
    -
    {{t('file-type-group-label')}}
    -

    - {{t('file-type-group-tooltip')}} -

    +
    + +
    @for (group of fileTypeGroups; track group) {
    @@ -126,132 +123,103 @@
    }
    -
    -
    + +
    -
    -
    -
    -
    -

    - -

    -
    -
    - - {{t('exclude-patterns-tooltip')}}{{t('help')}} -
    - -
    -
    -
    -
    -
    -
    - -
    - -
    - - - -
    -
    -
    +
    + +
    -
    -
    -

    - {{t('manage-collection-tooltip')}} -

    -
    + +
    -
    -
    -
    +
    + +
    -
    -
    -

    - {{t('manage-reading-list-tooltip')}} -

    -
    + +
    -
    -
    -
    +
    + +
    -
    -
    -

    - {{t('allow-scrobbling-tooltip')}} -

    -
    + +
    -
    -
    -
    +
    + +
    - - +
    -
    -

    - {{t('folder-watching-tooltip')}} -

    -
    + +
    -
    -
    -
    + +
    + +
    - - +
    -
    -

    - {{t('include-in-dashboard-tooltip')}} -

    -
    + +
    -
    -
    -
    + + +
    + +
    - - +
    -
    -

    - {{t('include-in-recommendation-tooltip')}} -

    -
    + +
    -
    -
    -
    -
    - - -
    -
    -

    - {{t('include-in-search-tooltip')}} -

    -
    + +
    + +
    + + + + +
    +
  • + + + @if (!isAddLibrary) { +
  • + {{t(TabID.Tasks)}} + + @for(task of tasks; track task.action) { +
    + + + +
    + } +
    +
  • + + } + +
    diff --git a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts index 63de44c41..6037954b8 100644 --- a/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts +++ b/UI/Web/src/app/sidenav/_modals/library-settings-modal/library-settings-modal.component.ts @@ -1,5 +1,4 @@ import { - AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, @@ -40,18 +39,24 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {CommonModule} from "@angular/common"; import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe"; import {CoverImageChooserComponent} from "../../../cards/cover-image-chooser/cover-image-chooser.component"; -import {translate, TranslocoModule} from "@ngneat/transloco"; +import {translate, TranslocoModule} from "@jsverse/transloco"; import {DefaultDatePipe} from "../../../_pipes/default-date.pipe"; import {allFileTypeGroup, FileTypeGroup} from "../../../_models/library/file-type-group.enum"; import {FileTypeGroupPipe} from "../../../_pipes/file-type-group.pipe"; import {EditListComponent} from "../../../shared/edit-list/edit-list.component"; import {WikiLink} from "../../../_models/wiki"; +import {SettingItemComponent} from "../../../settings/_components/setting-item/setting-item.component"; +import {SettingSwitchComponent} from "../../../settings/_components/setting-switch/setting-switch.component"; +import {SettingButtonComponent} from "../../../settings/_components/setting-button/setting-button.component"; +import {Action, ActionFactoryService, ActionItem} from "../../../_services/action-factory.service"; +import {ActionService} from "../../../_services/action.service"; enum TabID { General = 'general-tab', Folder = 'folder-tab', Cover = 'cover-tab', - Advanced = 'advanced-tab' + Advanced = 'advanced-tab', + Tasks = 'tasks-tab' } enum StepID { @@ -66,20 +71,15 @@ enum StepID { standalone: true, imports: [CommonModule, NgbModalModule, NgbNavLink, NgbNavItem, NgbNavContent, ReactiveFormsModule, NgbTooltip, SentenceCasePipe, NgbNav, NgbNavOutlet, CoverImageChooserComponent, TranslocoModule, DefaultDatePipe, - FileTypeGroupPipe, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionBody, EditListComponent], + FileTypeGroupPipe, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionBody, EditListComponent, SettingItemComponent, SettingSwitchComponent, SettingButtonComponent], templateUrl: './library-settings-modal.component.html', styleUrls: ['./library-settings-modal.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class LibrarySettingsModalComponent implements OnInit { - protected readonly LibraryType = LibraryType; - protected readonly Breakpoint = Breakpoint; - protected readonly TabID = TabID; - protected readonly WikiLink = WikiLink; - - public readonly utilityService = inject(UtilityService); - public readonly modal = inject(NgbActiveModal); + protected readonly utilityService = inject(UtilityService); + protected readonly modal = inject(NgbActiveModal); private readonly destroyRef = inject(DestroyRef); private readonly uploadService = inject(UploadService); private readonly modalService = inject(NgbModal); @@ -89,11 +89,22 @@ export class LibrarySettingsModalComponent implements OnInit { private readonly toastr = inject(ToastrService); private readonly cdRef = inject(ChangeDetectorRef); private readonly imageService = inject(ImageService); + private readonly actionFactoryService = inject(ActionFactoryService); + private readonly actionService = inject(ActionService); + + protected readonly LibraryType = LibraryType; + protected readonly Breakpoint = Breakpoint; + protected readonly TabID = TabID; + protected readonly WikiLink = WikiLink; + protected readonly Action = Action; @Input({required: true}) library!: Library | undefined; active = TabID.General; imageUrls: Array = []; + protected readonly excludePatternTooltip = `` + translate('library-settings-modal.exclude-patterns-tooltip') + + `${translate('library-settings-modal.help')}` + + ``; libraryForm: FormGroup = new FormGroup({ name: new FormControl('', { nonNullable: true, validators: [Validators.required] }), @@ -117,6 +128,8 @@ export class LibrarySettingsModalComponent implements OnInit { fileTypeGroups = allFileTypeGroup; excludePatterns: Array = ['']; + tasks: ActionItem[] = this.getTasks(); + get IsKavitaPlusEligible() { const libType = parseInt(this.libraryForm.get('type')?.value + '', 10) as LibraryType; return libType === LibraryType.Manga || libType === LibraryType.LightNovel; @@ -138,7 +151,7 @@ export class LibrarySettingsModalComponent implements OnInit { this.cdRef.markForCheck(); } - if (this.library && (this.library.type === LibraryType.Comic || this.library.type === LibraryType.Book)) { + if (this.library && !(this.library.type === LibraryType.Manga || this.library.type === LibraryType.LightNovel) ) { this.libraryForm.get('allowScrobbling')?.setValue(false); this.libraryForm.get('allowScrobbling')?.disable(); } @@ -201,6 +214,11 @@ export class LibrarySettingsModalComponent implements OnInit { } this.libraryForm.get('allowScrobbling')?.setValue(this.IsKavitaPlusEligible); + if (!this.IsKavitaPlusEligible) { + this.libraryForm.get('allowScrobbling')?.disable(); + } else { + this.libraryForm.get('allowScrobbling')?.enable(); + } this.cdRef.markForCheck(); }), takeUntilDestroyed(this.destroyRef) @@ -363,4 +381,28 @@ export class LibrarySettingsModalComponent implements OnInit { return false; // Advanced are optional } } + + getTasks() { + const blackList = [Action.Edit]; + return this.actionFactoryService.getActionablesForSettingsPage(this.actionFactoryService.getLibraryActions(this.runTask.bind(this)), blackList); + } + + async runTask(action: ActionItem) { + switch (action.action) { + case Action.Scan: + await this.actionService.scanLibrary(this.library!); + break; + case Action.RefreshMetadata: + await this.actionService.refreshLibraryMetadata(this.library!); + break; + case Action.GenerateColorScape: + await this.actionService.refreshLibraryMetadata(this.library!, undefined, false); + break; + case Action.Delete: + await this.actionService.deleteLibrary(this.library!, () => { + this.modal.dismiss(); + }); + break; + } + } } diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts index 545b52b42..451bbc5dd 100644 --- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts +++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts @@ -1,5 +1,5 @@ import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject} from '@angular/core'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {AsyncPipe, DOCUMENT, NgClass} from "@angular/common"; import {NavService} from "../../_services/nav.service"; import {AccountService, Role} from "../../_services/account.service"; diff --git a/UI/Web/src/app/statistics/_components/_modals/generic-list-modal/generic-list-modal.component.ts b/UI/Web/src/app/statistics/_components/_modals/generic-list-modal/generic-list-modal.component.ts index 8e3ebf492..a7178606f 100644 --- a/UI/Web/src/app/statistics/_components/_modals/generic-list-modal/generic-list-modal.component.ts +++ b/UI/Web/src/app/statistics/_components/_modals/generic-list-modal/generic-list-modal.component.ts @@ -2,7 +2,7 @@ import {Component, inject, Input} from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { FilterPipe } from '../../../../_pipes/filter.pipe'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-generic-list-modal', diff --git a/UI/Web/src/app/statistics/_components/_modals/generic-table-modal/generic-table-modal.component.ts b/UI/Web/src/app/statistics/_components/_modals/generic-table-modal/generic-table-modal.component.ts index cc74277f5..e230b34a3 100644 --- a/UI/Web/src/app/statistics/_components/_modals/generic-table-modal/generic-table-modal.component.ts +++ b/UI/Web/src/app/statistics/_components/_modals/generic-table-modal/generic-table-modal.component.ts @@ -1,7 +1,7 @@ import {Component, ContentChild, inject, Input, TemplateRef} from '@angular/core'; import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap"; import {NgTemplateOutlet} from "@angular/common"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-generic-table-modal', diff --git a/UI/Web/src/app/statistics/_components/day-breakdown/day-breakdown.component.ts b/UI/Web/src/app/statistics/_components/day-breakdown/day-breakdown.component.ts index b2b7cee81..1d57f89ef 100644 --- a/UI/Web/src/app/statistics/_components/day-breakdown/day-breakdown.component.ts +++ b/UI/Web/src/app/statistics/_components/day-breakdown/day-breakdown.component.ts @@ -8,7 +8,7 @@ import {StatCount} from '../../_models/stat-count'; import {DayOfWeekPipe} from '../../../_pipes/day-of-week.pipe'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {AsyncPipe, NgForOf, NgIf} from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {tap} from "rxjs/operators"; @Component({ diff --git a/UI/Web/src/app/statistics/_components/file-breakdown-stats/file-breakdown-stats.component.ts b/UI/Web/src/app/statistics/_components/file-breakdown-stats/file-breakdown-stats.component.ts index b6b6df4f6..3839e92af 100644 --- a/UI/Web/src/app/statistics/_components/file-breakdown-stats/file-breakdown-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/file-breakdown-stats/file-breakdown-stats.component.ts @@ -17,7 +17,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { MangaFormatPipe } from '../../../_pipes/manga-format.pipe'; import { BytesPipe } from '../../../_pipes/bytes.pipe'; import { NgIf, NgFor, AsyncPipe, DecimalPipe } from '@angular/common'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {Pagination} from "../../../_models/pagination"; import {DownloadService} from "../../../shared/_services/download.service"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; diff --git a/UI/Web/src/app/statistics/_components/kavitaplus-metadata-breakdown-stats/kavitaplus-metadata-breakdown-stats.component.ts b/UI/Web/src/app/statistics/_components/kavitaplus-metadata-breakdown-stats/kavitaplus-metadata-breakdown-stats.component.ts index bf3d41eac..3061497d6 100644 --- a/UI/Web/src/app/statistics/_components/kavitaplus-metadata-breakdown-stats/kavitaplus-metadata-breakdown-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/kavitaplus-metadata-breakdown-stats/kavitaplus-metadata-breakdown-stats.component.ts @@ -1,7 +1,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject} from '@angular/core'; import {StatisticsService} from "../../../_services/statistics.service"; import {KavitaPlusMetadataBreakdown} from "../../_models/kavitaplus-metadata-breakdown"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {PercentPipe} from "@angular/common"; import {NgbProgressbar, NgbProgressbarStacked, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; diff --git a/UI/Web/src/app/statistics/_components/manga-format-stats/manga-format-stats.component.ts b/UI/Web/src/app/statistics/_components/manga-format-stats/manga-format-stats.component.ts index 62760ec62..5fca79192 100644 --- a/UI/Web/src/app/statistics/_components/manga-format-stats/manga-format-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/manga-format-stats/manga-format-stats.component.ts @@ -16,7 +16,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { SortableHeader as SortableHeader_1 } from '../../../_single-module/table/_directives/sortable-header.directive'; import { NgIf, NgFor, AsyncPipe, DecimalPipe } from '@angular/common'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-manga-format-stats', diff --git a/UI/Web/src/app/statistics/_components/publication-status-stats/publication-status-stats.component.ts b/UI/Web/src/app/statistics/_components/publication-status-stats/publication-status-stats.component.ts index b33b51dea..7d6ac493e 100644 --- a/UI/Web/src/app/statistics/_components/publication-status-stats/publication-status-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/publication-status-stats/publication-status-stats.component.ts @@ -15,7 +15,7 @@ import { PieDataItem } from '../../_models/pie-data-item'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { SortableHeader as SortableHeader_1 } from '../../../_single-module/table/_directives/sortable-header.directive'; import { NgIf, NgFor, AsyncPipe, DecimalPipe } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-publication-status-stats', diff --git a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts index da508964e..404b26094 100644 --- a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts +++ b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts @@ -10,7 +10,7 @@ import { TimePeriods } from '../top-readers/top-readers.component'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { LineChartModule } from '@swimlane/ngx-charts'; import { NgIf, NgFor, AsyncPipe } from '@angular/common'; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {TranslocoDirective, TranslocoService} from "@jsverse/transloco"; const options: Intl.DateTimeFormatOptions = { month: "short", day: "numeric" }; diff --git a/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts b/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts index 0aae8472a..d6768df24 100644 --- a/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/server-stats/server-stats.component.ts @@ -23,7 +23,7 @@ import {TopReadersComponent} from '../top-readers/top-readers.component'; import {StatListComponent} from '../stat-list/stat-list.component'; import {IconAndTitleComponent} from '../../../shared/icon-and-title/icon-and-title.component'; import {AsyncPipe, DecimalPipe, NgIf} from '@angular/common'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison"; import {FilterField} from "../../../_models/metadata/v2/filter-field"; import { diff --git a/UI/Web/src/app/statistics/_components/stat-list/stat-list.component.ts b/UI/Web/src/app/statistics/_components/stat-list/stat-list.component.ts index 1857b9cd7..2e83b8d39 100644 --- a/UI/Web/src/app/statistics/_components/stat-list/stat-list.component.ts +++ b/UI/Web/src/app/statistics/_components/stat-list/stat-list.component.ts @@ -5,7 +5,7 @@ import { CompactNumberPipe } from '../../../_pipes/compact-number.pipe'; import { ImageComponent } from '../../../shared/image/image.component'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { NgClass, AsyncPipe } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-stat-list', diff --git a/UI/Web/src/app/statistics/_components/top-readers/top-readers.component.ts b/UI/Web/src/app/statistics/_components/top-readers/top-readers.component.ts index bc78e3c17..2ebc7de4e 100644 --- a/UI/Web/src/app/statistics/_components/top-readers/top-readers.component.ts +++ b/UI/Web/src/app/statistics/_components/top-readers/top-readers.component.ts @@ -12,7 +12,7 @@ import { StatisticsService } from 'src/app/_services/statistics.service'; import { TopUserRead } from '../../_models/top-reads'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { NgFor, AsyncPipe } from '@angular/common'; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; export const TimePeriods: Array<{title: string, value: number}> = [{title: 'this-week', value: new Date().getDay() || 1}, diff --git a/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts b/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts index 6d80b0a49..548aa82ad 100644 --- a/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts +++ b/UI/Web/src/app/statistics/_components/user-stats-info-cards/user-stats-info-cards.component.ts @@ -8,7 +8,7 @@ import { DecimalPipe } from '@angular/common'; import { IconAndTitleComponent } from '../../../shared/icon-and-title/icon-and-title.component'; import {AccountService} from "../../../_services/account.service"; import {CompactNumberPipe} from "../../../_pipes/compact-number.pipe"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; @Component({ selector: 'app-user-stats-info-cards', diff --git a/UI/Web/src/app/statistics/_components/user-stats/user-stats.component.ts b/UI/Web/src/app/statistics/_components/user-stats/user-stats.component.ts index 48dc85bd1..24f6c2962 100644 --- a/UI/Web/src/app/statistics/_components/user-stats/user-stats.component.ts +++ b/UI/Web/src/app/statistics/_components/user-stats/user-stats.component.ts @@ -12,7 +12,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {StatListComponent} from '../stat-list/stat-list.component'; import {ReadingActivityComponent} from '../reading-activity/reading-activity.component'; import {UserStatsInfoCardsComponent} from '../user-stats-info-cards/user-stats-info-cards.component'; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; import {DayBreakdownComponent} from "../day-breakdown/day-breakdown.component"; @Component({ diff --git a/UI/Web/src/app/typeahead/_components/typeahead.component.ts b/UI/Web/src/app/typeahead/_components/typeahead.component.ts index 07a48ba3f..0ebffe01e 100644 --- a/UI/Web/src/app/typeahead/_components/typeahead.component.ts +++ b/UI/Web/src/app/typeahead/_components/typeahead.component.ts @@ -25,7 +25,7 @@ import { KEY_CODES } from 'src/app/shared/_services/utility.service'; import { TypeaheadSettings } from '../_models/typeahead-settings'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {SelectionModel} from "../_models/selection-model"; diff --git a/UI/Web/src/app/user-settings/_modals/edit-device-modal/edit-device-modal.component.ts b/UI/Web/src/app/user-settings/_modals/edit-device-modal/edit-device-modal.component.ts index eb2a0e7fb..f35a2cde8 100644 --- a/UI/Web/src/app/user-settings/_modals/edit-device-modal/edit-device-modal.component.ts +++ b/UI/Web/src/app/user-settings/_modals/edit-device-modal/edit-device-modal.component.ts @@ -13,7 +13,7 @@ import {Device} from "../../../_models/device/device"; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {DevicePlatform, devicePlatforms} from "../../../_models/device/device-platform"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {NgbActiveModal, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {DevicePlatformPipe} from "../../../_pipes/device-platform.pipe"; import {Select2Module} from "ng-select2-component"; diff --git a/UI/Web/src/app/user-settings/api-key/api-key.component.ts b/UI/Web/src/app/user-settings/api-key/api-key.component.ts index a0d91fabf..62f0999cc 100644 --- a/UI/Web/src/app/user-settings/api-key/api-key.component.ts +++ b/UI/Web/src/app/user-settings/api-key/api-key.component.ts @@ -14,7 +14,7 @@ import {Clipboard} from '@angular/cdk/clipboard'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { NgIf } from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; @Component({ diff --git a/UI/Web/src/app/user-settings/change-age-restriction/change-age-restriction.component.ts b/UI/Web/src/app/user-settings/change-age-restriction/change-age-restriction.component.ts index 7ce790c85..e4aa3e8fb 100644 --- a/UI/Web/src/app/user-settings/change-age-restriction/change-age-restriction.component.ts +++ b/UI/Web/src/app/user-settings/change-age-restriction/change-age-restriction.component.ts @@ -17,7 +17,7 @@ import { AgeRatingPipe } from '../../_pipes/age-rating.pipe'; import { RestrictionSelectorComponent } from '../restriction-selector/restriction-selector.component'; import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; import {AsyncPipe, NgForOf, NgIf} from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SettingTitleComponent} from "../../settings/_components/setting-title/setting-title.component"; import {ReactiveFormsModule} from "@angular/forms"; import {Select2Module} from "ng-select2-component"; diff --git a/UI/Web/src/app/user-settings/change-email/change-email.component.ts b/UI/Web/src/app/user-settings/change-email/change-email.component.ts index 88cba7633..a83d4d069 100644 --- a/UI/Web/src/app/user-settings/change-email/change-email.component.ts +++ b/UI/Web/src/app/user-settings/change-email/change-email.component.ts @@ -7,7 +7,7 @@ import {AccountService} from 'src/app/_services/account.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { ApiKeyComponent } from '../api-key/api-key.component'; import { NgbTooltip, NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {ScrobbleProviderNamePipe} from "../../_pipes/scrobble-provider-name.pipe"; import {SettingTitleComponent} from "../../settings/_components/setting-title/setting-title.component"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; diff --git a/UI/Web/src/app/user-settings/change-password/change-password.component.ts b/UI/Web/src/app/user-settings/change-password/change-password.component.ts index 01aaa018d..a7dd6fab9 100644 --- a/UI/Web/src/app/user-settings/change-password/change-password.component.ts +++ b/UI/Web/src/app/user-settings/change-password/change-password.component.ts @@ -15,7 +15,7 @@ import { AccountService } from 'src/app/_services/account.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; import { AsyncPipe } from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SettingTitleComponent} from "../../settings/_components/setting-title/setting-title.component"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; diff --git a/UI/Web/src/app/user-settings/manage-devices/manage-devices.component.ts b/UI/Web/src/app/user-settings/manage-devices/manage-devices.component.ts index c56fe4aca..f64d859f2 100644 --- a/UI/Web/src/app/user-settings/manage-devices/manage-devices.component.ts +++ b/UI/Web/src/app/user-settings/manage-devices/manage-devices.component.ts @@ -10,7 +10,7 @@ import { DeviceService } from 'src/app/_services/device.service'; import { DevicePlatformPipe } from '../../_pipes/device-platform.pipe'; import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe'; import {NgbCollapse, NgbModal} from '@ng-bootstrap/ng-bootstrap'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SettingsService} from "../../admin/settings.service"; import {ConfirmService} from "../../shared/confirm.service"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; diff --git a/UI/Web/src/app/user-settings/manage-opds/manage-opds.component.ts b/UI/Web/src/app/user-settings/manage-opds/manage-opds.component.ts index f8ec6ab46..4349e9d5d 100644 --- a/UI/Web/src/app/user-settings/manage-opds/manage-opds.component.ts +++ b/UI/Web/src/app/user-settings/manage-opds/manage-opds.component.ts @@ -1,6 +1,6 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject} from '@angular/core'; import {ApiKeyComponent} from "../api-key/api-key.component"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {AccountService} from "../../_services/account.service"; import {SettingsService} from "../../admin/settings.service"; import {User} from "../../_models/user"; diff --git a/UI/Web/src/app/user-settings/manage-scrobbling-providers/manage-scrobbling-providers.component.ts b/UI/Web/src/app/user-settings/manage-scrobbling-providers/manage-scrobbling-providers.component.ts index eaee5a036..d62d0d083 100644 --- a/UI/Web/src/app/user-settings/manage-scrobbling-providers/manage-scrobbling-providers.component.ts +++ b/UI/Web/src/app/user-settings/manage-scrobbling-providers/manage-scrobbling-providers.component.ts @@ -9,7 +9,7 @@ import { } from "@ng-bootstrap/ng-bootstrap"; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {Select2Module} from "ng-select2-component"; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {AccountService} from "../../_services/account.service"; import {ScrobbleProvider, ScrobblingService} from "../../_services/scrobbling.service"; import {ToastrService} from "ngx-toastr"; diff --git a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html index 2e39672e1..b104fe479 100644 --- a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html +++ b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html @@ -367,6 +367,7 @@ + {{settingsForm.get('bookReaderFontSize')?.value + '%'}}
    diff --git a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.scss b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.scss index 0b4e72a12..9c67b2de4 100644 --- a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.scss +++ b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.scss @@ -14,5 +14,9 @@ .hex-code { font-size: 16px; - color: white; // TODO: Hook in with setting-item css variable + color: var(--primary-color); +} + +input[type="range"] { + //width: 92%; } diff --git a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.ts b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.ts index 79f439af4..1eaa3b40c 100644 --- a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.ts +++ b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import { bookLayoutModes, bookWritingStyles, @@ -133,6 +133,7 @@ export class ManageUserPreferencesComponent implements OnInit { user: User | undefined = undefined; get Locale() { + if (!this.settingsForm.get('locale')) return 'English'; return this.locales.filter(l => l.isoCode === this.settingsForm.get('locale')!.value)[0].title; } @@ -205,6 +206,7 @@ export class ManageUserPreferencesComponent implements OnInit { this.settingsForm.get('locale')?.disable(); } + // Automatically save settings as we edit them this.settingsForm.valueChanges.pipe( distinctUntilChanged(), debounceTime(100), @@ -214,9 +216,10 @@ export class ManageUserPreferencesComponent implements OnInit { const data = this.packSettings(); return this.accountService.updatePreferences(data); }), - tap(updatedPrefs => { + tap(prefs => { if (this.user) { - this.user.preferences = updatedPrefs; + this.user.preferences = {...prefs}; + this.reset(); this.cdRef.markForCheck(); } }) @@ -234,6 +237,45 @@ export class ManageUserPreferencesComponent implements OnInit { this.cdRef.markForCheck(); } + reset() { + if (!this.user) return; + + this.settingsForm.get('readingDirection')?.setValue(this.user.preferences.readingDirection, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('scalingOption')?.setValue(this.user.preferences.scalingOption, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('pageSplitOption')?.setValue(this.user.preferences.pageSplitOption, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('autoCloseMenu')?.setValue(this.user.preferences.autoCloseMenu, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('showScreenHints')?.setValue(this.user.preferences.showScreenHints, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('readerMode')?.setValue(this.user.preferences.readerMode, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('layoutMode')?.setValue(this.user.preferences.layoutMode, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('emulateBook')?.setValue(this.user.preferences.emulateBook, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('swipeToPaginate')?.setValue(this.user.preferences.swipeToPaginate, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('backgroundColor')?.setValue(this.user.preferences.backgroundColor, {onlySelf: true, emitEvent: false}); + + this.settingsForm.get('bookReaderFontFamily')?.setValue(this.user.preferences.bookReaderFontFamily, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderFontSize')?.setValue(this.user.preferences.bookReaderFontSize, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderLineSpacing')?.setValue(this.user.preferences.bookReaderLineSpacing, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderMargin')?.setValue(this.user.preferences.bookReaderMargin, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderReadingDirection')?.setValue(this.user.preferences.bookReaderReadingDirection, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderWritingStyle')?.setValue(this.user.preferences.bookReaderWritingStyle, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderTapToPaginate')?.setValue(this.user.preferences.bookReaderTapToPaginate, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderLayoutMode')?.setValue(this.user.preferences.bookReaderLayoutMode || BookPageLayoutMode.Default, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderThemeName')?.setValue(this.user?.preferences.bookReaderThemeName || bookColorThemes[0].name, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('bookReaderImmersiveMode')?.setValue(this.user?.preferences.bookReaderImmersiveMode, {onlySelf: true, emitEvent: false}); + + this.settingsForm.get('pdfTheme')?.setValue(this.user?.preferences.pdfTheme || PdfTheme.Dark, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('pdfScrollMode')?.setValue(this.user?.preferences.pdfScrollMode || PdfScrollMode.Vertical, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('pdfSpreadMode')?.setValue(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, {onlySelf: true, emitEvent: false}); + + this.settingsForm.get('theme')?.setValue(this.user.preferences.theme, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('globalPageLayoutMode')?.setValue(this.user.preferences.globalPageLayoutMode, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('blurUnreadSummaries')?.setValue(this.user.preferences.blurUnreadSummaries, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('promptForDownloadSize')?.setValue(this.user.preferences.promptForDownloadSize, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('noTransitions')?.setValue(this.user.preferences.noTransitions, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('collapseSeriesRelationships')?.setValue(this.user.preferences.collapseSeriesRelationships, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('shareReviews')?.setValue(this.user.preferences.shareReviews, {onlySelf: true, emitEvent: false}); + this.settingsForm.get('locale')?.setValue(this.user.preferences.locale || 'en', {onlySelf: true, emitEvent: false}); + } + packSettings(): Preferences { const modelSettings = this.settingsForm.value; return { @@ -264,7 +306,7 @@ export class ManageUserPreferencesComponent implements OnInit { swipeToPaginate: modelSettings.swipeToPaginate, collapseSeriesRelationships: modelSettings.collapseSeriesRelationships, shareReviews: modelSettings.shareReviews, - locale: modelSettings.locale, + locale: modelSettings.locale || 'en', pdfTheme: parseInt(modelSettings.pdfTheme, 10), pdfScrollMode: parseInt(modelSettings.pdfScrollMode, 10), pdfSpreadMode: parseInt(modelSettings.pdfSpreadMode, 10), @@ -281,10 +323,4 @@ export class ManageUserPreferencesComponent implements OnInit { this.settingsForm.get('backgroundColor')?.setValue(color); this.cdRef.markForCheck(); } - - translatePrefOptions(o: {text: string, value: any}) { - const d = {...o}; - d.text = translate('preferences.' + o.text); - return d; - } } diff --git a/UI/Web/src/app/user-settings/restriction-selector/restriction-selector.component.ts b/UI/Web/src/app/user-settings/restriction-selector/restriction-selector.component.ts index 20032b705..7e2f55a36 100644 --- a/UI/Web/src/app/user-settings/restriction-selector/restriction-selector.component.ts +++ b/UI/Web/src/app/user-settings/restriction-selector/restriction-selector.component.ts @@ -8,7 +8,7 @@ import { User } from 'src/app/_models/user'; import { MetadataService } from 'src/app/_services/metadata.service'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import {TitleCasePipe, NgTemplateOutlet} from '@angular/common'; -import {TranslocoModule} from "@ngneat/transloco"; +import {TranslocoModule} from "@jsverse/transloco"; @Component({ selector: 'app-restriction-selector', diff --git a/UI/Web/src/app/user-settings/scrobble-provider-item/scrobble-provider-item.component.ts b/UI/Web/src/app/user-settings/scrobble-provider-item/scrobble-provider-item.component.ts index cee26bd31..e0fbba039 100644 --- a/UI/Web/src/app/user-settings/scrobble-provider-item/scrobble-provider-item.component.ts +++ b/UI/Web/src/app/user-settings/scrobble-provider-item/scrobble-provider-item.component.ts @@ -9,7 +9,7 @@ import { } from '@angular/core'; import {NgOptimizedImage, NgTemplateOutlet} from "@angular/common"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ScrobbleProvider, ScrobblingService} from "../../_services/scrobbling.service"; import {ScrobbleProviderNamePipe} from "../../_pipes/scrobble-provider-name.pipe"; diff --git a/UI/Web/src/app/user-settings/theme-manager/theme-manager.component.ts b/UI/Web/src/app/user-settings/theme-manager/theme-manager.component.ts index 454d98528..3e4b57f13 100644 --- a/UI/Web/src/app/user-settings/theme-manager/theme-manager.component.ts +++ b/UI/Web/src/app/user-settings/theme-manager/theme-manager.component.ts @@ -15,7 +15,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { SiteThemeProviderPipe } from '../../_pipes/site-theme-provider.pipe'; import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe'; import { AsyncPipe, NgTemplateOutlet} from '@angular/common'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {shareReplay} from "rxjs/operators"; import {CarouselReelComponent} from "../../carousel/_components/carousel-reel/carousel-reel.component"; import {SeriesCardComponent} from "../../cards/series-card/series-card.component"; diff --git a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts index 936c556d2..752c870cf 100644 --- a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts +++ b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts @@ -12,7 +12,7 @@ import { NgbAccordionHeader, NgbAccordionItem } from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective} from "@ngneat/transloco"; +import {TranslocoDirective} from "@jsverse/transloco"; import {ListItemComponent} from "../../cards/list-item/list-item.component"; import {ImageService} from "../../_services/image.service"; import {ImageComponent} from "../../shared/image/image.component"; diff --git a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts index 3e6cfc091..45a2859b9 100644 --- a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts +++ b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts @@ -36,7 +36,7 @@ import { SeriesCardComponent } from '../../../cards/series-card/series-card.comp import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/card-detail-layout.component'; import { BulkOperationsComponent } from '../../../cards/bulk-operations/bulk-operations.component'; import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {translate, TranslocoDirective} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@jsverse/transloco"; import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2"; diff --git a/UI/Web/src/assets/images/ratings/unknown-rating.png b/UI/Web/src/assets/images/ratings/unknown-rating.png new file mode 100644 index 000000000..aadea021a Binary files /dev/null and b/UI/Web/src/assets/images/ratings/unknown-rating.png differ diff --git a/UI/Web/src/assets/langs/en.json b/UI/Web/src/assets/langs/en.json index a12024613..f8770f19a 100644 --- a/UI/Web/src/assets/langs/en.json +++ b/UI/Web/src/assets/langs/en.json @@ -133,7 +133,7 @@ "page-splitting-label": "Page Splitting", "page-splitting-tooltip": "How to split a full width image (ie both left and right images are combined)", "reading-mode-label": "Reading Mode", - "reading-mode-tooltip": "TODO", + "reading-mode-tooltip": "Change reader to paginate vertically, horizontally, or have an infinite scroll", "layout-mode-label": "Layout Mode", "layout-mode-tooltip": "Render a single image to the screen or two side-by-side images", "background-color-label": "Background Color", @@ -786,17 +786,35 @@ "edit-series-alt": "Edit Series Information", "download-series--tooltip": "Download Series", "downloading-status": "Downloading…", - "user-reviews-alt": "User Reviews", + "reviews-tab": "Reviews", "storyline-tab": "Storyline", "books-tab": "Books", "volumes-tab": "Volumes", "specials-tab": "Specials", "related-tab": "Related", + "cast-tab": "Cast", "recommendations-tab": "Recommendations", "send-to": "File emailed to {{deviceName}}", "no-pages": "{{toasts.no-pages}}", "no-chapters": "There are no chapters to this volume. Cannot read.", - "cover-change": "It can take up to a minute for your browser to refresh the image. Until then, the old image may be shown on some pages." + "cover-change": "It can take up to a minute for your browser to refresh the image. Until then, the old image may be shown on some pages.", + + "user-reviews-local": "Local Reviews", + "user-reviews-plus": "External Reviews", + + "writers-title": "Writers", + "cover-artists-title": "Cover 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" }, "series-metadata-detail": { @@ -862,7 +880,8 @@ "donate": "Donate", "donate-tooltip": "You can remove this by subscribing to Kavita+", "back": "Back", - "more": "More" + "more": "More", + "customize": "{{settings.customize}}" }, "library-settings-modal": { @@ -873,6 +892,7 @@ "folder-tab": "Folder", "cover-tab": "Cover", "advanced-tab": "Advanced", + "tasks-tab": "Tasks", "name-label": "Name", "library-name-unique": "Library name must be unique", "last-scanned-label": "Last Scanned:", @@ -900,8 +920,6 @@ "folder-watching-tooltip": "Override Server folder watching for this library. If off, folder watching won't run on the folders this library contains. If libraries share folders, then folders may still be ran against. Will always wait 10 minutes before triggering scan.", "include-in-dashboard-label": "Include in Dashboard", "include-in-dashboard-tooltip": "Should series from the library be included on the Dashboard. This affects all streams, like On Deck, Recently Updated, Recently Added, or any custom additions.", - "include-in-recommendation-label": "Include in Recommended", - "include-in-recommendation-tooltip": "Should series from the library be included on the Recommended page.", "include-in-search-label": "Include in Search", "include-in-search-tooltip": "Should series and any derived information (genres, people, files) from the library be included in search results.", "force-scan": "Force Scan", @@ -1355,7 +1373,7 @@ "convert-media-task": "Convert Media to Target Encoding", "convert-media-task-desc": "Runs a long-running task which will convert all kavita-managed media to the target encoding. This is slow (especially on ARM devices).", - "convert-media-success": "Conversion of Media to Target Encoding has been queued", + "convert-media-task-success": "Conversion of Media to Target Encoding has been queued", "bust-cache-task": "Bust Cache", "bust-cache-task-desc": "Busts the Kavita+ Cache - should only be used when debugging bad matches.", @@ -1607,7 +1625,9 @@ "help": "{{common.help}}", "announcements": "Announcements", "logout": "Logout", - "all-filters": "Smart Filters" + "all-filters": "Smart Filters", + "nav-link-header": "Navigation Options", + "close": "{{common.close}}" }, "promoted-icon": { @@ -1838,6 +1858,7 @@ "cover-image-tab": "Cover Image", "related-tab": "Related", "info-tab": "Info", + "tasks-tab": "{{library-settings-modal.tasks-tab}}", "genres-label": "Genres", "tags-label": "Tags", "cover-artist-label": "Cover Artist", @@ -2202,6 +2223,7 @@ "email-service-reachable": "Kavita Email Connection Successful", "email-service-unresponsive": "Email Service Url did not respond.", "refresh-covers-queued": "Refresh covers queued for {{name}}", + "generate-colorscape-queued": "Generate colorscape queued for {{name}}", "library-file-analysis-queued": "Library file analysis queued for {{name}}", "entity-read": "{{name}} is now read", "entity-unread": "{{name}} is now unread", @@ -2277,39 +2299,64 @@ "actionable": { "scan-library": "Scan Library", + "scan-library-tooltip": "Scan library for changes. Use force scan to force checking every folder", "refresh-covers": "Refresh Covers", + "refresh-covers-tooltip": "Regenerate all covers", "generate-colorscape": "Generate ColorScape", + "generate-colorscape-tooltip": "Generate colorscapes and any missing covers", "analyze-files": "Analyze Files", + "analyze-files-tooltip": "Analyze Files for extension type and size", "settings": "Settings", + "settings-tooltip": "View settings or details", "edit": "Edit", + "edit-tooltip": "Edit settings or details", "mark-as-read": "Mark as Read", + "mark-as-read-tooltip": "Mark progress as fully read", "mark-as-unread": "Mark as Unread", + "mark-as-unread-tooltip": "Mark progress as not read", "scan-series": "Scan Series", + "scan-series-tooltip": "Scan Series for changes", "add-to": "Add to", "add-to-want-to-read": "Add to Want to Read", + "add-to-want-to-read-tooltip": "Add series to Want to Read", "remove-from-want-to-read": "Remove from Want to Read", + "remove-from-want-to-read-tooltip": "Remove series from Want to Read", "remove-from-on-deck": "Remove From On Deck", + "remove-from-on-deck-tooltip": "Remove series from showing from On Deck", "others": "Others", "add-to-reading-list": "Add to Reading List", + "add-to-reading-list-tooltip": "Add to a Reading List", "add-to-collection": "Add to Collection", + "add-to-collection-tooltip": "A series to a collection", "send-to": "Send To", "delete": "Delete", + "delete-tooltip": "There is no way to revert this decision", "download": "Download", + "download-tooltip": "Download to device", "read-incognito": "Read Incognito", + "read-incognito-tooltip": "Read without tracking progress", "details": "Details", + "details-tooltip": "TODO", "view-series": "View Series", + "view-series-tooltip": "TODO", "clear": "{{common.clear}}", + "clear-tooltip": "Remove all bookmarks for this series", "import-cbl": "Import CBL", + "import-cbl-tooltip": "Creates a Reading List from a CBL File", "import-mal-stack": "Import MAL Stack", + "import-mal-stack-tooltip": "Creates a Smart Collection from your MAL Interest Stacks", "read": "Read", - "add-rule-group-and": "Add Rule Group (AND)", - "add-rule-group-or": "Add Rule Group (OR)", - "remove-rule-group": "Remove Rule Group", + "read-tooltip": "", "customize": "Customize", + "customize-tooltip": "TODO", "mark-visible": "Mark as Visible", + "mark-visible-tooltip": "TODO", "mark-invisible": "Mark as Invisible", + "mark-invisible-tooltip": "TODO", "unpromote": "Un-Promote", + "unpromote-tooltip": "Restrict the visibility to only your account", "promote": "Promote", + "promote-tooltip": "Make the item visible to all users", "new-collection": "New Collection", "multiple-selections": "Multiple Selections" }, @@ -2324,6 +2371,8 @@ "fit-to-width": "Fit to Width", "original": "Original", "fit-to-screen": "Fit to Screen", + "split-right-to-left": "Split Right to Left", + "split-left-to-right": "Split Left to Right", "no-split": "No Split", "webtoon": "Webtoon", "single": "Single", diff --git a/UI/Web/src/httpLoader.ts b/UI/Web/src/httpLoader.ts index 7c551c127..3f93a21d5 100644 --- a/UI/Web/src/httpLoader.ts +++ b/UI/Web/src/httpLoader.ts @@ -1,6 +1,6 @@ import {Injectable} from "@angular/core"; import {HttpClient} from "@angular/common/http"; -import {Translation, TranslocoLoader} from "@ngneat/transloco"; +import {Translation, TranslocoLoader} from "@jsverse/transloco"; import cacheBusting from 'i18n-cache-busting.json'; // allowSyntheticDefaultImports must be true @Injectable({ providedIn: 'root' }) diff --git a/UI/Web/src/main.ts b/UI/Web/src/main.ts index 26eeaabde..5faee134e 100644 --- a/UI/Web/src/main.ts +++ b/UI/Web/src/main.ts @@ -16,16 +16,16 @@ import {HTTP_INTERCEPTORS, withInterceptorsFromDi, provideHttpClient} from '@ang import { provideTransloco, TranslocoConfig, TranslocoService -} from "@ngneat/transloco"; +} from "@jsverse/transloco"; import {environment} from "./environments/environment"; import {HttpLoader} from "./httpLoader"; import { provideTranslocoPersistLang, -} from '@ngneat/transloco-persist-lang'; +} from "@jsverse/transloco-persist-lang"; import {AccountService} from "./app/_services/account.service"; import {switchMap} from "rxjs"; -import {provideTranslocoLocale} from "@ngneat/transloco-locale"; -import {provideTranslocoPersistTranslations} from "@ngneat/transloco-persist-translations"; +import {provideTranslocoLocale} from "@jsverse/transloco-locale"; +import {provideTranslocoPersistTranslations} from "@jsverse/transloco-persist-translations"; import {LazyLoadImageModule} from "ng-lazyload-image"; import {getSaver, SAVER} from "./app/_providers/saver.provider"; import {distinctUntilChanged} from "rxjs/operators"; diff --git a/UI/Web/src/theme/components/_input.scss b/UI/Web/src/theme/components/_input.scss index 8360df4e1..0186f60af 100644 --- a/UI/Web/src/theme/components/_input.scss +++ b/UI/Web/src/theme/components/_input.scss @@ -36,7 +36,3 @@ input:not([type="range"]), .form-control { background-color: var(--input-bg-color); border-color: var(--input-border-color); } - -.form-switch { - padding-left: 1.5em; -} diff --git a/UI/Web/src/theme/themes/dark.scss b/UI/Web/src/theme/themes/dark.scss index 8a3177d39..5c2bf718d 100644 --- a/UI/Web/src/theme/themes/dark.scss +++ b/UI/Web/src/theme/themes/dark.scss @@ -312,7 +312,7 @@ --brand-font-family: "Spartan", sans-serif; /* Card */ - --card-bg-color: var(--elevation-layer1); + --card-bg-color: var(--elevation-layer3); --card-text-color: var(--body-text-color); --card-border-width: 0 1px 1px 1px; --card-border-style: solid; diff --git a/openapi.json b/openapi.json index 03fa88656..7eaabcf7e 100644 --- a/openapi.json +++ b/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.1", + "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.2", "license": { "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" @@ -1245,6 +1245,45 @@ } } }, + "/api/Chapter": { + "get": { + "tags": [ + "Chapter" + ], + "parameters": [ + { + "name": "chapterId", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ChapterDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChapterDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ChapterDto" + } + } + } + } + } + } + }, "/api/Collection": { "get": { "tags": [