This commit is contained in:
Joe Milazzo 2024-11-14 07:11:39 -06:00 committed by GitHub
parent 2d5a7a3606
commit 7c4d7dc821
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 182 additions and 245 deletions

View File

@ -180,13 +180,6 @@ public class SettingsController : BaseApiController
_unitOfWork.SettingsRepository.Update(setting); _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 (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + string.Empty != setting.Value)
{ {
if (OsInfo.IsDocker) continue; if (OsInfo.IsDocker) continue;
@ -260,9 +253,16 @@ public class SettingsController : BaseApiController
} }
if (setting.Key == ServerSettingKey.EncodeMediaAs && 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); _unitOfWork.SettingsRepository.Update(setting);
} }

View File

@ -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;
/// <summary>
/// At some point, encoding settings wrote bad data to the backend, maybe in v0.8.0. This just fixes any bad data.
/// </summary>
public static class ManualMigrateEncodeSettings
{
public static async Task Migrate(DataContext context, ILogger<Program> 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<string, string>
{
{ 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");
}
}

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using API.DTOs.Settings; using API.DTOs.Settings;
using API.Entities; using API.Entities;
using API.Entities.Enums; using API.Entities.Enums;
@ -33,7 +34,7 @@ public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>,
destination.LoggingLevel = row.Value; destination.LoggingLevel = row.Value;
break; break;
case ServerSettingKey.Port: case ServerSettingKey.Port:
destination.Port = int.Parse(row.Value); destination.Port = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.IpAddresses: case ServerSettingKey.IpAddresses:
destination.IpAddresses = row.Value; destination.IpAddresses = row.Value;
@ -53,11 +54,8 @@ public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>,
case ServerSettingKey.InstallVersion: case ServerSettingKey.InstallVersion:
destination.InstallVersion = row.Value; destination.InstallVersion = row.Value;
break; break;
case ServerSettingKey.EncodeMediaAs:
destination.EncodeMediaAs = Enum.Parse<EncodeFormat>(row.Value);
break;
case ServerSettingKey.TotalBackups: case ServerSettingKey.TotalBackups:
destination.TotalBackups = int.Parse(row.Value); destination.TotalBackups = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.InstallId: case ServerSettingKey.InstallId:
destination.InstallId = row.Value; destination.InstallId = row.Value;
@ -66,33 +64,36 @@ public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>,
destination.EnableFolderWatching = bool.Parse(row.Value); destination.EnableFolderWatching = bool.Parse(row.Value);
break; break;
case ServerSettingKey.TotalLogs: case ServerSettingKey.TotalLogs:
destination.TotalLogs = int.Parse(row.Value); destination.TotalLogs = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.HostName: case ServerSettingKey.HostName:
destination.HostName = row.Value; destination.HostName = row.Value;
break; break;
case ServerSettingKey.CacheSize: case ServerSettingKey.CacheSize:
destination.CacheSize = long.Parse(row.Value); destination.CacheSize = long.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.OnDeckProgressDays: case ServerSettingKey.OnDeckProgressDays:
destination.OnDeckProgressDays = int.Parse(row.Value); destination.OnDeckProgressDays = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.OnDeckUpdateDays: case ServerSettingKey.OnDeckUpdateDays:
destination.OnDeckUpdateDays = int.Parse(row.Value); destination.OnDeckUpdateDays = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.CoverImageSize: case ServerSettingKey.CoverImageSize:
destination.CoverImageSize = Enum.Parse<CoverImageSize>(row.Value); destination.CoverImageSize = Enum.Parse<CoverImageSize>(row.Value);
break; break;
case ServerSettingKey.EncodeMediaAs:
destination.EncodeMediaAs = Enum.Parse<EncodeFormat>(row.Value);
break;
case ServerSettingKey.BackupDirectory: case ServerSettingKey.BackupDirectory:
destination.BookmarksDirectory = row.Value; destination.BookmarksDirectory = row.Value;
break; break;
case ServerSettingKey.EmailHost: case ServerSettingKey.EmailHost:
destination.SmtpConfig ??= new SmtpConfigDto(); destination.SmtpConfig ??= new SmtpConfigDto();
destination.SmtpConfig.Host = row.Value; destination.SmtpConfig.Host = row.Value ?? string.Empty;
break; break;
case ServerSettingKey.EmailPort: case ServerSettingKey.EmailPort:
destination.SmtpConfig ??= new SmtpConfigDto(); 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; break;
case ServerSettingKey.EmailAuthPassword: case ServerSettingKey.EmailAuthPassword:
destination.SmtpConfig ??= new SmtpConfigDto(); destination.SmtpConfig ??= new SmtpConfigDto();
@ -116,18 +117,22 @@ public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>,
break; break;
case ServerSettingKey.EmailSizeLimit: case ServerSettingKey.EmailSizeLimit:
destination.SmtpConfig ??= new SmtpConfigDto(); destination.SmtpConfig ??= new SmtpConfigDto();
destination.SmtpConfig.SizeLimit = int.Parse(row.Value); destination.SmtpConfig.SizeLimit = int.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.EmailCustomizedTemplates: case ServerSettingKey.EmailCustomizedTemplates:
destination.SmtpConfig ??= new SmtpConfigDto(); destination.SmtpConfig ??= new SmtpConfigDto();
destination.SmtpConfig.CustomizedTemplates = bool.Parse(row.Value); destination.SmtpConfig.CustomizedTemplates = bool.Parse(row.Value);
break; break;
case ServerSettingKey.FirstInstallDate: case ServerSettingKey.FirstInstallDate:
destination.FirstInstallDate = DateTime.Parse(row.Value); destination.FirstInstallDate = DateTime.Parse(row.Value, CultureInfo.InvariantCulture);
break; break;
case ServerSettingKey.FirstInstallVersion: case ServerSettingKey.FirstInstallVersion:
destination.FirstInstallVersion = row.Value; destination.FirstInstallVersion = row.Value;
break; break;
case ServerSettingKey.LicenseKey:
break;
default:
throw new ArgumentOutOfRangeException();
} }
} }

View File

@ -127,9 +127,11 @@ public class ArchiveService : IArchiveService
} }
case ArchiveLibrary.NotSupported: case ArchiveLibrary.NotSupported:
_logger.LogWarning("[GetNumberOfPagesFromArchive] This archive cannot be read: {ArchivePath}. Defaulting to 0 pages", archivePath); _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; return 0;
default: default:
_logger.LogWarning("[GetNumberOfPagesFromArchive] There was an exception when reading archive stream: {ArchivePath}. Defaulting to 0 pages", archivePath); _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; return 0;
} }
} }

View File

@ -362,6 +362,7 @@ public class MetadataService : IMetadataService
return; return;
} }
// TODO: Cache this because it's called a lot during scans
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync(); var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
var encodeFormat = settings.EncodeMediaAs; var encodeFormat = settings.EncodeMediaAs;
var coverImageSize = settings.CoverImageSize; var coverImageSize = settings.CoverImageSize;

View File

@ -99,6 +99,7 @@ public class ReadingItemService : IReadingItemService
/// <returns></returns> /// <returns></returns>
public int GetNumberOfPages(string filePath, MangaFormat format) public int GetNumberOfPages(string filePath, MangaFormat format)
{ {
switch (format) switch (format)
{ {
case MangaFormat.Archive: case MangaFormat.Archive:

View File

@ -276,6 +276,7 @@ public class Startup
await ManualMigrateRemovePeople.Migrate(dataContext, logger); await ManualMigrateRemovePeople.Migrate(dataContext, logger);
await MigrateDuplicateDarkTheme.Migrate(dataContext, logger); await MigrateDuplicateDarkTheme.Migrate(dataContext, logger);
await ManualMigrateUnscrobbleBookLibraries.Migrate(dataContext, logger); await ManualMigrateUnscrobbleBookLibraries.Migrate(dataContext, logger);
await ManualMigrateEncodeSettings.Migrate(dataContext, logger);
// Update the version in the DB after all migrations are run // Update the version in the DB after all migrations are run
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion); var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);

View File

@ -7,9 +7,9 @@
<div class="col-auto"> <div class="col-auto">
@if(!isLoading && allLibraries.length > 0) { @if(!isLoading && allLibraries.length > 0) {
<span class="form-check float-end"> <span class="form-check float-end">
<input id="select-all" type="checkbox" class="form-check-input" <input id="lib--select-all" type="checkbox" class="form-check-input"
[ngModel]="selectAll" (change)="toggleAll()" [indeterminate]="hasSomeSelected"> [ngModel]="selectAll" (change)="toggleAll()" [indeterminate]="hasSomeSelected">
<label for="select-all" class="form-check-label">{{selectAll ? t('deselect-all') : t('select-all')}}</label> <label for="lib--select-all" class="form-check-label">{{selectAll ? t('deselect-all') : t('select-all')}}</label>
</span> </span>
} }
</div> </div>

View File

@ -4,29 +4,20 @@ import {ToastrService} from 'ngx-toastr';
import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs'; import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs';
import {SettingsService} from '../settings.service'; import {SettingsService} from '../settings.service';
import {ServerSettings} from '../_models/server-settings'; 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 {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 {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component"; import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {BytesPipe} from "../../_pipes/bytes.pipe"; import {BytesPipe} from "../../_pipes/bytes.pipe";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
@Component({ @Component({
selector: 'app-manage-email-settings', selector: 'app-manage-email-settings',
templateUrl: './manage-email-settings.component.html', templateUrl: './manage-email-settings.component.html',
styleUrls: ['./manage-email-settings.component.scss'], styleUrls: ['./manage-email-settings.component.scss'],
standalone: true, standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, NgbTooltip, NgTemplateOutlet, TranslocoModule, SafeHtmlPipe, imports: [ReactiveFormsModule, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe]
ManageMediaIssuesComponent, TitleCasePipe, NgbAlert, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe, AsyncPipe, CardActionablesComponent]
}) })
export class ManageEmailSettingsComponent implements OnInit { export class ManageEmailSettingsComponent implements OnInit {
@ -55,8 +46,8 @@ export class ManageEmailSettingsComponent implements OnInit {
// Automatically save settings as we edit them // Automatically save settings as we edit them
this.settingsForm.valueChanges.pipe( this.settingsForm.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(), distinctUntilChanged(),
debounceTime(100),
filter(_ => this.settingsForm.valid), filter(_ => this.settingsForm.valid),
takeUntilDestroyed(this.destroyRef), takeUntilDestroyed(this.destroyRef),
switchMap(_ => { switchMap(_ => {
@ -65,7 +56,6 @@ export class ManageEmailSettingsComponent implements OnInit {
}), }),
tap(settings => { tap(settings => {
this.serverSettings = settings; this.serverSettings = settings;
this.resetForm();
this.cdRef.markForCheck(); this.cdRef.markForCheck();
}) })
).subscribe(); ).subscribe();

View File

@ -6,9 +6,9 @@
<div class="col-auto"> <div class="col-auto">
@if(selectedRoles.length > 0) { @if(selectedRoles.length > 0) {
<span class="form-check float-end"> <span class="form-check float-end">
<input id="select-all" type="checkbox" class="form-check-input" <input id="role--select-all" type="checkbox" class="form-check-input"
[ngModel]="selectAll" (change)="toggleAll()" [indeterminate]="hasSomeSelected"> [ngModel]="selectAll" (change)="toggleAll()" [indeterminate]="hasSomeSelected">
<label for="select-all" class="form-check-label">{{selectAll ? t('deselect-all') : t('select-all')}}</label> <label for="role--select-all" class="form-check-label">{{selectAll ? t('deselect-all') : t('select-all')}}</label>
</span> </span>
} }
</div> </div>

View File

@ -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) { updateFilter(data: FilterEvent) {
if (data.filterV2 === undefined) return; if (data.filterV2 === undefined) return;
this.filter = data.filterV2; this.filter = data.filterV2;

View File

@ -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: Series) { async handleAction(action: ActionItem<Series>, series: Series) {
switch (action.action) { switch (action.action) {
case(Action.Delete): case(Action.Delete):

View File

@ -2,7 +2,7 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef, DestroyRef, HostListener,
inject, inject,
Input, Input,
OnInit OnInit
@ -14,6 +14,7 @@ import {AsyncPipe, DecimalPipe, NgStyle} from "@angular/common";
import {TranslocoModule} from "@jsverse/transloco"; import {TranslocoModule} from "@jsverse/transloco";
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {KEY_CODES} from "../../shared/_services/utility.service";
@Component({ @Component({
selector: 'app-bulk-operations', selector: 'app-bulk-operations',
@ -55,7 +56,20 @@ export class BulkOperationsComponent implements OnInit {
public readonly bulkSelectionService = inject(BulkSelectionService); public readonly bulkSelectionService = inject(BulkSelectionService);
protected readonly Action = Action; 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 { ngOnInit(): void {
this.bulkSelectionService.actions$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(actions => { this.bulkSelectionService.actions$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(actions => {

View File

@ -81,20 +81,6 @@ export class AllCollectionsComponent implements OnInit {
trackByIdentity = (index: number, item: UserCollection) => `${item.id}_${item.title}_${item.owner}_${item.promoted}`; trackByIdentity = (index: number, item: UserCollection) => `${item.id}_${item.title}_${item.owner}_${item.promoted}`;
user!: User; 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() { constructor() {
this.router.routeReuseStrategy.shouldReuseRoute = () => false; this.router.routeReuseStrategy.shouldReuseRoute = () => false;

View File

@ -234,20 +234,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
this.scrollService.setScrollContainer(this.scrollingBlock); 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) { updateTag(tagId: number) {
this.collectionService.allCollections().subscribe(tags => { this.collectionService.allCollections().subscribe(tags => {
const matchingTags = tags.filter(t => t.id === tagId); const matchingTags = tags.filter(t => t.id === tagId);

View File

@ -29,12 +29,10 @@ import {FilterSettings} from '../metadata-filter/filter-settings';
import {JumpKey} from '../_models/jumpbar/jump-key'; import {JumpKey} from '../_models/jumpbar/jump-key';
import {SeriesRemovedEvent} from '../_models/events/series-removed-event'; import {SeriesRemovedEvent} from '../_models/events/series-removed-event';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {SentenceCasePipe} from '../_pipes/sentence-case.pipe';
import {BulkOperationsComponent} from '../cards/bulk-operations/bulk-operations.component'; import {BulkOperationsComponent} from '../cards/bulk-operations/bulk-operations.component';
import {SeriesCardComponent} from '../cards/series-card/series-card.component'; import {SeriesCardComponent} from '../cards/series-card/series-card.component';
import {CardDetailLayoutComponent} from '../cards/card-detail-layout/card-detail-layout.component'; import {CardDetailLayoutComponent} from '../cards/card-detail-layout/card-detail-layout.component';
import {DecimalPipe} from '@angular/common'; import {DecimalPipe} from '@angular/common';
import {NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavOutlet} from '@ng-bootstrap/ng-bootstrap';
import { import {
SideNavCompanionBarComponent SideNavCompanionBarComponent
} from '../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; } 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'], styleUrls: ['./library-detail.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
imports: [SideNavCompanionBarComponent, CardActionablesComponent, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavContent, imports: [SideNavCompanionBarComponent, CardActionablesComponent,
CardDetailLayoutComponent, SeriesCardComponent, BulkOperationsComponent, NgbNavOutlet, DecimalPipe, SentenceCasePipe, TranslocoDirective, LoadingComponent] CardDetailLayoutComponent, SeriesCardComponent, BulkOperationsComponent, DecimalPipe, TranslocoDirective, LoadingComponent]
}) })
export class LibraryDetailComponent implements OnInit { export class LibraryDetailComponent implements OnInit {

View File

@ -11,7 +11,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
import {FilterStatement} from '../../../_models/metadata/v2/filter-statement'; 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 {MetadataService} from 'src/app/_services/metadata.service';
import {mangaFormatFilters} from 'src/app/_models/metadata/series-filter'; import {mangaFormatFilters} from 'src/app/_models/metadata/series-filter';
import {PersonRole} from 'src/app/_models/metadata/person'; import {PersonRole} from 'src/app/_models/metadata/person';
@ -135,11 +135,8 @@ const BooleanComparisons = [
FilterFieldPipe, FilterFieldPipe,
FilterComparisonPipe, FilterComparisonPipe,
Select2Module, Select2Module,
NgTemplateOutlet,
TagBadgeComponent,
NgbTooltip, NgbTooltip,
TranslocoDirective, TranslocoDirective,
NgbDatepicker,
NgbInputDatepicker NgbInputDatepicker
], ],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
@ -207,9 +204,11 @@ export class MetadataFilterRowComponent implements OnInit {
); );
this.formGroup!.valueChanges.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)).subscribe(_ => { this.formGroup!.valueChanges.pipe(
this.propagateFilterUpdate(); distinctUntilChanged(),
}); tap(_ => this.propagateFilterUpdate()),
takeUntilDestroyed(this.destroyRef)
).subscribe();
this.loaded = true; this.loaded = true;
this.cdRef.markForCheck(); this.cdRef.markForCheck();
@ -336,13 +335,13 @@ export class MetadataFilterRowComponent implements OnInit {
if (StringFields.includes(inputVal)) { if (StringFields.includes(inputVal)) {
const comps = [...StringComparisons]; let comps = [...StringComparisons];
if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) { if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) {
comps.push(FilterComparison.IsEmpty); comps.push(FilterComparison.IsEmpty);
} }
this.validComparisons$.next(comps); this.validComparisons$.next([...new Set(comps)]);
this.predicateType$.next(PredicateType.Text); this.predicateType$.next(PredicateType.Text);
if (this.loaded) { if (this.loaded) {
@ -362,7 +361,7 @@ export class MetadataFilterRowComponent implements OnInit {
comps.push(FilterComparison.IsEmpty); comps.push(FilterComparison.IsEmpty);
} }
this.validComparisons$.next(comps); this.validComparisons$.next([...new Set(comps)]);
this.predicateType$.next(PredicateType.Number); this.predicateType$.next(PredicateType.Number);
if (this.loaded) { if (this.loaded) {
this.formGroup.get('filterValue')?.patchValue(0); this.formGroup.get('filterValue')?.patchValue(0);
@ -377,7 +376,7 @@ export class MetadataFilterRowComponent implements OnInit {
comps.push(FilterComparison.IsEmpty); comps.push(FilterComparison.IsEmpty);
} }
this.validComparisons$.next(comps); this.validComparisons$.next([...new Set(comps)]);
this.predicateType$.next(PredicateType.Date); this.predicateType$.next(PredicateType.Date);
if (this.loaded) { if (this.loaded) {
@ -388,12 +387,13 @@ export class MetadataFilterRowComponent implements OnInit {
} }
if (BooleanFields.includes(inputVal)) { if (BooleanFields.includes(inputVal)) {
const comps = [...DateComparisons]; let comps = [...DateComparisons];
if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) { if (FieldsThatShouldIncludeIsEmpty.includes(inputVal)) {
comps.push(FilterComparison.IsEmpty); comps.push(FilterComparison.IsEmpty);
} }
this.validComparisons$.next(comps);
this.validComparisons$.next([...new Set(comps)]);
this.predicateType$.next(PredicateType.Boolean); this.predicateType$.next(PredicateType.Boolean);
if (this.loaded) { if (this.loaded) {
@ -415,7 +415,7 @@ export class MetadataFilterRowComponent implements OnInit {
comps.push(FilterComparison.IsEmpty); comps.push(FilterComparison.IsEmpty);
} }
this.validComparisons$.next(comps); this.validComparisons$.next([...new Set(comps)]);
this.predicateType$.next(PredicateType.Dropdown); this.predicateType$.next(PredicateType.Dropdown);
if (this.loaded) { if (this.loaded) {
this.formGroup.get('filterValue')?.patchValue(0); this.formGroup.get('filterValue')?.patchValue(0);

View File

@ -49,19 +49,6 @@ export class ReadingListsComponent implements OnInit {
globalActions: Array<ActionItem<any>> = []; globalActions: Array<ActionItem<any>> = [];
trackByIdentity = (index: number, item: ReadingList) => `${item.id}_${item.title}_${item.promoted}`; 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, constructor(private readingListService: ReadingListService, public imageService: ImageService, private actionFactoryService: ActionFactoryService,
private accountService: AccountService, private toastr: ToastrService, private router: Router, private accountService: AccountService, private toastr: ToastrService, private router: Router,

View File

@ -2,11 +2,9 @@ import {
ChangeDetectionStrategy, ChangeDetectorRef, ChangeDetectionStrategy, ChangeDetectorRef,
Component, Component,
DestroyRef, DestroyRef,
EventEmitter,
inject, inject,
Input, Input,
OnInit, OnInit,
Output
} from '@angular/core'; } from '@angular/core';
import {AsyncPipe} from "@angular/common"; import {AsyncPipe} from "@angular/common";
import {Observable, shareReplay, tap} from "rxjs"; import {Observable, shareReplay, tap} from "rxjs";

View File

@ -10,7 +10,7 @@ import {
import {SeriesService} from "../../../_services/series.service"; import {SeriesService} from "../../../_services/series.service";
import {Rating} from "../../../_models/rating"; import {Rating} from "../../../_models/rating";
import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe"; 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 {LoadingComponent} from "../../../shared/loading/loading.component";
import {LibraryType} from "../../../_models/library/library"; import {LibraryType} from "../../../_models/library/library";
import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe"; import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe";
@ -22,13 +22,13 @@ import {TranslocoDirective} from "@jsverse/transloco";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
import {ImageService} from "../../../_services/image.service"; import {ImageService} from "../../../_services/image.service";
import {AsyncPipe, NgOptimizedImage, NgTemplateOutlet} from "@angular/common"; import {AsyncPipe, NgOptimizedImage, NgTemplateOutlet} from "@angular/common";
import {InviteUserComponent} from "../../../admin/invite-user/invite-user.component";
import {RatingModalComponent} from "../rating-modal/rating-modal.component"; import {RatingModalComponent} from "../rating-modal/rating-modal.component";
@Component({ @Component({
selector: 'app-external-rating', selector: 'app-external-rating',
standalone: true, 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', templateUrl: './external-rating.component.html',
styleUrls: ['./external-rating.component.scss'], styleUrls: ['./external-rating.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,

View File

@ -15,9 +15,7 @@ import {ImageService} from "../../../_services/image.service";
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service"; import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison"; import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
import {FilterField} from "../../../_models/metadata/v2/filter-field"; import {FilterField} from "../../../_models/metadata/v2/filter-field";
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
import {MangaFormat} from "../../../_models/manga-format"; import {MangaFormat} from "../../../_models/manga-format";
import {MangaFormatIconPipe} from "../../../_pipes/manga-format-icon.pipe";
import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component"; import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flipper/publisher-flipper.component"; import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flipper/publisher-flipper.component";
@ -32,8 +30,6 @@ import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flip
NgbTooltip, NgbTooltip,
TranslocoDirective, TranslocoDirective,
ImageComponent, ImageComponent,
MangaFormatPipe,
MangaFormatIconPipe,
SeriesFormatComponent, SeriesFormatComponent,
PublisherFlipperComponent PublisherFlipperComponent
], ],

View File

@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component, ContentChild, inject, Input, TemplateRef} from '@angular/core'; 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 {A11yClickDirective} from "../../../shared/a11y-click.directive";
import {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component"; import {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component";
import {TagBadgeComponent, TagBadgeCursor} from "../../../shared/tag-badge/tag-badge.component"; import {TagBadgeComponent, TagBadgeCursor} from "../../../shared/tag-badge/tag-badge.component";

View File

@ -1,11 +1,8 @@
import { import {
AsyncPipe, AsyncPipe,
DecimalPipe,
DOCUMENT, DOCUMENT,
JsonPipe,
Location, Location,
NgClass, NgClass,
NgOptimizedImage,
NgStyle, NgStyle,
NgTemplateOutlet NgTemplateOutlet
} from '@angular/common'; } from '@angular/common';
@ -37,7 +34,6 @@ import {
NgbNavItem, NgbNavItem,
NgbNavLink, NgbNavLink,
NgbNavOutlet, NgbNavOutlet,
NgbProgressbar,
NgbTooltip NgbTooltip
} from '@ng-bootstrap/ng-bootstrap'; } from '@ng-bootstrap/ng-bootstrap';
import {ToastrService} from 'ngx-toastr'; import {ToastrService} from 'ngx-toastr';
@ -81,30 +77,20 @@ import {
import {PageLayoutMode} from 'src/app/_models/page-layout-mode'; import {PageLayoutMode} from 'src/app/_models/page-layout-mode';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {UserReview} from "../../../_single-module/review-card/user-review"; 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 {ExternalSeriesCardComponent} from '../../../cards/external-series-card/external-series-card.component';
import {SeriesCardComponent} from '../../../cards/series-card/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 {VirtualScrollerModule} from '@iharbeck/ngx-virtual-scroller';
import {BulkOperationsComponent} from '../../../cards/bulk-operations/bulk-operations.component'; import {BulkOperationsComponent} from '../../../cards/bulk-operations/bulk-operations.component';
import {ReviewCardComponent} from '../../../_single-module/review-card/review-card.component'; import {ReviewCardComponent} from '../../../_single-module/review-card/review-card.component';
import {CarouselReelComponent} from '../../../carousel/_components/carousel-reel/carousel-reel.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 {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component"; import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
import {PublicationStatus} from "../../../_models/metadata/publication-status"; import {PublicationStatus} from "../../../_models/metadata/publication-status";
import {NextExpectedChapter} from "../../../_models/series-detail/next-expected-chapter"; import {NextExpectedChapter} from "../../../_models/series-detail/next-expected-chapter";
import {NextExpectedCardComponent} from "../../../cards/next-expected-card/next-expected-card.component"; 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 {MetadataService} from "../../../_services/metadata.service";
import {Rating} from "../../../_models/rating"; import {Rating} from "../../../_models/rating";
import {ThemeService} from "../../../_services/theme.service"; 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 {DetailsTabComponent} from "../../../_single-module/details-tab/details-tab.component";
import { import {
EditChapterModalCloseResult, EditChapterModalCloseResult,
@ -116,23 +102,14 @@ import {VolumeCardComponent} from "../../../cards/volume-card/volume-card.compon
import {SettingsTabId} from "../../../sidenav/preference-nav/preference-nav.component"; import {SettingsTabId} from "../../../sidenav/preference-nav/preference-nav.component";
import {FilterField} from "../../../_models/metadata/v2/filter-field"; import {FilterField} from "../../../_models/metadata/v2/filter-field";
import {AgeRating} from "../../../_models/metadata/age-rating"; import {AgeRating} from "../../../_models/metadata/age-rating";
import {AgeRatingPipe} from "../../../_pipes/age-rating.pipe";
import {DefaultValuePipe} from "../../../_pipes/default-value.pipe"; import {DefaultValuePipe} from "../../../_pipes/default-value.pipe";
import {ExternalRatingComponent} from "../external-rating/external-rating.component"; import {ExternalRatingComponent} from "../external-rating/external-rating.component";
import {ReadMoreComponent} from "../../../shared/read-more/read-more.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 {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service"; 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 {BadgeExpanderComponent} from "../../../shared/badge-expander/badge-expander.component";
import {A11yClickDirective} from "../../../shared/a11y-click.directive";
import {ScrobblingService} from "../../../_services/scrobbling.service"; import {ScrobblingService} from "../../../_services/scrobbling.service";
import {HourEstimateRange} from "../../../_models/series-detail/hour-estimate-range"; 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 {PublicationStatusPipe} from "../../../_pipes/publication-status.pipe";
import {MetadataDetailRowComponent} from "../metadata-detail-row/metadata-detail-row.component"; import {MetadataDetailRowComponent} from "../metadata-detail-row/metadata-detail-row.component";
import {DownloadButtonComponent} from "../download-button/download-button.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 {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-modules/related-tab/related-tab.component";
import {CollectionTagService} from "../../../_services/collection-tag.service"; import {CollectionTagService} from "../../../_services/collection-tag.service";
import {UserCollection} from "../../../_models/collection-tag"; 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 {CoverImageComponent} from "../../../_single-module/cover-image/cover-image.component";
import {DefaultModalOptions} from "../../../_models/default-modal-options"; import {DefaultModalOptions} from "../../../_models/default-modal-options";
@ -171,16 +146,14 @@ interface StoryLineItem {
styleUrls: ['./series-detail.component.scss'], styleUrls: ['./series-detail.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
imports: [SideNavCompanionBarComponent, CardActionablesComponent, ReactiveFormsModule, NgStyle, imports: [CardActionablesComponent, ReactiveFormsModule, NgStyle,
TagBadgeComponent, ImageComponent, NgbTooltip, NgbProgressbar, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbTooltip, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu,
NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent, NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent,
NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, CardItemComponent, NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent,
LoadingComponent, DecimalPipe, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent, NgClass, AsyncPipe, DetailsTabComponent, ChapterCardComponent,
NgClass, NgOptimizedImage, ProviderImagePipe, AsyncPipe, PersonBadgeComponent, DetailsTabComponent, ChapterCardComponent, VolumeCardComponent, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, RouterLink, BadgeExpanderComponent,
VolumeCardComponent, JsonPipe, AgeRatingPipe, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, ReadTimePipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, CoverImageComponent]
RouterLink, TimeAgoPipe, AgeRatingImageComponent, CompactNumberPipe, IconAndTitleComponent, SafeHtmlPipe, BadgeExpanderComponent,
A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, SeriesFormatComponent, MangaFormatPipe, CoverImageComponent]
}) })
export class SeriesDetailComponent implements OnInit, AfterContentChecked { 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) { onNavChange(event: NgbNavChangeEvent) {
this.bulkSelectionService.deselectAll(); this.bulkSelectionService.deselectAll();

View File

@ -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() { constructor() {
this.pageOperationsForm.get('accessibilityMode')?.valueChanges.pipe( this.pageOperationsForm.get('accessibilityMode')?.valueChanges.pipe(

View File

@ -14,36 +14,40 @@
<div [ngStyle]="{'height': ScrollingBlockHeight}" class="main-container container-fluid ps-0" #scrollingBlock> <div [ngStyle]="{'height': ScrollingBlockHeight}" class="main-container container-fluid ps-0" #scrollingBlock>
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations> <app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
<app-card-detail-layout *ngIf="filter" @if (filter) {
[isLoading]="isLoading" <app-card-detail-layout [isLoading]="isLoading"
[items]="series" [items]="series"
[pagination]="pagination" [pagination]="pagination"
[filterSettings]="filterSettings" [filterSettings]="filterSettings"
[filterOpen]="filterOpen" [filterOpen]="filterOpen"
[jumpBarKeys]="jumpbarKeys" [jumpBarKeys]="jumpbarKeys"
[trackByIdentity]="trackByIdentity" [trackByIdentity]="trackByIdentity"
[refresh]="refresh" [refresh]="refresh"
(applyFilter)="updateFilter($event)"> (applyFilter)="updateFilter($event)">
<ng-template #cardItem let-item let-position="idx"> <ng-template #cardItem let-item let-position="idx">
<app-series-card [series]="item" [libraryId]="item.libraryId" (reload)="removeSeries($event)" <app-series-card [series]="item" [libraryId]="item.libraryId" (reload)="removeSeries($event)"
(selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)"
[selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"
></app-series-card> ></app-series-card>
</ng-template> </ng-template>
<div *ngIf="!filterActive && series.length === 0"> @if (!filterActive && series.length === 0) {
<ng-template #noData> <div>
{{t('no-items')}} <ng-template #noData>
</ng-template> {{t('no-items')}}
</ng-template>
</div> </div>
} @else if (filterActive && series.length === 0) {
<div *ngIf="filterActive && series.length === 0"> <div>
<ng-template #noData> <ng-template #noData>
{{t('no-items-filtered')}} {{t('no-items-filtered')}}
</ng-template> </ng-template>
</div> </div>
}
</app-card-detail-layout> </app-card-detail-layout>
}
</div> </div>
</ng-container> </ng-container>
</div> </div>

View File

@ -46,7 +46,7 @@ import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
styleUrls: ['./want-to-read.component.scss'], styleUrls: ['./want-to-read.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, 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 { export class WantToReadComponent implements OnInit, AfterContentChecked {
@ -145,20 +145,6 @@ export class WantToReadComponent implements OnInit, AfterContentChecked {
this.scrollService.setScrollContainer(this.scrollingBlock); 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) { removeSeries(seriesId: number) {
this.series = this.series.filter(s => s.id != seriesId); this.series = this.series.filter(s => s.id != seriesId);
this.pagination.totalItems--; this.pagination.totalItems--;