From 7c4d7dc821588f15984cf89b4b026f01382bd483 Mon Sep 17 00:00:00 2001 From: Joe Milazzo Date: Thu, 14 Nov 2024 07:11:39 -0600 Subject: [PATCH] Polish 4 (#3363) --- API/Controllers/SettingsController.cs | 18 ++--- .../ManualMigrateEncodeSettings.cs | 67 +++++++++++++++++++ .../Converters/ServerSettingConverter.cs | 31 +++++---- API/Services/ArchiveService.cs | 2 + API/Services/MetadataService.cs | 1 + API/Services/ReadingItemService.cs | 1 + API/Startup.cs | 1 + .../library-selector.component.html | 4 +- .../manage-email-settings.component.ts | 22 ++---- .../role-selector.component.html | 4 +- .../all-series/all-series.component.ts | 15 ----- .../bookmarks/bookmarks.component.ts | 14 ---- .../bulk-operations.component.ts | 18 ++++- .../all-collections.component.ts | 14 ---- .../collection-detail.component.ts | 14 ---- .../library-detail.component.ts | 6 +- .../metadata-filter-row.component.ts | 28 ++++---- .../reading-lists/reading-lists.component.ts | 13 ---- .../download-button.component.ts | 2 - .../external-rating.component.ts | 6 +- .../metadata-detail-row.component.ts | 4 -- .../metadata-detail.component.ts | 2 +- .../series-detail/series-detail.component.ts | 54 ++------------- .../customize-sidenav-streams.component.ts | 16 ----- .../want-to-read/want-to-read.component.html | 54 ++++++++------- .../want-to-read/want-to-read.component.ts | 16 +---- 26 files changed, 182 insertions(+), 245 deletions(-) create mode 100644 API/Data/ManualMigrations/ManualMigrateEncodeSettings.cs diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs index 65e9587e1..e88432c1e 100644 --- a/API/Controllers/SettingsController.cs +++ b/API/Controllers/SettingsController.cs @@ -180,13 +180,6 @@ public class SettingsController : BaseApiController _unitOfWork.SettingsRepository.Update(setting); } - if (setting.Key == ServerSettingKey.CoverImageSize && - updateSettingsDto.CoverImageSize + string.Empty != setting.Value) - { - setting.Value = updateSettingsDto.CoverImageSize + string.Empty; - _unitOfWork.SettingsRepository.Update(setting); - } - if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + string.Empty != setting.Value) { if (OsInfo.IsDocker) continue; @@ -260,9 +253,16 @@ public class SettingsController : BaseApiController } if (setting.Key == ServerSettingKey.EncodeMediaAs && - updateSettingsDto.EncodeMediaAs + string.Empty != setting.Value) + ((int)updateSettingsDto.EncodeMediaAs).ToString() != setting.Value) { - setting.Value = updateSettingsDto.EncodeMediaAs + string.Empty; + setting.Value = ((int)updateSettingsDto.EncodeMediaAs).ToString(); + _unitOfWork.SettingsRepository.Update(setting); + } + + if (setting.Key == ServerSettingKey.CoverImageSize && + ((int)updateSettingsDto.CoverImageSize).ToString() != setting.Value) + { + setting.Value = ((int)updateSettingsDto.CoverImageSize).ToString(); _unitOfWork.SettingsRepository.Update(setting); } diff --git a/API/Data/ManualMigrations/ManualMigrateEncodeSettings.cs b/API/Data/ManualMigrations/ManualMigrateEncodeSettings.cs new file mode 100644 index 000000000..e71f583ba --- /dev/null +++ b/API/Data/ManualMigrations/ManualMigrateEncodeSettings.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using API.Entities; +using API.Entities.Enums; +using Flurl.Util; +using Kavita.Common.EnvironmentInfo; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace API.Data.ManualMigrations; + +/// +/// At some point, encoding settings wrote bad data to the backend, maybe in v0.8.0. This just fixes any bad data. +/// +public static class ManualMigrateEncodeSettings +{ + public static async Task Migrate(DataContext context, ILogger logger) + { + if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateEncodeSettings")) + { + return; + } + + logger.LogCritical("Running ManualMigrateEncodeSettings migration - Please be patient, this may take some time. This is not an error"); + + + var encodeAs = await context.ServerSetting.FirstAsync(s => s.Key == ServerSettingKey.EncodeMediaAs); + var coverSize = await context.ServerSetting.FirstAsync(s => s.Key == ServerSettingKey.CoverImageSize); + + var encodeMap = new Dictionary + { + { EncodeFormat.WEBP.ToString(), ((int)EncodeFormat.WEBP).ToString() }, + { EncodeFormat.PNG.ToString(), ((int)EncodeFormat.PNG).ToString() }, + { EncodeFormat.AVIF.ToString(), ((int)EncodeFormat.AVIF).ToString() } + }; + + if (encodeMap.TryGetValue(encodeAs.Value, out var encodedValue)) + { + encodeAs.Value = encodedValue; + context.ServerSetting.Update(encodeAs); + } + + if (coverSize.Value == "0") + { + coverSize.Value = ((int)CoverImageSize.Default).ToString(); + context.ServerSetting.Update(coverSize); + } + + + if (context.ChangeTracker.HasChanges()) + { + await context.SaveChangesAsync(); + } + + await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory() + { + Name = "ManualMigrateEncodeSettings", + ProductVersion = BuildInfo.Version.ToString(), + RanAt = DateTime.UtcNow + }); + await context.SaveChangesAsync(); + + logger.LogCritical("Running ManualMigrateEncodeSettings migration - Completed. This is not an error"); + } +} diff --git a/API/Helpers/Converters/ServerSettingConverter.cs b/API/Helpers/Converters/ServerSettingConverter.cs index 1180e4087..c9d31f8c2 100644 --- a/API/Helpers/Converters/ServerSettingConverter.cs +++ b/API/Helpers/Converters/ServerSettingConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using API.DTOs.Settings; using API.Entities; using API.Entities.Enums; @@ -33,7 +34,7 @@ public class ServerSettingConverter : ITypeConverter, destination.LoggingLevel = row.Value; break; case ServerSettingKey.Port: - destination.Port = int.Parse(row.Value); + destination.Port = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.IpAddresses: destination.IpAddresses = row.Value; @@ -53,11 +54,8 @@ public class ServerSettingConverter : ITypeConverter, case ServerSettingKey.InstallVersion: destination.InstallVersion = row.Value; break; - case ServerSettingKey.EncodeMediaAs: - destination.EncodeMediaAs = Enum.Parse(row.Value); - break; case ServerSettingKey.TotalBackups: - destination.TotalBackups = int.Parse(row.Value); + destination.TotalBackups = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.InstallId: destination.InstallId = row.Value; @@ -66,33 +64,36 @@ public class ServerSettingConverter : ITypeConverter, destination.EnableFolderWatching = bool.Parse(row.Value); break; case ServerSettingKey.TotalLogs: - destination.TotalLogs = int.Parse(row.Value); + destination.TotalLogs = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.HostName: destination.HostName = row.Value; break; case ServerSettingKey.CacheSize: - destination.CacheSize = long.Parse(row.Value); + destination.CacheSize = long.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.OnDeckProgressDays: - destination.OnDeckProgressDays = int.Parse(row.Value); + destination.OnDeckProgressDays = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.OnDeckUpdateDays: - destination.OnDeckUpdateDays = int.Parse(row.Value); + destination.OnDeckUpdateDays = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.CoverImageSize: destination.CoverImageSize = Enum.Parse(row.Value); break; + case ServerSettingKey.EncodeMediaAs: + destination.EncodeMediaAs = Enum.Parse(row.Value); + break; case ServerSettingKey.BackupDirectory: destination.BookmarksDirectory = row.Value; break; case ServerSettingKey.EmailHost: destination.SmtpConfig ??= new SmtpConfigDto(); - destination.SmtpConfig.Host = row.Value; + destination.SmtpConfig.Host = row.Value ?? string.Empty; break; case ServerSettingKey.EmailPort: destination.SmtpConfig ??= new SmtpConfigDto(); - destination.SmtpConfig.Port = string.IsNullOrEmpty(row.Value) ? 0 : int.Parse(row.Value); + destination.SmtpConfig.Port = string.IsNullOrEmpty(row.Value) ? 0 : int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.EmailAuthPassword: destination.SmtpConfig ??= new SmtpConfigDto(); @@ -116,18 +117,22 @@ public class ServerSettingConverter : ITypeConverter, break; case ServerSettingKey.EmailSizeLimit: destination.SmtpConfig ??= new SmtpConfigDto(); - destination.SmtpConfig.SizeLimit = int.Parse(row.Value); + destination.SmtpConfig.SizeLimit = int.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.EmailCustomizedTemplates: destination.SmtpConfig ??= new SmtpConfigDto(); destination.SmtpConfig.CustomizedTemplates = bool.Parse(row.Value); break; case ServerSettingKey.FirstInstallDate: - destination.FirstInstallDate = DateTime.Parse(row.Value); + destination.FirstInstallDate = DateTime.Parse(row.Value, CultureInfo.InvariantCulture); break; case ServerSettingKey.FirstInstallVersion: destination.FirstInstallVersion = row.Value; break; + case ServerSettingKey.LicenseKey: + break; + default: + throw new ArgumentOutOfRangeException(); } } diff --git a/API/Services/ArchiveService.cs b/API/Services/ArchiveService.cs index 120cbf3f7..aa0447fc2 100644 --- a/API/Services/ArchiveService.cs +++ b/API/Services/ArchiveService.cs @@ -127,9 +127,11 @@ public class ArchiveService : IArchiveService } case ArchiveLibrary.NotSupported: _logger.LogWarning("[GetNumberOfPagesFromArchive] This archive cannot be read: {ArchivePath}. Defaulting to 0 pages", archivePath); + _mediaErrorService.ReportMediaIssue(archivePath, MediaErrorProducer.ArchiveService, "File format not supported", string.Empty); return 0; default: _logger.LogWarning("[GetNumberOfPagesFromArchive] There was an exception when reading archive stream: {ArchivePath}. Defaulting to 0 pages", archivePath); + _mediaErrorService.ReportMediaIssue(archivePath, MediaErrorProducer.ArchiveService, "File format not supported", string.Empty); return 0; } } diff --git a/API/Services/MetadataService.cs b/API/Services/MetadataService.cs index b444ecde2..7a406fe48 100644 --- a/API/Services/MetadataService.cs +++ b/API/Services/MetadataService.cs @@ -362,6 +362,7 @@ public class MetadataService : IMetadataService return; } + // TODO: Cache this because it's called a lot during scans var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync(); var encodeFormat = settings.EncodeMediaAs; var coverImageSize = settings.CoverImageSize; diff --git a/API/Services/ReadingItemService.cs b/API/Services/ReadingItemService.cs index 3898bd238..581690733 100644 --- a/API/Services/ReadingItemService.cs +++ b/API/Services/ReadingItemService.cs @@ -99,6 +99,7 @@ public class ReadingItemService : IReadingItemService /// public int GetNumberOfPages(string filePath, MangaFormat format) { + switch (format) { case MangaFormat.Archive: diff --git a/API/Startup.cs b/API/Startup.cs index cf6c7ccfd..7ab16c466 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -276,6 +276,7 @@ public class Startup await ManualMigrateRemovePeople.Migrate(dataContext, logger); await MigrateDuplicateDarkTheme.Migrate(dataContext, logger); await ManualMigrateUnscrobbleBookLibraries.Migrate(dataContext, logger); + await ManualMigrateEncodeSettings.Migrate(dataContext, logger); // Update the version in the DB after all migrations are run var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion); diff --git a/UI/Web/src/app/admin/library-selector/library-selector.component.html b/UI/Web/src/app/admin/library-selector/library-selector.component.html index 617c91d6f..52ea164e3 100644 --- a/UI/Web/src/app/admin/library-selector/library-selector.component.html +++ b/UI/Web/src/app/admin/library-selector/library-selector.component.html @@ -7,9 +7,9 @@
@if(!isLoading && allLibraries.length > 0) { - - + }
diff --git a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts index f9c88092e..9b7d7e64c 100644 --- a/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts +++ b/UI/Web/src/app/admin/manage-email-settings/manage-email-settings.component.ts @@ -4,29 +4,20 @@ import {ToastrService} from 'ngx-toastr'; import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs'; import {SettingsService} from '../settings.service'; import {ServerSettings} from '../_models/server-settings'; -import { - NgbAlert, - NgbTooltip -} from '@ng-bootstrap/ng-bootstrap'; -import {AsyncPipe, NgTemplateOutlet, TitleCasePipe} from '@angular/common'; import {translate, TranslocoModule} from "@jsverse/transloco"; -import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe"; -import {ManageMediaIssuesComponent} from "../manage-media-issues/manage-media-issues.component"; import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component"; import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {BytesPipe} from "../../_pipes/bytes.pipe"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; @Component({ - selector: 'app-manage-email-settings', - templateUrl: './manage-email-settings.component.html', - styleUrls: ['./manage-email-settings.component.scss'], - standalone: true, + selector: 'app-manage-email-settings', + templateUrl: './manage-email-settings.component.html', + styleUrls: ['./manage-email-settings.component.scss'], + standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ReactiveFormsModule, NgbTooltip, NgTemplateOutlet, TranslocoModule, SafeHtmlPipe, - ManageMediaIssuesComponent, TitleCasePipe, NgbAlert, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe, AsyncPipe, CardActionablesComponent] + imports: [ReactiveFormsModule, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe] }) export class ManageEmailSettingsComponent implements OnInit { @@ -55,8 +46,8 @@ export class ManageEmailSettingsComponent implements OnInit { // Automatically save settings as we edit them this.settingsForm.valueChanges.pipe( + debounceTime(300), distinctUntilChanged(), - debounceTime(100), filter(_ => this.settingsForm.valid), takeUntilDestroyed(this.destroyRef), switchMap(_ => { @@ -65,7 +56,6 @@ export class ManageEmailSettingsComponent implements OnInit { }), tap(settings => { this.serverSettings = settings; - this.resetForm(); this.cdRef.markForCheck(); }) ).subscribe(); diff --git a/UI/Web/src/app/admin/role-selector/role-selector.component.html b/UI/Web/src/app/admin/role-selector/role-selector.component.html index 3d5a32558..31b46a13e 100644 --- a/UI/Web/src/app/admin/role-selector/role-selector.component.html +++ b/UI/Web/src/app/admin/role-selector/role-selector.component.html @@ -6,9 +6,9 @@
@if(selectedRoles.length > 0) { - - + }
diff --git a/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts b/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts index a1e3e4900..44b651875 100644 --- a/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts +++ b/UI/Web/src/app/all-series/_components/all-series/all-series.component.ts @@ -133,21 +133,6 @@ export class AllSeriesComponent implements OnInit { }); } - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } - - updateFilter(data: FilterEvent) { if (data.filterV2 === undefined) return; this.filter = data.filterV2; diff --git a/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts b/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts index e4800a127..878843464 100644 --- a/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts +++ b/UI/Web/src/app/bookmark/_components/bookmarks/bookmarks.component.ts @@ -100,20 +100,6 @@ export class BookmarksComponent implements OnInit { } - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } - async handleAction(action: ActionItem, series: Series) { switch (action.action) { case(Action.Delete): diff --git a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts index 9886cbadf..230a49cb1 100644 --- a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts +++ b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - DestroyRef, + DestroyRef, HostListener, inject, Input, OnInit @@ -14,6 +14,7 @@ import {AsyncPipe, DecimalPipe, NgStyle} from "@angular/common"; import {TranslocoModule} from "@jsverse/transloco"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; +import {KEY_CODES} from "../../shared/_services/utility.service"; @Component({ selector: 'app-bulk-operations', @@ -55,7 +56,20 @@ export class BulkOperationsComponent implements OnInit { public readonly bulkSelectionService = inject(BulkSelectionService); protected readonly Action = Action; - constructor() { } + @HostListener('document:keydown.shift', ['$event']) + handleKeypress(event: KeyboardEvent) { + if (event.key === KEY_CODES.SHIFT) { + this.bulkSelectionService.isShiftDown = true; + } + // TODO: See if we can figure out a select all (Ctrl+A) by having each method handle the event or pass all the data into this component. + } + + @HostListener('document:keyup.shift', ['$event']) + handleKeyUp(event: KeyboardEvent) { + if (event.key === KEY_CODES.SHIFT) { + this.bulkSelectionService.isShiftDown = false; + } + } ngOnInit(): void { this.bulkSelectionService.actions$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(actions => { diff --git a/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts b/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts index 6e1e501b9..ed771d5d8 100644 --- a/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts +++ b/UI/Web/src/app/collections/_components/all-collections/all-collections.component.ts @@ -81,20 +81,6 @@ export class AllCollectionsComponent implements OnInit { trackByIdentity = (index: number, item: UserCollection) => `${item.id}_${item.title}_${item.owner}_${item.promoted}`; user!: User; - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } - constructor() { this.router.routeReuseStrategy.shouldReuseRoute = () => false; diff --git a/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts index e0192781d..e1780675a 100644 --- a/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts +++ b/UI/Web/src/app/collections/_components/collection-detail/collection-detail.component.ts @@ -234,20 +234,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked { this.scrollService.setScrollContainer(this.scrollingBlock); } - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } - updateTag(tagId: number) { this.collectionService.allCollections().subscribe(tags => { const matchingTags = tags.filter(t => t.id === tagId); diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index 12f71e2f1..220e3ecc9 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -29,12 +29,10 @@ import {FilterSettings} from '../metadata-filter/filter-settings'; import {JumpKey} from '../_models/jumpbar/jump-key'; import {SeriesRemovedEvent} from '../_models/events/series-removed-event'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {SentenceCasePipe} from '../_pipes/sentence-case.pipe'; import {BulkOperationsComponent} from '../cards/bulk-operations/bulk-operations.component'; import {SeriesCardComponent} from '../cards/series-card/series-card.component'; import {CardDetailLayoutComponent} from '../cards/card-detail-layout/card-detail-layout.component'; import {DecimalPipe} from '@angular/common'; -import {NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavOutlet} from '@ng-bootstrap/ng-bootstrap'; import { SideNavCompanionBarComponent } from '../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; @@ -52,8 +50,8 @@ import {debounceTime, ReplaySubject, tap} from "rxjs"; styleUrls: ['./library-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [SideNavCompanionBarComponent, CardActionablesComponent, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavContent, - CardDetailLayoutComponent, SeriesCardComponent, BulkOperationsComponent, NgbNavOutlet, DecimalPipe, SentenceCasePipe, TranslocoDirective, LoadingComponent] + imports: [SideNavCompanionBarComponent, CardActionablesComponent, + CardDetailLayoutComponent, SeriesCardComponent, BulkOperationsComponent, DecimalPipe, TranslocoDirective, LoadingComponent] }) export class LibraryDetailComponent implements OnInit { diff --git a/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts b/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts index 47dae4c44..c4cbd2f99 100644 --- a/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts +++ b/UI/Web/src/app/metadata-filter/_components/metadata-filter-row/metadata-filter-row.component.ts @@ -11,7 +11,7 @@ import { } from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {FilterStatement} from '../../../_models/metadata/v2/filter-statement'; -import {BehaviorSubject, distinctUntilChanged, filter, map, Observable, of, startWith, switchMap} from 'rxjs'; +import {BehaviorSubject, distinctUntilChanged, filter, map, Observable, of, startWith, switchMap, tap} from 'rxjs'; import {MetadataService} from 'src/app/_services/metadata.service'; import {mangaFormatFilters} from 'src/app/_models/metadata/series-filter'; import {PersonRole} from 'src/app/_models/metadata/person'; @@ -135,11 +135,8 @@ const BooleanComparisons = [ FilterFieldPipe, FilterComparisonPipe, Select2Module, - NgTemplateOutlet, - TagBadgeComponent, NgbTooltip, TranslocoDirective, - NgbDatepicker, NgbInputDatepicker ], changeDetection: ChangeDetectionStrategy.OnPush @@ -207,9 +204,11 @@ export class MetadataFilterRowComponent implements OnInit { ); - this.formGroup!.valueChanges.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)).subscribe(_ => { - this.propagateFilterUpdate(); - }); + this.formGroup!.valueChanges.pipe( + distinctUntilChanged(), + tap(_ => this.propagateFilterUpdate()), + takeUntilDestroyed(this.destroyRef) + ).subscribe(); this.loaded = true; this.cdRef.markForCheck(); @@ -336,13 +335,13 @@ export class MetadataFilterRowComponent implements OnInit { if (StringFields.includes(inputVal)) { - const comps = [...StringComparisons]; + let comps = [...StringComparisons]; if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) { comps.push(FilterComparison.IsEmpty); } - this.validComparisons$.next(comps); + this.validComparisons$.next([...new Set(comps)]); this.predicateType$.next(PredicateType.Text); if (this.loaded) { @@ -362,7 +361,7 @@ export class MetadataFilterRowComponent implements OnInit { comps.push(FilterComparison.IsEmpty); } - this.validComparisons$.next(comps); + this.validComparisons$.next([...new Set(comps)]); this.predicateType$.next(PredicateType.Number); if (this.loaded) { this.formGroup.get('filterValue')?.patchValue(0); @@ -377,7 +376,7 @@ export class MetadataFilterRowComponent implements OnInit { comps.push(FilterComparison.IsEmpty); } - this.validComparisons$.next(comps); + this.validComparisons$.next([...new Set(comps)]); this.predicateType$.next(PredicateType.Date); if (this.loaded) { @@ -388,12 +387,13 @@ export class MetadataFilterRowComponent implements OnInit { } if (BooleanFields.includes(inputVal)) { - const comps = [...DateComparisons]; + let comps = [...DateComparisons]; if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) { comps.push(FilterComparison.IsEmpty); } - this.validComparisons$.next(comps); + + this.validComparisons$.next([...new Set(comps)]); this.predicateType$.next(PredicateType.Boolean); if (this.loaded) { @@ -415,7 +415,7 @@ export class MetadataFilterRowComponent implements OnInit { comps.push(FilterComparison.IsEmpty); } - this.validComparisons$.next(comps); + this.validComparisons$.next([...new Set(comps)]); this.predicateType$.next(PredicateType.Dropdown); if (this.loaded) { this.formGroup.get('filterValue')?.patchValue(0); diff --git a/UI/Web/src/app/reading-list/_components/reading-lists/reading-lists.component.ts b/UI/Web/src/app/reading-list/_components/reading-lists/reading-lists.component.ts index 2f430ee57..dbf49d186 100644 --- a/UI/Web/src/app/reading-list/_components/reading-lists/reading-lists.component.ts +++ b/UI/Web/src/app/reading-list/_components/reading-lists/reading-lists.component.ts @@ -49,19 +49,6 @@ export class ReadingListsComponent implements OnInit { globalActions: Array> = []; trackByIdentity = (index: number, item: ReadingList) => `${item.id}_${item.title}_${item.promoted}`; - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } constructor(private readingListService: ReadingListService, public imageService: ImageService, private actionFactoryService: ActionFactoryService, private accountService: AccountService, private toastr: ToastrService, private router: Router, diff --git a/UI/Web/src/app/series-detail/_components/download-button/download-button.component.ts b/UI/Web/src/app/series-detail/_components/download-button/download-button.component.ts index 8e2f413e2..15151af18 100644 --- a/UI/Web/src/app/series-detail/_components/download-button/download-button.component.ts +++ b/UI/Web/src/app/series-detail/_components/download-button/download-button.component.ts @@ -2,11 +2,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, - EventEmitter, inject, Input, OnInit, - Output } from '@angular/core'; import {AsyncPipe} from "@angular/common"; import {Observable, shareReplay, tap} from "rxjs"; diff --git a/UI/Web/src/app/series-detail/_components/external-rating/external-rating.component.ts b/UI/Web/src/app/series-detail/_components/external-rating/external-rating.component.ts index e01a40a81..2e26176dc 100644 --- a/UI/Web/src/app/series-detail/_components/external-rating/external-rating.component.ts +++ b/UI/Web/src/app/series-detail/_components/external-rating/external-rating.component.ts @@ -10,7 +10,7 @@ import { import {SeriesService} from "../../../_services/series.service"; import {Rating} from "../../../_models/rating"; import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; -import {NgbModal, NgbPopover, NgbRating} from "@ng-bootstrap/ng-bootstrap"; +import {NgbModal, NgbPopover} from "@ng-bootstrap/ng-bootstrap"; import {LoadingComponent} from "../../../shared/loading/loading.component"; import {LibraryType} from "../../../_models/library/library"; import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe"; @@ -22,13 +22,13 @@ import {TranslocoDirective} from "@jsverse/transloco"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {ImageService} from "../../../_services/image.service"; import {AsyncPipe, NgOptimizedImage, NgTemplateOutlet} from "@angular/common"; -import {InviteUserComponent} from "../../../admin/invite-user/invite-user.component"; import {RatingModalComponent} from "../rating-modal/rating-modal.component"; @Component({ selector: 'app-external-rating', standalone: true, - imports: [ProviderImagePipe, NgbRating, NgbPopover, LoadingComponent, ProviderNamePipe, NgxStarsModule, ImageComponent, TranslocoDirective, SafeHtmlPipe, NgOptimizedImage, AsyncPipe, NgTemplateOutlet], + imports: [ProviderImagePipe, NgbPopover, LoadingComponent, ProviderNamePipe, NgxStarsModule, ImageComponent, + TranslocoDirective, SafeHtmlPipe, NgOptimizedImage, AsyncPipe, NgTemplateOutlet], templateUrl: './external-rating.component.html', styleUrls: ['./external-rating.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/UI/Web/src/app/series-detail/_components/metadata-detail-row/metadata-detail-row.component.ts b/UI/Web/src/app/series-detail/_components/metadata-detail-row/metadata-detail-row.component.ts index 4d1d91b1e..952916d08 100644 --- a/UI/Web/src/app/series-detail/_components/metadata-detail-row/metadata-detail-row.component.ts +++ b/UI/Web/src/app/series-detail/_components/metadata-detail-row/metadata-detail-row.component.ts @@ -15,9 +15,7 @@ import {ImageService} from "../../../_services/image.service"; import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service"; import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison"; import {FilterField} from "../../../_models/metadata/v2/filter-field"; -import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe"; import {MangaFormat} from "../../../_models/manga-format"; -import {MangaFormatIconPipe} from "../../../_pipes/manga-format-icon.pipe"; import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component"; import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flipper/publisher-flipper.component"; @@ -32,8 +30,6 @@ import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flip NgbTooltip, TranslocoDirective, ImageComponent, - MangaFormatPipe, - MangaFormatIconPipe, SeriesFormatComponent, PublisherFlipperComponent ], diff --git a/UI/Web/src/app/series-detail/_components/metadata-detail/metadata-detail.component.ts b/UI/Web/src/app/series-detail/_components/metadata-detail/metadata-detail.component.ts index 90d0e2afa..9d2c3c441 100644 --- a/UI/Web/src/app/series-detail/_components/metadata-detail/metadata-detail.component.ts +++ b/UI/Web/src/app/series-detail/_components/metadata-detail/metadata-detail.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectionStrategy, Component, ContentChild, inject, Input, TemplateRef} from '@angular/core'; -import {CommonModule, NgTemplateOutlet} from '@angular/common'; +import {NgTemplateOutlet} from '@angular/common'; import {A11yClickDirective} from "../../../shared/a11y-click.directive"; import {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component"; import {TagBadgeComponent, TagBadgeCursor} from "../../../shared/tag-badge/tag-badge.component"; diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts index f5de0fbe4..551b49620 100644 --- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts +++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts @@ -1,11 +1,8 @@ import { AsyncPipe, - DecimalPipe, DOCUMENT, - JsonPipe, Location, NgClass, - NgOptimizedImage, NgStyle, NgTemplateOutlet } from '@angular/common'; @@ -37,7 +34,6 @@ import { NgbNavItem, NgbNavLink, NgbNavOutlet, - NgbProgressbar, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import {ToastrService} from 'ngx-toastr'; @@ -81,30 +77,20 @@ import { import {PageLayoutMode} from 'src/app/_models/page-layout-mode'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {UserReview} from "../../../_single-module/review-card/user-review"; -import {LoadingComponent} from '../../../shared/loading/loading.component'; import {ExternalSeriesCardComponent} from '../../../cards/external-series-card/external-series-card.component'; import {SeriesCardComponent} from '../../../cards/series-card/series-card.component'; -import {EntityTitleComponent} from '../../../cards/entity-title/entity-title.component'; -import {CardItemComponent} from '../../../cards/card-item/card-item.component'; import {VirtualScrollerModule} from '@iharbeck/ngx-virtual-scroller'; import {BulkOperationsComponent} from '../../../cards/bulk-operations/bulk-operations.component'; import {ReviewCardComponent} from '../../../_single-module/review-card/review-card.component'; import {CarouselReelComponent} from '../../../carousel/_components/carousel-reel/carousel-reel.component'; -import {ImageComponent} from '../../../shared/image/image.component'; -import {TagBadgeComponent} from '../../../shared/tag-badge/tag-badge.component'; -import { - SideNavCompanionBarComponent -} from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component"; import {PublicationStatus} from "../../../_models/metadata/publication-status"; import {NextExpectedChapter} from "../../../_models/series-detail/next-expected-chapter"; import {NextExpectedCardComponent} from "../../../cards/next-expected-card/next-expected-card.component"; -import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; import {MetadataService} from "../../../_services/metadata.service"; import {Rating} from "../../../_models/rating"; import {ThemeService} from "../../../_services/theme.service"; -import {PersonBadgeComponent} from "../../../shared/person-badge/person-badge.component"; import {DetailsTabComponent} from "../../../_single-module/details-tab/details-tab.component"; import { EditChapterModalCloseResult, @@ -116,23 +102,14 @@ import {VolumeCardComponent} from "../../../cards/volume-card/volume-card.compon import {SettingsTabId} from "../../../sidenav/preference-nav/preference-nav.component"; import {FilterField} from "../../../_models/metadata/v2/filter-field"; import {AgeRating} from "../../../_models/metadata/age-rating"; -import {AgeRatingPipe} from "../../../_pipes/age-rating.pipe"; import {DefaultValuePipe} from "../../../_pipes/default-value.pipe"; import {ExternalRatingComponent} from "../external-rating/external-rating.component"; import {ReadMoreComponent} from "../../../shared/read-more/read-more.component"; -import {ReadTimePipe} from "../../../_pipes/read-time.pipe"; import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison"; import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service"; -import {TimeAgoPipe} from "../../../_pipes/time-ago.pipe"; -import {AgeRatingImageComponent} from "../../../_single-modules/age-rating-image/age-rating-image.component"; -import {CompactNumberPipe} from "../../../_pipes/compact-number.pipe"; -import {IconAndTitleComponent} from "../../../shared/icon-and-title/icon-and-title.component"; -import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component"; -import {A11yClickDirective} from "../../../shared/a11y-click.directive"; import {ScrobblingService} from "../../../_services/scrobbling.service"; import {HourEstimateRange} from "../../../_models/series-detail/hour-estimate-range"; -import {ReadTimeLeftPipe} from "../../../_pipes/read-time-left.pipe"; import {PublicationStatusPipe} from "../../../_pipes/publication-status.pipe"; import {MetadataDetailRowComponent} from "../metadata-detail-row/metadata-detail-row.component"; import {DownloadButtonComponent} from "../download-button/download-button.component"; @@ -142,8 +119,6 @@ import {CoverUpdateEvent} from "../../../_models/events/cover-update-event"; import {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-modules/related-tab/related-tab.component"; import {CollectionTagService} from "../../../_services/collection-tag.service"; import {UserCollection} from "../../../_models/collection-tag"; -import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component"; -import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe"; import {CoverImageComponent} from "../../../_single-module/cover-image/cover-image.component"; import {DefaultModalOptions} from "../../../_models/default-modal-options"; @@ -171,16 +146,14 @@ interface StoryLineItem { styleUrls: ['./series-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [SideNavCompanionBarComponent, CardActionablesComponent, ReactiveFormsModule, NgStyle, - TagBadgeComponent, ImageComponent, NgbTooltip, NgbProgressbar, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, + imports: [CardActionablesComponent, ReactiveFormsModule, NgStyle, + NgbTooltip, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent, - NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, CardItemComponent, - EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet, - LoadingComponent, DecimalPipe, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent, - NgClass, NgOptimizedImage, ProviderImagePipe, AsyncPipe, PersonBadgeComponent, DetailsTabComponent, ChapterCardComponent, - VolumeCardComponent, JsonPipe, AgeRatingPipe, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, ReadTimePipe, - RouterLink, TimeAgoPipe, AgeRatingImageComponent, CompactNumberPipe, IconAndTitleComponent, SafeHtmlPipe, BadgeExpanderComponent, - A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, SeriesFormatComponent, MangaFormatPipe, CoverImageComponent] + NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet, + TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent, + NgClass, AsyncPipe, DetailsTabComponent, ChapterCardComponent, + VolumeCardComponent, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, RouterLink, BadgeExpanderComponent, + PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, CoverImageComponent] }) export class SeriesDetailComponent implements OnInit, AfterContentChecked { @@ -544,19 +517,6 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked { }); } - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } onNavChange(event: NgbNavChangeEvent) { this.bulkSelectionService.deselectAll(); 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 1835fe931..23bfebbc5 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 @@ -108,22 +108,6 @@ export class CustomizeSidenavStreamsComponent implements OnDestroy { }); } - - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - this.cdRef.markForCheck(); - } - } - constructor() { this.pageOperationsForm.get('accessibilityMode')?.valueChanges.pipe( diff --git a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.html b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.html index 71ad6f220..a113e7fa8 100644 --- a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.html +++ b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.html @@ -14,36 +14,40 @@
- - - - + @if (filter) { + + + + -
- - {{t('no-items')}} - + @if (!filterActive && series.length === 0) { +
+ + {{t('no-items')}} +
- -
- - {{t('no-items-filtered')}} - + } @else if (filterActive && series.length === 0) { +
+ + {{t('no-items-filtered')}} +
+ } + } +
diff --git a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts index 45a2859b9..f917f7dc7 100644 --- a/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts +++ b/UI/Web/src/app/want-to-read/_components/want-to-read/want-to-read.component.ts @@ -46,7 +46,7 @@ import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2"; styleUrls: ['./want-to-read.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [SideNavCompanionBarComponent, NgStyle, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, NgIf, DecimalPipe, TranslocoDirective] + imports: [SideNavCompanionBarComponent, NgStyle, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, DecimalPipe, TranslocoDirective] }) export class WantToReadComponent implements OnInit, AfterContentChecked { @@ -145,20 +145,6 @@ export class WantToReadComponent implements OnInit, AfterContentChecked { this.scrollService.setScrollContainer(this.scrollingBlock); } - @HostListener('document:keydown.shift', ['$event']) - handleKeypress(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = true; - } - } - - @HostListener('document:keyup.shift', ['$event']) - handleKeyUp(event: KeyboardEvent) { - if (event.key === KEY_CODES.SHIFT) { - this.bulkSelectionService.isShiftDown = false; - } - } - removeSeries(seriesId: number) { this.series = this.series.filter(s => s.id != seriesId); this.pagination.totalItems--;