diff --git a/API/Controllers/ChapterController.cs b/API/Controllers/ChapterController.cs new file mode 100644 index 0000000000..4872342efe --- /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 0a2627e9b8..b0b25fb044 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -229,22 +229,25 @@ public async Task UpdateSeries(UpdateSeriesDto updateSeries) { // 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")); + } + + if (needsRefreshMetadata) + { + _taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id); } - return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-series-update")); + return Ok(); } /// diff --git a/API/Controllers/UploadController.cs b/API/Controllers/UploadController.cs index af6b9a7ccf..03841f26db 100644 --- a/API/Controllers/UploadController.cs +++ b/API/Controllers/UploadController.cs @@ -377,6 +377,7 @@ public async Task UploadLibraryCoverImageFromUrl(UploadFileDto upl 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 b346ca57be..a42c418af9 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 fd6f21e744..4c7ba0f444 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 a16d288711..14609b4aee 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 e4dfcf3036..abe1b50001 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 ffb72ff6a6..bceccd43a2 100644 --- a/API/DTOs/VolumeDto.cs +++ b/API/DTOs/VolumeDto.cs @@ -66,4 +66,10 @@ public bool IsSpecial() 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 21d707c2fe..2a6d8faffa 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 0c779d52e2..fb894121c2 100644 --- a/API/Entities/Chapter.cs +++ b/API/Entities/Chapter.cs @@ -193,4 +193,10 @@ public bool IsSingleVolumeChapter() { 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 4df55587c9..5570e37eb3 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 9fbc4a5920..7a413e3f44 100644 --- a/API/Entities/Library.cs +++ b/API/Entities/Library.cs @@ -80,4 +80,10 @@ public void UpdateLastScanned(DateTime? time) 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 359e576e6c..4a11845af7 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 65a5330bc0..b33700e059 100644 --- a/API/Entities/Series.cs +++ b/API/Entities/Series.cs @@ -145,4 +145,10 @@ public bool MatchesSeriesByName(string nameNormalized, string localizedNameNorma 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 bf7312d3dd..17c15b9782 100644 --- a/API/Entities/Volume.cs +++ b/API/Entities/Volume.cs @@ -73,4 +73,10 @@ public string GetNumberTitle() 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 2b6eedf493..5c4a5c95be 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,58 +3258,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true - }, - "node_modules/@ljharb/through": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", - "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@microsoft/signalr": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.14.tgz", - "integrity": "sha512-dnS7gSJF5LxByZwJaj82+F1K755ya7ttPT+JnSeCBef3sL8p8FBkHePXphK8NSuOquIb7vsphXWa28A+L2SPpw==", - "dependencies": { - "abort-controller": "^3.0.0", - "eventsource": "^2.0.2", - "fetch-cookie": "^2.0.3", - "node-fetch": "^2.6.7", - "ws": "^7.4.5" - } - }, - "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-16.0.0.tgz", - "integrity": "sha512-+FJ3e6cX9DW2t7021Ji3oz433rk3+4jLfqzU+Jyx6/vJz1dIOaML3EAY6lYuW4TLiXgMPOMvs6KzPFALGh4Lag==", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "^17.0.0", - "@angular/core": "^17.0.0", - "@angular/forms": "^17.0.0", - "@angular/localize": "^17.0.0", - "@popperjs/core": "^2.11.8", - "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==", + "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": { - "@ngneat/transloco-utils": "^5.0.0", + "@jsverse/transloco-utils": "^7.0.0", "flat": "6.0.1", "fs-extra": "^11.0.0", "glob": "^10.0.0", @@ -3322,59 +3276,59 @@ "@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==", + "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": ">=13.0.0", - "@ngneat/transloco": ">=4.0.0", + "@angular/core": ">=16.0.0", + "@jsverse/transloco": ">=7.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==", + "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", - "@ngneat/transloco": ">=5.0.0" + "@jsverse/transloco": ">=7.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==", + "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", - "@ngneat/transloco": ">=5.0.0" + "@jsverse/transloco": ">=7.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==", + "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", - "@ngneat/transloco": ">=5.0.0" + "@jsverse/transloco": ">=7.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==", + "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" @@ -3383,6 +3337,52 @@ "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", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@microsoft/signalr": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.14.tgz", + "integrity": "sha512-dnS7gSJF5LxByZwJaj82+F1K755ya7ttPT+JnSeCBef3sL8p8FBkHePXphK8NSuOquIb7vsphXWa28A+L2SPpw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + } + }, + "node_modules/@ng-bootstrap/ng-bootstrap": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-16.0.0.tgz", + "integrity": "sha512-+FJ3e6cX9DW2t7021Ji3oz433rk3+4jLfqzU+Jyx6/vJz1dIOaML3EAY6lYuW4TLiXgMPOMvs6KzPFALGh4Lag==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/localize": "^17.0.0", + "@popperjs/core": "^2.11.8", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "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 27a72daea8..1f3ee529cc 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 9ab6dea954..ade7956097 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 5d403469e8..41a8b1eefb 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 8f9c31fed2..39b1235627 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 3e1c8324cd..db846e9111 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 44db8d8e96..15554cf058 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 beef289b4d..f56e31194c 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 47cbf1865b..3f4da39a72 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 274dbf33b9..c168ebcb2f 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 d0fca1a53b..8f54e269ee 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 f2fd9c0fec..30bd478c93 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 61e7c5e68b..7cd541e0b3 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 e95d437888..4f20903299 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 e996eafdef..88d5954202 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 33a7c960e4..df4600a38a 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 c28cd48133..acd0993f73 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 9987347fe2..1e0e51c84a 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 b43f5e2c12..74a62647fc 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 9b526ae104..60672271b4 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 a0c3a9d669..45928d66bb 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 a5436255db..0720a9a9b4 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 281cd977a2..71ea22b1f1 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 04584d256a..88b9b1c3f7 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 d5aa0d7ccd..7fb0c2d7ee 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 6845f0a2a7..e6f4f4cb8f 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 f4d5a621df..98a62a2b6b 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 0927d8d428..7626a6cab0 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 b805c2fa49..f38f0779ad 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 7535ffea0a..2764cbb56e 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 b150cbaadd..6dc25c2ca5 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 08e0b29962..7597b7f382 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 8de6576a53..14425d21c2 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 bb7ff09b37..6899d8d516 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 8044631b3e..13ff4f7582 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 632beb0e7b..c15974b6b4 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 a66c255f5b..99039126c5 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 c608ff51a5..1d23bae4a3 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 f1680bb721..8136595d6e 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 c4eba8ca9d..3a231d3472 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 3d290f9c53..1497519298 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 0000000000..8588b914f5 --- /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 4320305d2f..e10a6744e5 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 f729084c57..1a0984f2b9 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 b977e22ba3..6991136cba 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 57003c3a9f..2bf7dae4b9 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 4926ac5e80..d1329e0f37 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 f9f8c53c37..54338740b4 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 64485c3dd5..0dd2f9f93d 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 8b791afb37..c7ba47ad82 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 f7158b27c5..227938b255 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 f2eeefbf58..bb68620fd6 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 6eefdcb700..4c50214798 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 34dcad9f12..2d275a27c4 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 6fa7177203..3a5ba05e5e 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 5ea4ac0e92..ab6965b770 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 a764b38856..5c0f554f31 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 658c0abe69..b460e82f8f 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 1e6a64605b..1c5e0b6fae 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 3293d61620..8f75028692 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 c050f0ca09..0a42b636ed 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 1b96d1f27b..20315fb8af 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 a14353c99b..f3b872c36c 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 df06da44bf..06df1b0bfa 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 a9240a804e..4ca6358e15 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 08effc0964..923052f4c9 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 63a66c68d6..b6a8d7e59f 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 37bb496665..13c8164e01 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 2ed5ca8f4f..4a98830b74 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 @@

{{t('customization-settings-title')}}

- -
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 8c61642863..bd5789435f 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 27f129c8bb..a5042fb6d8 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 5146cab4e7..c265e875c1 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 @@

{{t('title')}}

- -
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 3b43d80569..c0a11eb59f 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 1de33f6fcb..850204e07c 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 05d938a54f..54ffe4ca58 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 bb9ec0df8d..021c76faa4 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 be68010f91..a13b3577fd 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 c4d2a7245a..b8cafec9c5 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 132b347a66..2ac5212bc0 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 41e6f36d93..e5258bb093 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 1d4b260771..09a53815dc 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 7e28223a9a..f346b56020 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 9a02dd58b0..67a56903f2 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 7a672693b6..434b6a85a2 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 476d7e559e..f570655309 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 b4ea748066..7e3f3110fb 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 86d558ecc5..ecc6997c7f 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 68775efd94..e4800a1270 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 2a40ebc354..b8f65061b1 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 ba52bb0218..df6cbf14bc 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 7725aa776b..e0b6ebc2c2 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 @@
+ @if (nextExpectedChapter) { @switch (tabId) { @@ -368,26 +477,22 @@

{{t('page-settings-title' - @if (!item.isSpecial) { - - - } + + - @if (item.number !== LooseLeafOrSpecialNumber) { - - - } + + @@ -401,34 +506,30 @@

{{t('page-settings-title' - @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 ee7a1de2d5..316c33c351 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 880e60b105..526dcf10d0 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 f9c596abcf..0b97943971 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 b66af72287..7a50d98e63 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 80b0418b4b..6e3cf4d8c1 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 4757732ddb..3b6f032422 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 7ba448274a..20866b68e5 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 663f94f6d2..cab6109861 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 747d5445d8..11e32281b7 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 352f3c4317..90747c1d7f 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 04a23a3302..b3c8a74345 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 db56231f20..1beb8a2eaf 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 46f3375c8b..3b2d364791 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 48b2624aec..9fd915ab68 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 4d5ce1c7da..2bfa4eec58 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 d45607e7e6..3c472bb568 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 7045989e0d..82a0cc837d 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 9b77f4dbc9..3504e55e80 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 95076c6fda..0000000000 --- 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 384ca51145..0000000000 --- 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 97e16c1000..0000000000 --- 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 45fae2a039..1088897f61 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 141d742671..1835fe9314 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 e1e516a162..17c1f27047 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 7074e2c387..862ddf62e2 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 c55641ffe3..cf6a4df26e 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 743fecd8fd..99c399714a 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 4a09c1a92d..49fcebe650 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 811d9f84d1..3ccfca1fa5 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 56a569233d..25798d70af 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 fd55052914..e6315f1357 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 3c3beeba5e..3b65b7797b 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 50620e9ad6..569004e7fb 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 ffd343bd8c..692c9d8f20 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 @@
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 0b4e72a125..9c67b2de4d 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 79f439af45..1eaa3b40ce 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 20032b705e..7e2f55a36e 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 cee26bd31a..e0fbba0398 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 454d985282..3e4b57f136 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 936c556d25..752c870cfb 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 3e6cfc091d..45a2859b9d 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 0000000000..aadea021a1 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 a120246136..f8770f19a3 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 7c551c1279..3f93a21d52 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 26eeaabdea..5faee134e9 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 8360df4e16..0186f60afd 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 8a3177d39f..5c2bf718d8 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 03fa886567..7eaabcf7e0 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": [