Co-authored-by: Andre Smith <Hobogrammer@users.noreply.github.com>
This commit is contained in:
Joe Milazzo 2023-11-28 16:00:04 -06:00 committed by GitHub
parent 565a93f2d2
commit 915bf13a7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 606 additions and 342 deletions

View File

@ -72,7 +72,6 @@
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />

View File

@ -101,8 +101,8 @@ public class BookController : BaseApiController
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"));

View File

@ -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<LibraryController> 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 class LibraryController : BaseApiController
_eventHub = eventHub;
_libraryWatcher = libraryWatcher;
_localizationService = localizationService;
_streamService = streamService;
_libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library);
}
@ -240,8 +243,6 @@ public class LibraryController : BaseApiController
// Bust cache
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
// TODO: Update a user's SideNav based on library access
_unitOfWork.UserRepository.Update(user);
return Ok(_mapper.Map<MemberDto>(user));

View File

@ -834,10 +834,10 @@ public class OpdsController : BaseApiController
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));
}
}

View File

@ -231,6 +231,7 @@ public class ReaderController : BaseApiController
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 class ReaderController : BaseApiController
}
}
return Ok(info);
}

View File

@ -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<AppUser> _userManager;
private readonly ILocalizationService _localizationService;
public UnitOfWork(DataContext context, IMapper mapper, UserManager<AppUser> userManager)
{

View File

@ -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}",

View File

@ -261,24 +261,26 @@ public class SeriesService : ISeriesService
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 class SeriesService : ISeriesService
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 class SeriesService : ISeriesService
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 class SeriesService : ISeriesService
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

View File

@ -33,7 +33,6 @@ public interface IStreamService
Task<ExternalSourceDto> CreateExternalSource(int userId, ExternalSourceDto dto);
Task<ExternalSourceDto> UpdateExternalSource(int userId, ExternalSourceDto dto);
Task DeleteExternalSource(int userId, int externalSourceId);
}
public class StreamService : IStreamService
@ -342,6 +341,4 @@ public class StreamService : IStreamService
await _unitOfWork.CommitAsync();
}
}

View File

@ -295,14 +295,17 @@ public class ProcessSeries : IProcessSeries
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;
}

View File

@ -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)

555
UI/Web/package-lock.json generated
View File

@ -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"
},

View File

@ -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",

View File

@ -160,7 +160,7 @@
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i></button>
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)" title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
</button>
</div>

View File

@ -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;
}

View File

@ -3,11 +3,12 @@
<app-image borderRadius=".25rem .25rem 0 0" height="230px" width="158px" classes="extreme-blur" [imageUrl]="imageUrl"></app-image>
<div class="card-overlay"></div>
<ng-container *ngIf="overlayInformation | safeHtml as info">
<div class="overlay-information overlay-information--centered" *ngIf="info !== '' || info !== undefined">
<ng-container *ngIf="entity.title | safeHtml as info">
<div class="overlay-information overlay-information--centered" *ngIf="info !== ''">
<div class="position-relative">
<span class="card-title library mx-auto" style="width: auto;">
<i class="fa-regular fa-clock mb-2" style="font-size: 26px" aria-hidden="true"></i>
<div class="upcoming-header">Upcoming</div>
<span [innerHTML]="info"></span>
</span>
</div>

View File

@ -6,6 +6,10 @@
background-color: transparent;
}
.upcoming-header {
font-size: 16px;
}
.card-title {
width: 146px;
}

View File

@ -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 = `<div>${tokens[0]}</div><div>${tokens[1]}</div>`;
if (this.entity.expectedDate) {
const utcPipe = new UtcToLocalTimePipe();
this.title = translate('next-expected-card.title', {date: utcPipe.transform(this.entity.expectedDate, 'shortDate')});

View File

@ -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;

View File

@ -22,39 +22,41 @@
<p>{{t('validate-description')}}</p>
<div class="row g-0">
<ngb-accordion #a="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess">
<ng-container *ngIf="fileToProcess.validateSummary as summary">
<ng-template ngbPanelTitle>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template>
<ng-template ngbPanelContent>
<ng-container *ngIf="summary.results.length > 0; else noValidateIssues">
<h5>{{t('validate-warning')}}</h5>
<ol class="list-group list-group-numbered list-group-flush" >
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
[innerHTML]="result | cblConflictReason | safeHtml">
</li>
</ol>
</ng-container>
<ng-template #noValidateIssues>
<div class="justify-content-center col">
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
</div>
<div class="flex-grow-1 ms-3">
{{t('validate-no-issue')}}
<div ngbAccordion #accordion="ngbAccordion">
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<div ngbAccordionItem *ngIf="fileToProcess.validateSummary as summary">
<h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</button>
</h5>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
@if(summary.results.length > 0) {
<h5>{{t('validate-warning')}}</h5>
<ol class="list-group list-group-numbered list-group-flush" >
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
[innerHTML]="result | cblConflictReason | safeHtml">
</li>
</ol>
} @else {
<div class="justify-content-center col">
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="fa-solid fa-circle-check" style="font-size: 24px" aria-hidden="true"></i>
</div>
<div class="flex-grow-1 ms-3">
{{t('validate-no-issue')}}
</div>
</div>
{{t('validate-no-issue-description')}}
</div>
{{t('validate-no-issue-description')}}
</div>
</ng-template>
</ng-template>
</ng-container>
</ngb-panel>
</ngb-accordion>
}
</div>
</div>
</div>
}
</div>
</div>
</ng-container>
@ -62,57 +64,65 @@
<div class="row g-0">
<p>{{t('dry-run-description')}}</p>
<ngb-accordion #a="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess">
<ng-container *ngIf="fileToProcess.dryRunSummary as summary">
<ng-template ngbPanelTitle>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template>
<ng-template ngbPanelContent>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</ng-template>
</ng-container>
</ngb-panel>
</ngb-accordion>
<div ngbAccordion #a="ngbAccordion">
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<div ngbAccordionItem *ngIf="fileToProcess.dryRunSummary as summary">
<h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</button>
</h5>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</div>
</div>
</div>
}
</div>
</div>
</ng-container>
<ng-container *ngIf="currentStepIndex === Step.Finalize">
<div class="row g-0">
<ngb-accordion #a="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess">
<ng-container *ngIf="fileToProcess.finalizeSummary as summary">
<ng-template ngbPanelTitle>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template>
<ng-template ngbPanelContent>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</ng-template>
</ng-container>
</ngb-panel>
</ngb-accordion>
<div ngbAccordion #a="ngbAccordion">
@for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<div ngbAccordionItem *ngIf="fileToProcess.finalizeSummary as summary">
<h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</button>
</h5>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</div>
</div>
</div>
}
</div>
</div>
</ng-container>
</div>
<ng-template #resultsList let-summary="summary">
<ul class="list-group list-group-flush">
<li class="list-group-item no-hover" *ngFor="let result of summary.results"
innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li>
@for(result of summary.results; track result.order) {
<li class="list-group-item no-hover"
innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li>
}
</ul>
</ng-template>
<ng-template #heading let-filename="filename" let-summary="summary">
<ng-container *ngIf="summary.success | cblImportResult as success">
<ng-container [ngSwitch]="summary.success">
<span *ngSwitchCase="CblImportResult.Success" class="badge bg-primary me-1">{{success}}</span>
<span *ngSwitchCase="CblImportResult.Fail" class="badge bg-danger me-1">{{success}}</span>
<span *ngSwitchCase="CblImportResult.Partial" class="badge bg-warning me-1">{{success}}</span>
<ng-container *ngIf="summary.success | cblImportResult as success">
<ng-container [ngSwitch]="summary.success">
<span *ngSwitchCase="CblImportResult.Success" class="badge bg-primary me-1">{{success}}</span>
<span *ngSwitchCase="CblImportResult.Fail" class="badge bg-danger me-1">{{success}}</span>
<span *ngSwitchCase="CblImportResult.Partial" class="badge bg-warning me-1">{{success}}</span>
</ng-container>
</ng-container>
</ng-container>
<span>{{filename}}<span *ngIf="summary.cblName">: ({{summary.cblName}})</span></span>
<span>{{filename}}<span *ngIf="summary.cblName">: ({{summary.cblName}})</span></span>
</ng-template>

View File

@ -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<HTMLInputElement>;
translocoService = inject(TranslocoService);
fileUploadControl = new FormControl<undefined | Array<File>>(undefined, [
FileUploadValidators.accept(['.cbl']),
]);
@ -61,10 +59,10 @@ export class ImportCblModalComponent {
isLoading: boolean = false;
steps: Array<TimelineStep> = [
{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();
});
}

View File

@ -5,7 +5,7 @@
<div class="col-lg-10 col-md-12 pe-2">
<div class="mb-3">
<label for="item--{{i}}" class="visually-hidden">{{label}}</label>
<input type="text" class="form-control" formControlName="link{{i}}" attr.id="item--{{i}}">
<input type="text" class="form-control" formControlName="link{{i}}" id="item--{{i}}">
</div>
</div>
<div class="col-lg-2">

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -66,10 +66,10 @@
</button>
</div>
<div class="row mt-2">
<p>{{t('help-us-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files" rel="noopener noreferrer" target="_blank" referrerpolicy="no-refer">{{t('help-us-part-2')}}</a> {{t('help-us-part-3')}}</p>
<p>{{t('help-us-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">{{t('help-us-part-2')}}</a> {{t('help-us-part-3')}}</p>
</div>
<div class="row mt-2">
<p>{{t('naming-conventions-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files/scanner#introduction" rel="noopener noreferrer" target="_blank" referrerpolicy="no-refer">{{t('naming-conventions-part-2')}}</a> {{t('naming-conventions-part-3')}}</p>
<p>{{t('naming-conventions-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files/scanner#introduction" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">{{t('naming-conventions-part-2')}}</a> {{t('naming-conventions-part-3')}}</p>
</div>
</ng-template>
</li>

View File

@ -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<Member[]> | undefined;
data$: Observable<Array<PieDataItem>>;
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) {

View File

@ -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": [
{