diff --git a/API/API.csproj b/API/API.csproj index 8e80b029d2..34ba35da89 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -72,7 +72,6 @@ - diff --git a/API/Controllers/BookController.cs b/API/Controllers/BookController.cs index 4f747b1f16..962500ec75 100644 --- a/API/Controllers/BookController.cs +++ b/API/Controllers/BookController.cs @@ -101,8 +101,8 @@ public async Task GetBookPageResources(int chapterId, [FromQuery] if (chapterId <= 0) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist")); var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId); if (chapter == null) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist")); - using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath, BookService.BookReaderOptions); + using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath, BookService.BookReaderOptions); var key = BookService.CoalesceKeyForAnyFile(book, file); if (!book.Content.AllFiles.ContainsLocalFileRefWithKey(key)) return BadRequest(await _localizationService.Get("en", "file-missing")); diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs index aafb6efada..e4bb5db6cc 100644 --- a/API/Controllers/LibraryController.cs +++ b/API/Controllers/LibraryController.cs @@ -40,13 +40,15 @@ public class LibraryController : BaseApiController private readonly IEventHub _eventHub; private readonly ILibraryWatcher _libraryWatcher; private readonly ILocalizationService _localizationService; + private readonly IStreamService _streamService; private readonly IEasyCachingProvider _libraryCacheProvider; private const string CacheKey = "library_"; public LibraryController(IDirectoryService directoryService, ILogger logger, IMapper mapper, ITaskScheduler taskScheduler, IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher, - IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService) + IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService, + IStreamService streamService) { _directoryService = directoryService; _logger = logger; @@ -56,6 +58,7 @@ public LibraryController(IDirectoryService directoryService, _eventHub = eventHub; _libraryWatcher = libraryWatcher; _localizationService = localizationService; + _streamService = streamService; _libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library); } @@ -240,8 +243,6 @@ public async Task> UpdateUserLibraries(UpdateLibraryForU // Bust cache await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey); - // TODO: Update a user's SideNav based on library access - _unitOfWork.UserRepository.Update(user); return Ok(_mapper.Map(user)); diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index f0c527d474..1eb0dbb745 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -834,10 +834,10 @@ public async Task GetVolume(string apiKey, int seriesId, int volu foreach (var chapter in chapters) { var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id); - var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id); + var chapterDto = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id); foreach (var mangaFile in files) { - feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volumeId, chapter.Id, mangaFile, series, chapterTest, apiKey, prefix, baseUrl)); + feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volumeId, chapter.Id, mangaFile, series, chapterDto!, apiKey, prefix, baseUrl)); } } diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index 733f084b9b..932fba045b 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -231,6 +231,7 @@ public async Task> GetChapterInfo(int chapterId, bo var mangaFile = chapter.Files.First(); var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(dto.SeriesId, User.GetUserId()); + if (series == null) return Unauthorized(); var info = new ChapterInfoDto() { @@ -278,6 +279,7 @@ public async Task> GetChapterInfo(int chapterId, bo } } + return Ok(info); } diff --git a/API/Data/UnitOfWork.cs b/API/Data/UnitOfWork.cs index 3eadc3de00..49178cc579 100644 --- a/API/Data/UnitOfWork.cs +++ b/API/Data/UnitOfWork.cs @@ -2,10 +2,8 @@ using System.Threading.Tasks; using API.Data.Repositories; using API.Entities; -using API.Services; using AutoMapper; using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; namespace API.Data; @@ -42,7 +40,6 @@ public class UnitOfWork : IUnitOfWork private readonly DataContext _context; private readonly IMapper _mapper; private readonly UserManager _userManager; - private readonly ILocalizationService _localizationService; public UnitOfWork(DataContext context, IMapper mapper, UserManager userManager) { diff --git a/API/I18N/en.json b/API/I18N/en.json index 0648b04dfe..2f3dd6fb5a 100644 --- a/API/I18N/en.json +++ b/API/I18N/en.json @@ -189,12 +189,6 @@ "user-no-access-library-from-series": "User does not have access to the library this series belongs to", "series-restricted-age-restriction": "User is not allowed to view this series due to age restrictions", - "next-volume-num": "Upcoming Volume: {0}", - "next-book-num": "Upcoming Book: {0}", - "next-issue-num": "Upcoming Issue: {0}{1}", - "next-chapter-num": "Upcoming Chapter: {0}", - - "volume-num": "Volume {0}", "book-num": "Book {0}", "issue-num": "Issue {0}{1}", diff --git a/API/Services/SeriesService.cs b/API/Services/SeriesService.cs index acc41e8707..a234f64a22 100644 --- a/API/Services/SeriesService.cs +++ b/API/Services/SeriesService.cs @@ -261,24 +261,26 @@ void HandleAddPerson(Person person) HandleAddPerson, () => series.Metadata.CoverArtistLocked = true); } - - series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked; - series.Metadata.PublicationStatusLocked = updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked; - series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked; - series.Metadata.GenresLocked = updateSeriesMetadataDto.SeriesMetadata.GenresLocked; - series.Metadata.TagsLocked = updateSeriesMetadataDto.SeriesMetadata.TagsLocked; - series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharacterLocked; - series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristLocked; - series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorLocked; - series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkerLocked; - series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LettererLocked; - series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillerLocked; - series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublisherLocked; - series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked; - series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked; - series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked; - series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked; - series.Metadata.ReleaseYearLocked = updateSeriesMetadataDto.SeriesMetadata.ReleaseYearLocked; + if (updateSeriesMetadataDto.SeriesMetadata != null) + { + series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked; + series.Metadata.PublicationStatusLocked = updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked; + series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked; + series.Metadata.GenresLocked = updateSeriesMetadataDto.SeriesMetadata.GenresLocked; + series.Metadata.TagsLocked = updateSeriesMetadataDto.SeriesMetadata.TagsLocked; + series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharacterLocked; + series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristLocked; + series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorLocked; + series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkerLocked; + series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LettererLocked; + series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillerLocked; + series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublisherLocked; + series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked; + series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked; + series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked; + series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked; + series.Metadata.ReleaseYearLocked = updateSeriesMetadataDto.SeriesMetadata.ReleaseYearLocked; + } if (!_unitOfWork.HasChanges()) { @@ -721,6 +723,7 @@ public async Task GetEstimatedChapterCreationDate(int se return _emptyExpectedChapter; } + const int minimumTimeDeltas = 3; var chapters = _unitOfWork.ChapterRepository.GetChaptersForSeries(seriesId) .Where(c => !c.IsSpecial) .OrderBy(c => c.CreatedUtc) @@ -746,15 +749,14 @@ public async Task GetEstimatedChapterCreationDate(int se previousChapterTime = chapter.CreatedUtc; } - - if (timeDifferences.Count < 3) + if (timeDifferences.Count < minimumTimeDeltas) { return _emptyExpectedChapter; } var historicalTimeDifferences = timeDifferences.Select(td => td.TotalDays).ToList(); - if (historicalTimeDifferences.Count < 3) + if (historicalTimeDifferences.Count < minimumTimeDeltas) { return _emptyExpectedChapter; } @@ -793,10 +795,10 @@ public async Task GetEstimatedChapterCreationDate(int se result.VolumeNumber = lastChapter.Volume.Number; result.Title = series.Library.Type switch { - LibraryType.Manga => await _localizationService.Translate(userId, "next-chapter-num", result.ChapterNumber), - LibraryType.Comic => await _localizationService.Translate(userId, "next-issue-num", "#", result.ChapterNumber), - LibraryType.Book => await _localizationService.Translate(userId, "next-book-num", result.ChapterNumber), - _ => await _localizationService.Translate(userId, "next-chapter-num", result.ChapterNumber) + LibraryType.Manga => await _localizationService.Translate(userId, "chapter-num", result.ChapterNumber), + LibraryType.Comic => await _localizationService.Translate(userId, "issue-num", "#", result.ChapterNumber), + LibraryType.Book => await _localizationService.Translate(userId, "book-num", result.ChapterNumber), + _ => await _localizationService.Translate(userId, "chapter-num", result.ChapterNumber) }; } else diff --git a/API/Services/StreamService.cs b/API/Services/StreamService.cs index a52fab7678..fc10378232 100644 --- a/API/Services/StreamService.cs +++ b/API/Services/StreamService.cs @@ -33,7 +33,6 @@ public interface IStreamService Task CreateExternalSource(int userId, ExternalSourceDto dto); Task UpdateExternalSource(int userId, ExternalSourceDto dto); Task DeleteExternalSource(int userId, int externalSourceId); - } public class StreamService : IStreamService @@ -342,6 +341,4 @@ public async Task DeleteExternalSource(int userId, int externalSourceId) await _unitOfWork.CommitAsync(); } - - } diff --git a/API/Services/Tasks/Scanner/ProcessSeries.cs b/API/Services/Tasks/Scanner/ProcessSeries.cs index f6880adfd4..d73f3e5450 100644 --- a/API/Services/Tasks/Scanner/ProcessSeries.cs +++ b/API/Services/Tasks/Scanner/ProcessSeries.cs @@ -295,14 +295,17 @@ public void UpdateSeriesMetadata(Series series, Library library) if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1) { series.Metadata.MaxCount = 1; + } else if (series.Metadata.TotalCount == 1 && chapters.Count == 1 && chapters.First().IsSpecial) + { + // If a series has a TotalCount of 1 and there is only a Special, mark it as Complete + series.Metadata.MaxCount = series.Metadata.TotalCount; } else if ((maxChapter == 0 || maxChapter > series.Metadata.TotalCount) && maxVolume <= series.Metadata.TotalCount) { series.Metadata.MaxCount = maxVolume; } else if (maxVolume == series.Metadata.TotalCount) { series.Metadata.MaxCount = maxVolume; - } - else + } else { series.Metadata.MaxCount = maxChapter; } diff --git a/README.md b/README.md index f6d2a437ec..5cbd436755 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ your reading collection with your friends and family! [![Release](https://img.shields.io/github/release/Kareadita/Kavita.svg?style=flat&maxAge=3600)](https://github.com/Kareadita/Kavita/releases) [![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://github.com/Kareadita/Kavita/blob/master/LICENSE) [![Downloads](https://img.shields.io/github/downloads/Kareadita/Kavita/total.svg?style=flat)](https://github.com/Kareadita/Kavita/releases) -[![Docker Pulls](https://img.shields.io/docker/pulls/kizaing/kavita.svg)](https://hub.docker.com/r/kizaing/kavita/) +[![Docker Pulls](https://img.shields.io/docker/pulls/kizaing/kavita.svg)](https://hub.docker.com/r/jvmilazz0/kavita) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=security_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) [![Backers on Open Collective](https://opencollective.com/kavita/backers/badge.svg)](#backers) diff --git a/UI/Web/package-lock.json b/UI/Web/package-lock.json index 0d38dd80cb..9202c54f81 100644 --- a/UI/Web/package-lock.json +++ b/UI/Web/package-lock.json @@ -8,21 +8,21 @@ "name": "kavita-webui", "version": "0.4.2", "dependencies": { - "@angular/animations": "^17.0.3", + "@angular/animations": "^17.0.4", "@angular/cdk": "^17.0.1", - "@angular/common": "^17.0.3", - "@angular/compiler": "^17.0.3", - "@angular/core": "^17.0.3", - "@angular/forms": "^17.0.3", - "@angular/localize": "^17.0.3", - "@angular/platform-browser": "^17.0.3", - "@angular/platform-browser-dynamic": "^17.0.3", - "@angular/router": "^17.0.3", + "@angular/common": "^17.0.4", + "@angular/compiler": "^17.0.4", + "@angular/core": "^17.0.4", + "@angular/forms": "^17.0.4", + "@angular/localize": "^17.0.4", + "@angular/platform-browser": "^17.0.4", + "@angular/platform-browser-dynamic": "^17.0.4", + "@angular/router": "^17.0.4", "@fortawesome/fontawesome-free": "^6.4.2", "@iharbeck/ngx-virtual-scroller": "^17.0.0", "@iplab/ngx-file-upload": "^17.0.0", "@microsoft/signalr": "^7.0.12", - "@ng-bootstrap/ng-bootstrap": "^15.1.2", + "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@ngneat/transloco": "^6.0.0", "@ngneat/transloco-locale": "^5.1.1", "@ngneat/transloco-persist-lang": "^5.0.0", @@ -38,12 +38,13 @@ "ng-circle-progress": "^1.7.1", "ng-lazyload-image": "^9.1.3", "ng-select2-component": "^13.0.9", - "ngx-color-picker": "^15.0.0", - "ngx-extended-pdf-viewer": "^18.1.6", + "ngx-color-picker": "^16.0.0", + "ngx-extended-pdf-viewer": "^18.1.9", "ngx-file-drop": "^16.0.0", "ngx-slider-v2": "^17.0.0", "ngx-stars": "^1.6.5", - "ngx-toastr": "^17.0.2", + "ngx-toaster": "^1.0.1", + "ngx-toastr": "^18.0.0", "nosleep.js": "^0.12.0", "rxjs": "^7.8.0", "screenfull": "^6.0.2", @@ -52,20 +53,20 @@ "zone.js": "^0.14.2" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.0.1", + "@angular-devkit/build-angular": "^17.0.3", "@angular-eslint/builder": "^17.1.0", "@angular-eslint/eslint-plugin": "^17.1.0", "@angular-eslint/eslint-plugin-template": "^17.1.0", "@angular-eslint/schematics": "^17.1.0", "@angular-eslint/template-parser": "^17.1.0", - "@angular/cli": "^17.0.1", - "@angular/compiler-cli": "^17.0.3", + "@angular/cli": "^17.0.3", + "@angular/compiler-cli": "^17.0.4", "@types/d3": "^7.4.3", "@types/file-saver": "^2.0.7", - "@types/luxon": "^3.3.4", - "@types/node": "^20.9.2", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", + "@types/luxon": "^3.3.5", + "@types/node": "^20.10.0", + "@typescript-eslint/eslint-plugin": "^6.13.0", + "@typescript-eslint/parser": "^6.13.0", "eslint": "^8.54.0", "jsonminify": "^0.4.2", "karma-coverage": "~2.2.0", @@ -96,12 +97,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1700.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.1.tgz", - "integrity": "sha512-w84luzQNRjlt7XxX3+jyzcwBBv3gAjjvFWTjN1E5mlpDCUXgYmQ3CMowFHeu0U06HD5Sapap9p2l6GoajuZK5Q==", + "version": "0.1700.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.3.tgz", + "integrity": "sha512-HUjx7vD16paWXHKHYc2LsSn/kaYbFr2YNnlzuSr9C0kauKS1e7sRpRvtGwQzXfohzgyKi81AAU5uA2KLRGq83w==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.1", + "@angular-devkit/core": "17.0.3", "rxjs": "7.8.1" }, "engines": { @@ -111,15 +112,15 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.1.tgz", - "integrity": "sha512-OomGAeBg/OOxzPpoU7EkdD3WwhKip+0Giy/cGtkalSgQ5vWTuZhf8UnxwTf7xEXW5LtvfoTtv7sKmb1dJT7FzA==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.3.tgz", + "integrity": "sha512-1lx0mERC1eTHX4vf8q7kUHZNHS0jwZxbwYHAISOplwHjkzRociX0W6rx04yMXn2NCSNhK+w3xbWyAIgyYbP9nA==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1700.1", - "@angular-devkit/build-webpack": "0.1700.1", - "@angular-devkit/core": "17.0.1", + "@angular-devkit/architect": "0.1700.3", + "@angular-devkit/build-webpack": "0.1700.3", + "@angular-devkit/core": "17.0.3", "@babel/core": "7.23.2", "@babel/generator": "7.23.0", "@babel/helper-annotate-as-pure": "7.22.5", @@ -130,7 +131,7 @@ "@babel/preset-env": "7.23.2", "@babel/runtime": "7.23.2", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.0.1", + "@ngtools/webpack": "17.0.3", "@vitejs/plugin-basic-ssl": "1.0.1", "ansi-colors": "4.1.3", "autoprefixer": "10.4.16", @@ -669,12 +670,12 @@ "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1700.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.1.tgz", - "integrity": "sha512-u9LTcG9Kg2J6WkF1WSoBLdDabhbKxcuHY24SouAJTwg33j6YksglL7qnofOsNxny3Gdnze2BhCjQ1GS9Y8ovXw==", + "version": "0.1700.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.3.tgz", + "integrity": "sha512-r8nVakAnwV5Yy2AjWDpdcGUjHRQBcPljZDhX5tX2H7M3bxD6zG5owXDy8XmG64A7U1jd6D7dQv7zoW/tZwpYvw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1700.1", + "@angular-devkit/architect": "0.1700.3", "rxjs": "7.8.1" }, "engines": { @@ -688,9 +689,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.1.tgz", - "integrity": "sha512-UjNx9fZW0oU7UaeoB0HblYz/Nm8MWtinAe39XkY+zjECLWqKAcHPotfYjucXiky1UlBUOScIKbwjMDdEY8xkuw==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.3.tgz", + "integrity": "sha512-SOngD3rKnwZWhhUV68AYlH8M3LRGvF69jnDrYKwtRy1ESqSH7tt+1vexGC290gKvqH7bNMgYv8f5BS1AASRfzw==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -727,12 +728,12 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.1.tgz", - "integrity": "sha512-bwgdGviRZC5X8Tl4QcjtIJAcC0p8yIhOyYVFrq4PWYvI+DfV9P6w3OFuoS6rwEoiIQR90+12iKBYMt1MfL/c0Q==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.3.tgz", + "integrity": "sha512-gNocyYuNJRd24+JSM5kpO7g9Vg4THcoH5It8nJmS3muelLHDzegvDzXB7iPBjVR8Lxts6sbifYdIkKencUc4vg==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.1", + "@angular-devkit/core": "17.0.3", "jsonc-parser": "3.2.0", "magic-string": "0.30.5", "ora": "5.4.1", @@ -843,9 +844,9 @@ } }, "node_modules/@angular/animations": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.3.tgz", - "integrity": "sha512-aBLVJ0HHYdIZCAXymQDP6UGuz/5oM/3uLCFVHx32vhibLByjw0jNCvy2lzmPLU5gUU6wEWX2b3ZtnzFqhmo4+A==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.4.tgz", + "integrity": "sha512-XHkTBZAoYf1t4Hb06RkYa6cgtjEA5JGq1ArXu/DckOS6G/ZuY+dwWULEmaf9ejJem8O78ol223ZQ5d7sXqpixQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -853,7 +854,7 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.0.3" + "@angular/core": "17.0.4" } }, "node_modules/@angular/cdk": { @@ -873,15 +874,15 @@ } }, "node_modules/@angular/cli": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.1.tgz", - "integrity": "sha512-3iJWw+bpr/8y1ZY1m0wGfukffQVmD6DJUNubB297NCq1bJyUj+uwBuDnpIH+vidJvPBEEY+9XPJr0Jnd6+i7rg==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.3.tgz", + "integrity": "sha512-pRGXms87aEqmB4yPdcPI/VM7JegjDcBIeLadms0wrBkoyQiv+jL5LesxODhid6ujXZOj1xqMCYbCnX7HY+mLcQ==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1700.1", - "@angular-devkit/core": "17.0.1", - "@angular-devkit/schematics": "17.0.1", - "@schematics/angular": "17.0.1", + "@angular-devkit/architect": "0.1700.3", + "@angular-devkit/core": "17.0.3", + "@angular-devkit/schematics": "17.0.3", + "@schematics/angular": "17.0.3", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", @@ -940,9 +941,9 @@ "dev": true }, "node_modules/@angular/common": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.3.tgz", - "integrity": "sha512-AD/d1n0hNisHDhIeBsW2ERZI9ChjiOuZ3IiGwcYKmlcOHTrZTJPAh/ZMgahv24rArlNVax7bT+Ik8+sJedGcEQ==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.4.tgz", + "integrity": "sha512-/y38PbuiaWOuOmP5ZELTlJSjZGijc6Nq2XQloT5pKsaH935prxPjyWazwlY6cUnJMQgSRU644/ULosDJec7Zxw==", "dependencies": { "tslib": "^2.3.0" }, @@ -950,14 +951,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.0.3", + "@angular/core": "17.0.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.3.tgz", - "integrity": "sha512-ryUcj8Vc+Q4jMrjrmsEIsGLXeWSmNE/KoTyURPCH+NWq9GBMbjv4oe0/oFSBMN2ZtRMVCvqv2Nq+Z2KRDRGB0A==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.4.tgz", + "integrity": "sha512-OweJui9EWCa1ZcZjkJHS5z1gqICqyryR1Gdmyr8vIa6HD8wU/5BaeBJPCDgYgt+qJkvcT/sPxgZQsc2pVeUwbQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -965,7 +966,7 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.0.3" + "@angular/core": "17.0.4" }, "peerDependenciesMeta": { "@angular/core": { @@ -974,9 +975,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.3.tgz", - "integrity": "sha512-oj7KJBFgs6ulT1/A/xkkDHBOB0c7o9HV2Mn5pUosXBo2VgcGYeuJeXffC+mFr5FyiRO1sUanw4vSWnLzK1U0pQ==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.4.tgz", + "integrity": "sha512-ywj8XNI+hvHHYGcNWvXaVHSRtcd3S7MqJNgXWfnb0JjAb282oGSvjEc7wnH4ERqkvnSrpk1kQ2Fj3uJ2P5zfmQ==", "dev": true, "dependencies": { "@babel/core": "7.23.2", @@ -997,14 +998,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "17.0.3", + "@angular/compiler": "17.0.4", "typescript": ">=5.2 <5.3" } }, "node_modules/@angular/core": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.3.tgz", - "integrity": "sha512-zY4yhPiphuktrodaM+GiP8G07qnUlmwKElLjYazeIR8A+kF51RQRpSf/pWe5M0uJIn5Oja+RdO9kzhDI9QvOcA==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.4.tgz", + "integrity": "sha512-zk+z5sYPZd87pLxECx27quB5FvSmoi9PjJlcSlaBwwqaGnh/tPJI14u3q1dRY/CoZgP9egEiwc428+DzvOzJew==", "dependencies": { "tslib": "^2.3.0" }, @@ -1017,9 +1018,9 @@ } }, "node_modules/@angular/forms": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.3.tgz", - "integrity": "sha512-slCUGe5nVOrA0Su9pkmgPXBVzkgh4stvVFBb6ic9/+GlmtRi8h1v5jAFhR4B0R4iaaIoF+TTpRKhZShwtFSqSg==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.4.tgz", + "integrity": "sha512-R5J87dfJNWwi5SESD7tRkZnqG4u8KNAT4vImX4oG70/6vWioKUSWpLoSp1gpzy9UW51E85AKb8DNvIex7LclSg==", "dependencies": { "tslib": "^2.3.0" }, @@ -1027,16 +1028,16 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.3", - "@angular/core": "17.0.3", - "@angular/platform-browser": "17.0.3", + "@angular/common": "17.0.4", + "@angular/core": "17.0.4", + "@angular/platform-browser": "17.0.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.3.tgz", - "integrity": "sha512-AxMqZwClWNiYjoaHYu2Y499yM30DJDCgrV9k8mOb92SU3IUaQ52loPb91Cb2Xp4h0b6A3TVAmnYApcLYaUf40g==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.4.tgz", + "integrity": "sha512-4kLvkwAI9lVaxKzMVjF/0do/Xfn4r3W1bk9Xzb7fYLS21wz11ouL0aV7umtHT5DOZiwkY/F8rOYaJm5Fkz8ubw==", "dependencies": { "@babel/core": "7.23.2", "fast-glob": "3.3.1", @@ -1051,14 +1052,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "17.0.3", - "@angular/compiler-cli": "17.0.3" + "@angular/compiler": "17.0.4", + "@angular/compiler-cli": "17.0.4" } }, "node_modules/@angular/platform-browser": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.3.tgz", - "integrity": "sha512-4SoW0yeAxgfcLIekKsvZVg/WgI5aQZyz9HGOoyBcVQ8coYoZmM9bAYQi+9zvyweqoWc+jgw72X1E8wtmMXt7Aw==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.4.tgz", + "integrity": "sha512-lApUzVPfCEz/4aot77qzWUNg7yQgT0JSzy3BrBm95+2TbgH894J9Fswhig0sEN9jxGSkc3A5Yp5fs1HJcPqUiw==", "dependencies": { "tslib": "^2.3.0" }, @@ -1066,9 +1067,9 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "17.0.3", - "@angular/common": "17.0.3", - "@angular/core": "17.0.3" + "@angular/animations": "17.0.4", + "@angular/common": "17.0.4", + "@angular/core": "17.0.4" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1077,9 +1078,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.3.tgz", - "integrity": "sha512-Ab6ZeGG63z9Ilv8r4lHcmSirVaw8quRrPjDbT8cgIteHbj0SbwgDzxX0ve+fjjubFUluNSNtc6OYglWMHJ/g7Q==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.4.tgz", + "integrity": "sha512-mZZNH+iFzFug0z7rBQKdFz375sR6Y4iBbHu2aJD2BpgA2/SJaZ0WHGlF4bHbtpCYkZi3f4wKF2+Cwe4G5ebPOQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -1087,16 +1088,16 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.3", - "@angular/compiler": "17.0.3", - "@angular/core": "17.0.3", - "@angular/platform-browser": "17.0.3" + "@angular/common": "17.0.4", + "@angular/compiler": "17.0.4", + "@angular/core": "17.0.4", + "@angular/platform-browser": "17.0.4" } }, "node_modules/@angular/router": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.3.tgz", - "integrity": "sha512-zw31XXMqLJ1CcHxDtEl2/FTJXeRbbnLM8oHtCPzbbxTkhAlnXxSYxjds0+1IMmpzz/v9qGBhYvUt8ZfZhqDBHQ==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.4.tgz", + "integrity": "sha512-hQ+T+h6YE9NqyOmjqAIHe/k8xtW+yh0Mp8FCcl8REBezNyLXmOdsScCIOOc7GeFtbjRnQyJrBo4QxZ81acHP7Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -1104,9 +1105,9 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.3", - "@angular/core": "17.0.3", - "@angular/platform-browser": "17.0.3", + "@angular/common": "17.0.4", + "@angular/core": "17.0.4", + "@angular/platform-browser": "17.0.4", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -3608,18 +3609,18 @@ } }, "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.2.tgz", - "integrity": "sha512-mM2yiGnt9o7KZLIFp8K1vjfmVfu7HR3d8dhH5SszfArbgn9DvvQ4P5D5TDGygzyBSzeyZe18p7I8rX8vgA6DKw==", + "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": "^16.0.0", - "@angular/core": "^16.0.0", - "@angular/forms": "^16.0.0", - "@angular/localize": "^16.0.0", - "@popperjs/core": "^2.11.6", + "@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" } }, @@ -3714,9 +3715,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.1.tgz", - "integrity": "sha512-IfiWIBY1GntfJFV/U1CSOHZ7zF5p0zFMFzux7/iGXUXit299LTdJ5mZTe9++lFcH6dPHgEPWlinuYAfzorY4ng==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.3.tgz", + "integrity": "sha512-H39WQ/tM6kOErfiyU6QkPasMtuOZHbm6INkirSR3ol4e93o6gLJ0ptwg3IQlyGtZK2QexWagPC6jzsdGIaN3iw==", "dev": true, "engines": { "node": "^18.13.0 || >=20.9.0", @@ -4176,13 +4177,13 @@ } }, "node_modules/@schematics/angular": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.1.tgz", - "integrity": "sha512-BacI1fQsEXNYkfJzDJn3CsUSc9A4M7nhXtvt3XjceUhOqUp2AR4uIeUwDOrpLnkRwv5+rZLafUnRN3k01WUJOw==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.3.tgz", + "integrity": "sha512-pFHAqHMNm2WLoquJD4osSA/OAgH+wsFayPuqQnKjDEzeVW/YfJSbUksJ2iFt+uSfrhc/VxPf6pmGBMzi+9d0ng==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.1", - "@angular-devkit/schematics": "17.0.1", + "@angular-devkit/core": "17.0.3", + "@angular-devkit/schematics": "17.0.3", "jsonc-parser": "3.2.0" }, "engines": { @@ -4387,9 +4388,9 @@ } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", - "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -4748,9 +4749,9 @@ "dev": true }, "node_modules/@types/luxon": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.4.tgz", - "integrity": "sha512-H9OXxv4EzJwE75aTPKpiGXJq+y4LFxjpsdgKwSmr503P5DkWc3AG7VAFYrFNVvqemT5DfgZJV9itYhqBHSGujA==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.5.tgz", + "integrity": "sha512-1cyf6Ge/94zlaWIZA2ei1pE6SZ8xpad2hXaYa5JEFiaUH0YS494CZwyi4MXNpXD9oEuv6ZH0Bmh0e7F9sPhmZA==", "dev": true }, "node_modules/@types/mime": { @@ -4760,18 +4761,18 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", - "integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", + "version": "20.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", + "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/node-forge": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", - "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", + "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", "dev": true, "dependencies": { "@types/node": "*" @@ -4841,25 +4842,25 @@ } }, "node_modules/@types/ws": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", - "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", - "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.0.tgz", + "integrity": "sha512-HTvbSd0JceI2GW5DHS3R9zbarOqjkM9XDR7zL8eCsBUO/eSiHcoNE7kSL5sjGXmVa9fjH5LCfHDXNnH4QLp7tQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/type-utils": "6.11.0", - "@typescript-eslint/utils": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", + "@typescript-eslint/scope-manager": "6.13.0", + "@typescript-eslint/type-utils": "6.13.0", + "@typescript-eslint/utils": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4884,6 +4885,132 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.0.tgz", + "integrity": "sha512-2x0K2/CujsokIv+LN2T0l5FVDMtsCjkUyYtlcY4xxnxLAW+x41LXr16duoicHpGtLhmtN7kqvuFJ3zbz00Ikhw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.0.tgz", + "integrity": "sha512-YHufAmZd/yP2XdoD3YeFEjq+/Tl+myhzv+GJHSOz+ro/NFGS84mIIuLU3pVwUcauSmwlCrVXbBclkn1HfjY0qQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.13.0", + "@typescript-eslint/utils": "6.13.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.0.tgz", + "integrity": "sha512-oXg7DFxx/GmTrKXKKLSoR2rwiutOC7jCQ5nDH5p5VS6cmHE1TcPTaYQ0VPSSUvj7BnNqCgQ/NXcTBxn59pfPTQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.0.tgz", + "integrity": "sha512-IT4O/YKJDoiy/mPEDsfOfp+473A9GVqXlBKckfrAOuVbTqM8xbc0LuqyFCcgeFWpqu3WjQexolgqN2CuWBYbog==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.0.tgz", + "integrity": "sha512-V+txaxARI8yznDkcQ6FNRXxG+T37qT3+2NsDTZ/nKLxv6VfGrRhTnuvxPUxpVuWWr+eVeIxU53PioOXbz8ratQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.13.0", + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/typescript-estree": "6.13.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.0.tgz", + "integrity": "sha512-UQklteCEMCRoq/1UhKFZsHv5E4dN1wQSzJoxTfABasWk1HgJRdg1xNUve/Kv/Sdymt4x+iEzpESOqRFlQr/9Aw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4918,15 +5045,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", - "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.0.tgz", + "integrity": "sha512-VpG+M7GNhHLI/aTDctqAV0XbzB16vf+qDX9DXuMZSe/0bahzDA9AKZB15NDbd+D9M4cDsJvfkbGOA7qiZ/bWJw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/typescript-estree": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", + "@typescript-eslint/scope-manager": "6.13.0", + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/typescript-estree": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0", "debug": "^4.3.4" }, "engines": { @@ -4945,6 +5072,113 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.0.tgz", + "integrity": "sha512-2x0K2/CujsokIv+LN2T0l5FVDMtsCjkUyYtlcY4xxnxLAW+x41LXr16duoicHpGtLhmtN7kqvuFJ3zbz00Ikhw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.0.tgz", + "integrity": "sha512-oXg7DFxx/GmTrKXKKLSoR2rwiutOC7jCQ5nDH5p5VS6cmHE1TcPTaYQ0VPSSUvj7BnNqCgQ/NXcTBxn59pfPTQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.0.tgz", + "integrity": "sha512-IT4O/YKJDoiy/mPEDsfOfp+473A9GVqXlBKckfrAOuVbTqM8xbc0LuqyFCcgeFWpqu3WjQexolgqN2CuWBYbog==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "@typescript-eslint/visitor-keys": "6.13.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.0.tgz", + "integrity": "sha512-UQklteCEMCRoq/1UhKFZsHv5E4dN1wQSzJoxTfABasWk1HgJRdg1xNUve/Kv/Sdymt4x+iEzpESOqRFlQr/9Aw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", @@ -11489,9 +11723,9 @@ } }, "node_modules/ngx-color-picker": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-15.0.0.tgz", - "integrity": "sha512-+7wK8Pz9pm7ywJQOWELRcLYO9J0q4giF4b5QFxq8J3kEcHsUBn0hKOpBbGud+UmNnOwbJVgU2rhyRpGIDUCDJw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-16.0.0.tgz", + "integrity": "sha512-Dk2FvcbebD6STZSVzkI5oFHOlTrrNC5bOHh+YVaFgaWuWrVUdVIJm68ocUvTgr/qxTEJjrfcnRnS4wi7BJ2hKg==", "dependencies": { "tslib": "^2.3.0" }, @@ -11502,16 +11736,16 @@ } }, "node_modules/ngx-extended-pdf-viewer": { - "version": "18.1.6", - "resolved": "https://registry.npmjs.org/ngx-extended-pdf-viewer/-/ngx-extended-pdf-viewer-18.1.6.tgz", - "integrity": "sha512-MZi1bVgMtEqNGTW5QvjEAq7+x2Fje7yejxopBB+1yysevETt8cs9XYtiwiHWBpS7VhmOc0+vPgeK9FvZ4/27Tg==", + "version": "18.1.9", + "resolved": "https://registry.npmjs.org/ngx-extended-pdf-viewer/-/ngx-extended-pdf-viewer-18.1.9.tgz", + "integrity": "sha512-puISS6h1JoHObo0BZK68EhlWlI215AWP5RJ5D86yuWiBgVYeNUa8JrEVnaJtQ/bI6WbfvleaBe8NBwKnM0Bqsw==", "dependencies": { "lodash.deburr": "^4.1.0", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": ">=12.0.0 <17.0.0", - "@angular/core": ">=12.0.0 <17.0.0" + "@angular/common": ">=12.0.0 <18.0.0", + "@angular/core": ">=12.0.0 <18.0.0" } }, "node_modules/ngx-file-drop": { @@ -11557,10 +11791,23 @@ "@angular/core": ">=2.0.0" } }, + "node_modules/ngx-toaster": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ngx-toaster/-/ngx-toaster-1.0.1.tgz", + "integrity": "sha512-2XxTCT7+EWffb8wDpMLiFhwwZJ4B36Y1RM4m3rgG2cCt/8edsj3UzvqZjapF5wKwB+Jz8lVuVYJ94Hztcj83Cg==", + "peerDependencies": { + "@angular/common": ">=4.3.0 || >5.0.0", + "@angular/compiler": ">=4.3.0 || >5.0.0", + "@angular/core": ">=4.3.0 || >5.0.0", + "@angular/forms": ">=4.3.0 || >5.0.0", + "rxjs": ">=5.4.3", + "typescript": ">=2.3.0" + } + }, "node_modules/ngx-toastr": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-17.0.2.tgz", - "integrity": "sha512-KehiPx6bkbiUyJbabf0ZA04+ASumS8r/y4wPsUOMI9OrBvBcfq27UQmWuQKoVR8E+9y4Pq7eZkSg2kvxNvpxTA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-18.0.0.tgz", + "integrity": "sha512-jZ3rOG6kygl8ittY8OltIMSo47P1VStuS01igm3MZXK6InJwHVvxU7wDHI/HGMlXSyNvWncyOuFHnnMEAifsew==", "dependencies": { "tslib": "^2.3.0" }, diff --git a/UI/Web/package.json b/UI/Web/package.json index 54373a6c93..eee30e27b3 100644 --- a/UI/Web/package.json +++ b/UI/Web/package.json @@ -13,21 +13,21 @@ }, "private": true, "dependencies": { - "@angular/animations": "^17.0.3", + "@angular/animations": "^17.0.4", "@angular/cdk": "^17.0.1", - "@angular/common": "^17.0.3", - "@angular/compiler": "^17.0.3", - "@angular/core": "^17.0.3", - "@angular/forms": "^17.0.3", - "@angular/localize": "^17.0.3", - "@angular/platform-browser": "^17.0.3", - "@angular/platform-browser-dynamic": "^17.0.3", - "@angular/router": "^17.0.3", + "@angular/common": "^17.0.4", + "@angular/compiler": "^17.0.4", + "@angular/core": "^17.0.4", + "@angular/forms": "^17.0.4", + "@angular/localize": "^17.0.4", + "@angular/platform-browser": "^17.0.4", + "@angular/platform-browser-dynamic": "^17.0.4", + "@angular/router": "^17.0.4", "@fortawesome/fontawesome-free": "^6.4.2", "@iharbeck/ngx-virtual-scroller": "^17.0.0", "@iplab/ngx-file-upload": "^17.0.0", "@microsoft/signalr": "^7.0.12", - "@ng-bootstrap/ng-bootstrap": "^15.1.2", + "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@ngneat/transloco": "^6.0.0", "@ngneat/transloco-locale": "^5.1.1", "@ngneat/transloco-persist-lang": "^5.0.0", @@ -43,12 +43,13 @@ "ng-circle-progress": "^1.7.1", "ng-lazyload-image": "^9.1.3", "ng-select2-component": "^13.0.9", - "ngx-color-picker": "^15.0.0", - "ngx-extended-pdf-viewer": "^18.1.6", + "ngx-color-picker": "^16.0.0", + "ngx-extended-pdf-viewer": "^18.1.9", "ngx-file-drop": "^16.0.0", "ngx-slider-v2": "^17.0.0", "ngx-stars": "^1.6.5", - "ngx-toastr": "^17.0.2", + "ngx-toaster": "^1.0.1", + "ngx-toastr": "^18.0.0", "nosleep.js": "^0.12.0", "rxjs": "^7.8.0", "screenfull": "^6.0.2", @@ -57,20 +58,20 @@ "zone.js": "^0.14.2" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.0.1", + "@angular-devkit/build-angular": "^17.0.3", "@angular-eslint/builder": "^17.1.0", "@angular-eslint/eslint-plugin": "^17.1.0", "@angular-eslint/eslint-plugin-template": "^17.1.0", "@angular-eslint/schematics": "^17.1.0", "@angular-eslint/template-parser": "^17.1.0", - "@angular/cli": "^17.0.1", - "@angular/compiler-cli": "^17.0.3", + "@angular/cli": "^17.0.3", + "@angular/compiler-cli": "^17.0.4", "@types/d3": "^7.4.3", "@types/file-saver": "^2.0.7", - "@types/luxon": "^3.3.4", - "@types/node": "^20.9.2", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", + "@types/luxon": "^3.3.5", + "@types/node": "^20.10.0", + "@typescript-eslint/eslint-plugin": "^6.13.0", + "@typescript-eslint/parser": "^6.13.0", "eslint": "^8.54.0", "jsonminify": "^0.4.2", "karma-coverage": "~2.2.0", diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html index be5101d5a4..ccdc2aa2d2 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html @@ -160,7 +160,7 @@
{{t('title')}}
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 19a17fe8d5..c4cf2cdecf 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 @@ -102,10 +102,29 @@ const elementLevelStyles = ['line-height', 'font-family']; ]) ], standalone: true, - imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip, BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective] + imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, + NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip, + BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective] }) export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { + private readonly route = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly accountService = inject(AccountService); + private readonly seriesService = inject(SeriesService); + private readonly readerService = inject(ReaderService); + private readonly renderer = inject(Renderer2); + private readonly navService = inject(NavService); + private readonly toastr = inject(ToastrService); + private readonly domSanitizer = inject(DomSanitizer); + private readonly bookService = inject(BookService); + private readonly memberService = inject(MemberService); + private readonly scrollService = inject(ScrollService); + private readonly utilityService = inject(UtilityService); + private readonly libraryService = inject(LibraryService); + private readonly themeService = inject(ThemeService); + private readonly cdRef = inject(ChangeDetectorRef); + libraryId!: number; seriesId!: number; volumeId!: number; @@ -194,11 +213,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { */ page: SafeHtml | undefined = undefined; /** - * Next Chapter Id. This is not garunteed to be a valid ChapterId. Prefetched on page load (non-blocking). + * Next Chapter Id. This is not guaranteed to be a valid ChapterId. Prefetched on page load (non-blocking). */ nextChapterId: number = CHAPTER_ID_NOT_FETCHED; /** - * Previous Chapter Id. This is not garunteed to be a valid ChapterId. Prefetched on page load (non-blocking). + * Previous Chapter Id. This is not guaranteed to be a valid ChapterId. Prefetched on page load (non-blocking). */ prevChapterId: number = CHAPTER_ID_NOT_FETCHED; /** @@ -480,13 +499,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { return (this.windowHeight) - (this.topOffset * 2) + 'px'; } - - constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService, - private seriesService: SeriesService, private readerService: ReaderService, private location: Location, - private renderer: Renderer2, private navService: NavService, private toastr: ToastrService, - private domSanitizer: DomSanitizer, private bookService: BookService, private memberService: MemberService, - private scrollService: ScrollService, private utilityService: UtilityService, private libraryService: LibraryService, - @Inject(DOCUMENT) private document: Document, private themeService: ThemeService, private readonly cdRef: ChangeDetectorRef) { + constructor(@Inject(DOCUMENT) private document: Document) { this.navService.hideNavBar(); this.themeService.clearThemes(); this.navService.hideSideNav(); @@ -1620,7 +1633,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { // Responsible for handling pagination only handleContainerClick(event: MouseEvent) { - if (this.drawerOpen || ['action-bar'].some(className => (event.target as Element).classList.contains(className))) { + if (this.drawerOpen || ['action-bar', 'offcanvas-backdrop'].some(className => (event.target as Element).classList.contains(className))) { return; } diff --git a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.html b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.html index 75fe1ce821..cd8c0f2f88 100644 --- a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.html +++ b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.html @@ -3,11 +3,12 @@
- -
+ +
+
Upcoming
diff --git a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.scss b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.scss index e558bb0a56..e3bfacc604 100644 --- a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.scss +++ b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.scss @@ -6,6 +6,10 @@ background-color: transparent; } +.upcoming-header { + font-size: 16px; +} + .card-title { width: 146px; } diff --git a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts index d9f8bded8b..2db7158602 100644 --- a/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts +++ b/UI/Web/src/app/cards/next-expected-card/next-expected-card.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core'; import {CommonModule} from '@angular/common'; import {ImageComponent} from "../../shared/image/image.component"; import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter"; @@ -14,7 +14,7 @@ import {translate} from "@ngneat/transloco"; styleUrl: './next-expected-card.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) -export class NextExpectedCardComponent { +export class NextExpectedCardComponent implements OnInit { private readonly cdRef = inject(ChangeDetectorRef); /** @@ -25,19 +25,9 @@ export class NextExpectedCardComponent { * This is the entity we are representing. It will be returned if an action is executed. */ @Input({required: true}) entity!: NextExpectedChapter; - - /** - * Additional information to show on the overlay area. Will always render. - */ - @Input() overlayInformation: string = ''; title: string = ''; - - ngOnInit(): void { - const tokens = this.entity.title.split(':'); - this.overlayInformation = `
${tokens[0]}
${tokens[1]}
`; - if (this.entity.expectedDate) { const utcPipe = new UtcToLocalTimePipe(); this.title = translate('next-expected-card.title', {date: utcPipe.transform(this.entity.expectedDate, 'shortDate')}); diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts index 0315848de0..8fd798f911 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts @@ -67,7 +67,7 @@ import { FittingIconPipe } from '../../../_pipes/fitting-icon.pipe'; import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scroller.component'; import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive'; import { LoadingComponent } from '../../../shared/loading/loading.component'; -import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@ngneat/transloco"; const PREFETCH_PAGES = 10; diff --git a/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.html b/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.html index b4f611f334..9c1fddd9df 100644 --- a/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.html +++ b/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.html @@ -22,39 +22,41 @@

{{t('validate-description')}}

- - - - - - - - - -
{{t('validate-warning')}}
-
    -
  1. -
  2. -
-
- -
-
-
- -
-
- {{t('validate-no-issue')}} +
+ @for(fileToProcess of filesToProcess; track fileToProcess.fileName) { +
+
+ +
+
+
+ @if(summary.results.length > 0) { +
{{t('validate-warning')}}
+
    +
  1. +
  2. +
+ } @else { +
+
+
+ +
+
+ {{t('validate-no-issue')}} +
+ {{t('validate-no-issue-description')}}
- {{t('validate-no-issue-description')}} -
- - - - - + } +
+
+
+ } +
@@ -62,57 +64,65 @@
{{t('validate-warning')}}

{{t('dry-run-description')}}

- - - - - - - - - - - - - +
+ @for(fileToProcess of filesToProcess; track fileToProcess.fileName) { +
+
+ +
+
+
+ +
+
+
+ } +
- - - - - - - - - - - - - +
+ @for(fileToProcess of filesToProcess; track fileToProcess.fileName) { +
+
+ +
+
+
+ +
+
+
+ } +
    -
  • + @for(result of summary.results; track result.order) { +
  • + }
- - - {{success}} - {{success}} - {{success}} + + + {{success}} + {{success}} + {{success}} + - - {{filename}}: ({{summary.cblName}}) + {{filename}}: ({{summary.cblName}}) diff --git a/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.ts b/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.ts index 2345e16ccd..2e49a3a402 100644 --- a/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.ts +++ b/UI/Web/src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component.ts @@ -16,7 +16,7 @@ import {CommonModule} from "@angular/common"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {CblConflictReasonPipe} from "../../../_pipes/cbl-conflict-reason.pipe"; import {CblImportResultPipe} from "../../../_pipes/cbl-import-result.pipe"; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; interface FileStep { fileName: string; @@ -48,8 +48,6 @@ export class ImportCblModalComponent { @ViewChild('fileUpload') fileUpload!: ElementRef; - translocoService = inject(TranslocoService); - fileUploadControl = new FormControl>(undefined, [ FileUploadValidators.accept(['.cbl']), ]); @@ -61,10 +59,10 @@ export class ImportCblModalComponent { isLoading: boolean = false; steps: Array = [ - {title: this.translocoService.translate('import-cbl-modal.import-step'), index: Step.Import, active: true, icon: 'fa-solid fa-file-arrow-up'}, - {title: this.translocoService.translate('import-cbl-modal.validate-cbl-step'), index: Step.Validate, active: false, icon: 'fa-solid fa-spell-check'}, - {title: this.translocoService.translate('import-cbl-modal.dry-run-step'), index: Step.DryRun, active: false, icon: 'fa-solid fa-gears'}, - {title: this.translocoService.translate('import-cbl-modal.final-import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'}, + {title: translate('import-cbl-modal.import-step'), index: Step.Import, active: true, icon: 'fa-solid fa-file-arrow-up'}, + {title: translate('import-cbl-modal.validate-cbl-step'), index: Step.Validate, active: false, icon: 'fa-solid fa-spell-check'}, + {title: translate('import-cbl-modal.dry-run-step'), index: Step.DryRun, active: false, icon: 'fa-solid fa-gears'}, + {title: translate('import-cbl-modal.final-import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'}, ]; currentStepIndex = this.steps[0].index; @@ -103,7 +101,7 @@ export class ImportCblModalComponent { case Step.Import: const files = this.uploadForm.get('files')?.value; if (!files) { - this.toastr.error(this.translocoService.translate('toasts.select-files-warning')); + this.toastr.error(translate('toasts.select-files-warning')); return; } // Load each file into filesToProcess and group their data @@ -236,7 +234,7 @@ export class ImportCblModalComponent { this.isLoading = false; this.currentStepIndex++; - this.toastr.success(this.translocoService.translate('toasts.reading-list-imported')); + this.toastr.success(translate('toasts.reading-list-imported')); this.cdRef.markForCheck(); }); } diff --git a/UI/Web/src/app/shared/edit-list/edit-list.component.html b/UI/Web/src/app/shared/edit-list/edit-list.component.html index 7245422d90..8fbd7568dc 100644 --- a/UI/Web/src/app/shared/edit-list/edit-list.component.html +++ b/UI/Web/src/app/shared/edit-list/edit-list.component.html @@ -5,7 +5,7 @@
- +
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 index 2e00b966d9..b0b5d8615e 100644 --- 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 @@ -27,7 +27,8 @@ enum TabID { selector: 'app-customize-dashboard-modal', standalone: true, imports: [CommonModule, SafeHtmlPipe, TranslocoDirective, DraggableOrderedListComponent, ReadingListItemComponent, DashboardStreamListItemComponent, - NgbNav, NgbNavContent, NgbNavLink, NgbNavItem, NgbNavOutlet, CustomizeDashboardStreamsComponent, CustomizeSidenavStreamsComponent, ManageExternalSourcesComponent, ManageSmartFiltersComponent], + NgbNav, NgbNavContent, NgbNavLink, NgbNavItem, NgbNavOutlet, CustomizeDashboardStreamsComponent, CustomizeSidenavStreamsComponent, + ManageExternalSourcesComponent, ManageSmartFiltersComponent], templateUrl: './customize-dashboard-modal.component.html', styleUrls: ['./customize-dashboard-modal.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush 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 3ed5fa5c3f..d93a39fb84 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 @@ -13,6 +13,7 @@ import {forkJoin} from "rxjs"; import {TranslocoDirective} from "@ngneat/transloco"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {FilterPipe} from "../../../_pipes/filter.pipe"; +import {Breakpoint, UtilityService} from "../../../shared/_services/utility.service"; @Component({ selector: 'app-customize-dashboard-streams', @@ -31,6 +32,7 @@ export class CustomizeDashboardStreamsComponent { private readonly dashboardService = inject(DashboardService); private readonly filterService = inject(FilterService); private readonly cdRef = inject(ChangeDetectorRef); + private readonly utilityService = inject(UtilityService); listForm: FormGroup = new FormGroup({ 'filterQuery': new FormControl('', []) @@ -50,7 +52,7 @@ export class CustomizeDashboardStreamsComponent { this.items = results[0]; // After 100 items, drag and drop is disabled to use virtualization - if (this.items.length > 100) { + if (this.items.length > 100 || this.utilityService.getActiveBreakpoint() <= Breakpoint.Tablet) { this.accessibilityMode = true; } 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 5d232c1526..9dc7378a99 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 @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - DestroyRef, EventEmitter, + DestroyRef, HostListener, inject, OnDestroy @@ -29,9 +29,9 @@ import {FilterPipe} from "../../../_pipes/filter.pipe"; import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component"; import {Action, ActionItem} from "../../../_services/action-factory.service"; import {BulkSelectionService} from "../../../cards/bulk-selection.service"; -import {filter, tap} from "rxjs/operators"; +import {tap} from "rxjs/operators"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {KEY_CODES} from "../../../shared/_services/utility.service"; +import {Breakpoint, KEY_CODES, UtilityService} from "../../../shared/_services/utility.service"; @Component({ selector: 'app-customize-sidenav-streams', @@ -43,7 +43,6 @@ import {KEY_CODES} from "../../../shared/_services/utility.service"; }) export class CustomizeSidenavStreamsComponent implements OnDestroy { - //@Input({required: true}) parentScrollElem!: Element | Window; items: SideNavStream[] = []; smartFilters: SmartFilter[] = []; externalSources: ExternalSource[] = []; @@ -108,6 +107,7 @@ export class CustomizeSidenavStreamsComponent implements OnDestroy { private readonly cdRef = inject(ChangeDetectorRef); private readonly destroyRef = inject(DestroyRef); public readonly bulkSelectionService = inject(BulkSelectionService); + public readonly utilityService = inject(UtilityService); @HostListener('document:keydown.shift', ['$event']) handleKeypress(event: KeyboardEvent) { @@ -172,7 +172,7 @@ export class CustomizeSidenavStreamsComponent implements OnDestroy { this.items = results[0]; // After X items, drag and drop is disabled to use virtualization - if (this.items.length > this.virtualizeAfter) { + if (this.items.length > this.virtualizeAfter || this.utilityService.getActiveBreakpoint() <= Breakpoint.Tablet) { this.pageOperationsForm.get('accessibilityMode')?.setValue(true); } 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 7299e4add4..ac5ddceaaf 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 @@ -66,10 +66,10 @@
-

{{t('help-us-part-1')}}{{t('help-us-part-2')}} {{t('help-us-part-3')}}

+

{{t('help-us-part-1')}}{{t('help-us-part-2')}} {{t('help-us-part-3')}}

-

{{t('naming-conventions-part-1')}}{{t('naming-conventions-part-2')}} {{t('naming-conventions-part-3')}}

+

{{t('naming-conventions-part-1')}}{{t('naming-conventions-part-2')}} {{t('naming-conventions-part-3')}}

diff --git a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts index b7af340136..7dd16eb1ce 100644 --- a/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts +++ b/UI/Web/src/app/statistics/_components/reading-activity/reading-activity.component.ts @@ -11,7 +11,6 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import { LineChartModule } from '@swimlane/ngx-charts'; import { NgIf, NgFor, AsyncPipe } from '@angular/common'; import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; -import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe"; const options: Intl.DateTimeFormatOptions = { month: "short", day: "numeric" }; @@ -32,10 +31,10 @@ export class ReadingActivityComponent implements OnInit { @Input() isAdmin: boolean = true; @Input() individualUserMode: boolean = false; - private readonly utcDatePipe = new UtcToLocalTimePipe(); private readonly destroyRef = inject(DestroyRef); - private readonly translocoService = inject(TranslocoService); - private readonly cdRef = inject(ChangeDetectorRef); + //private readonly translocoService = inject(TranslocoService); + private readonly statService = inject(StatisticsService); + private readonly memberService = inject(MemberService); view: [number, number] = [0, 400]; formGroup: FormGroup = new FormGroup({ @@ -45,14 +44,14 @@ export class ReadingActivityComponent implements OnInit { users$: Observable | undefined; data$: Observable>; timePeriods = TimePeriods; - mangaFormatPipe = new MangaFormatPipe(this.translocoService); + //mangaFormatPipe = new MangaFormatPipe(this.translocoService); - constructor(private statService: StatisticsService, private memberService: MemberService) { + constructor() { this.data$ = this.formGroup.valueChanges.pipe( switchMap(_ => this.statService.getReadCountByDay(this.formGroup.get('users')!.value, this.formGroup.get('days')!.value)), map(data => { const gList = data.reduce((formats, entry) => { - const formatTranslated = this.mangaFormatPipe.transform(entry.format); + const formatTranslated = this.statService.mangaFormatPipe.transform(entry.format); if (!formats[formatTranslated]) { formats[formatTranslated] = { name: formatTranslated, @@ -76,7 +75,10 @@ export class ReadingActivityComponent implements OnInit { } ngOnInit(): void { - this.users$ = (this.isAdmin ? this.memberService.getMembers() : of([])).pipe(filter(_ => this.isAdmin), takeUntilDestroyed(this.destroyRef), shareReplay()); + this.users$ = (this.isAdmin ? this.memberService.getMembers() : of([])).pipe( + filter(_ => this.isAdmin), + takeUntilDestroyed(this.destroyRef), + shareReplay()); this.formGroup.get('users')?.setValue(this.userId, {emitValue: true}); if (!this.isAdmin) { diff --git a/openapi.json b/openapi.json index 1b6ef99835..84a1898ef5 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.7.10.16" + "version": "0.7.10.17" }, "servers": [ {