Angular 15 (#1764)

* Updated ngx-virtual-scroller

* Removed the karma test config as it's breaking migration

* Reverted to pre angular 15

* Upgraded packages and reverted target to ES6 for older devices

* It's broken. Need to also find the safari version for old Ipads

* Fixes some code in default pipe and many updates to packages. Removed support for old iOS versions as it restricted Kavita from using newer features. Build still broken.

* More progress in getting build working on Angular 15. Removed polyfills.ts for new angular config

* Remove all.css for icons and use scss instead

* Removed stuff that isn't needed

* Migrated extended linting to eslint, ran on project and updated issues. Removed a duplicate component that did nothing. Fixed a few places where lifecycle hooks werent being called as interface wasn't implemented.

* App builds correctly. Source maps are still needed.

* Fixed source maps and removed more testing stuff. I will re-add later in another release when I figure out how to properly tackle dependencies on backend.

* Reverted back to old source map definition
This commit is contained in:
Joe Milazzo 2023-01-30 06:27:52 -08:00 committed by GitHub
parent 12d0ae6f2c
commit f64f232e51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 7393 additions and 7937 deletions

View file

@ -1 +0,0 @@
<p>devices works!</p>

View file

@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-devices',
templateUrl: './devices.component.html',
styleUrls: ['./devices.component.scss']
})
export class DevicesComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View file

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Member } from 'src/app/_models/auth/member';
@ -9,7 +9,7 @@ import { AccountService } from 'src/app/_services/account.service';
templateUrl: './reset-password-modal.component.html',
styleUrls: ['./reset-password-modal.component.scss']
})
export class ResetPasswordModalComponent implements OnInit {
export class ResetPasswordModalComponent {
@Input() member!: Member;
errorMessage = '';
@ -19,9 +19,6 @@ export class ResetPasswordModalComponent implements OnInit {
constructor(public modal: NgbActiveModal, private accountService: AccountService) { }
ngOnInit(): void {
}
save() {
this.accountService.resetPassword(this.member.username, this.resetPasswordForm.value.password,'').subscribe(() => {
this.modal.close();

View file

@ -19,7 +19,7 @@
<div>Shared Folders: {{library.folders.length + ' folders'}}</div>
<div>
Last Scanned:
<span *ngIf="library.lastScanned == '0001-01-01T00:00:00'; else activeDate">Never</span>
<span *ngIf="library.lastScanned === '0001-01-01T00:00:00'; else activeDate">Never</span>
<ng-template #activeDate>
{{library.lastScanned | timeAgo}}
</ng-template>

View file

@ -53,7 +53,7 @@
</h4>
<div class="user-info">
<div>Last Active:
<span *ngIf="member.lastActive == '0001-01-01T00:00:00'; else activeDate">Never</span>
<span *ngIf="member.lastActive === '0001-01-01T00:00:00'; else activeDate">Never</span>
<ng-template #activeDate>
{{member.lastActive | date: 'short'}}
</ng-template>

View file

@ -1,15 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-announcements',
templateUrl: './announcements.component.html',
styleUrls: ['./announcements.component.scss']
})
export class AnnouncementsComponent implements OnInit {
export class AnnouncementsComponent {
constructor() { }
ngOnInit(): void {
}
}

View file

@ -3,9 +3,9 @@
<div [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'content-wrapper': navService.sideNavVisibility$ | async}">
<a id="content"></a>
<app-side-nav *ngIf="navService.sideNavVisibility$ | async as sideNavVisibile"></app-side-nav>
<div class="container-fluid" [ngClass]="{'g-0': !(navService.sideNavVisibility$ | async)}">
<div class="container-fluid" [ngClass]="{'g-0': (navService.sideNavVisibility$ | async) === false}">
<div style="padding: 20px 0 0;" *ngIf="navService.sideNavVisibility$ | async else noSideNav">
<div class="companion-bar" [ngClass]="{'companion-bar-content': !(navService.sideNavCollapsed$ | async)}">
<div class="companion-bar" [ngClass]="{'companion-bar-content': (navService.sideNavCollapsed$ | async) === false}">
<router-outlet></router-outlet>
</div>
</div>

View file

@ -1,6 +1,6 @@
import { Component, HostListener, Inject, OnInit } from '@angular/core';
import { NavigationStart, Router } from '@angular/router';
import { distinctUntilChanged, map, take } from 'rxjs/operators';
import { map, take } from 'rxjs/operators';
import { AccountService } from './_services/account.service';
import { LibraryService } from './_services/library.service';
import { MessageHubService } from './_services/message-hub.service';
@ -8,7 +8,6 @@ import { NavService } from './_services/nav.service';
import { filter } from 'rxjs/operators';
import { NgbModal, NgbRatingConfig } from '@ng-bootstrap/ng-bootstrap';
import { DOCUMENT } from '@angular/common';
import { DeviceService } from './_services/device.service';
import { Observable } from 'rxjs';
@Component({

View file

@ -12,7 +12,6 @@ import { ErrorInterceptor } from './_interceptors/error.interceptor';
import { SAVER, getSaver } from './shared/_providers/saver.provider';
import { SidenavModule } from './sidenav/sidenav.module';
import { NavModule } from './nav/nav.module';
import { DevicesComponent } from './_components/devices/devices.component';
@ -24,7 +23,6 @@ if (disableAnimations) console.error("Web Animations have been disabled as your
@NgModule({
declarations: [
AppComponent,
DevicesComponent,
],
imports: [
HttpClientModule,

View file

@ -11,7 +11,7 @@
</div>
<ng-template #nestedChildren>
<ul *ngFor="let chapterGroup of chapters" class="chapter-title">
<li class="{{chapterGroup.page == pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
<li class="{{chapterGroup.page === pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
{{chapterGroup.title}}
</li>
<ul *ngFor="let chapter of chapterGroup.children">

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { BookChapterItem } from '../../_models/book-chapter-item';
@ -8,7 +8,7 @@ import { BookChapterItem } from '../../_models/book-chapter-item';
styleUrls: ['./table-of-contents.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class TableOfContentsComponent implements OnInit, OnDestroy {
export class TableOfContentsComponent implements OnDestroy {
@Input() chapterId!: number;
@Input() pageNum!: number;
@ -23,9 +23,6 @@ export class TableOfContentsComponent implements OnInit, OnDestroy {
constructor() {}
ngOnInit(): void {
}
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
@ -13,7 +13,7 @@ import { CollectionTagService } from 'src/app/_services/collection-tag.service';
styleUrls: ['./bulk-add-to-collection.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BulkAddToCollectionComponent implements OnInit {
export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
@Input() title!: string;
/**

View file

@ -176,10 +176,7 @@ export class BulkSelectionService {
if (action.children === null || action.children?.length === 0) return ret;
action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));
// action.children?.forEach((childAction) => {
// this.applyFilter(childAction, allowedActions);
// });
return ret;
}

View file

@ -95,7 +95,7 @@
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Cover]" [disabled]="!(isAdmin$ | async)">
<li [ngbNavItem]="tabs[TabID.Cover]" [disabled]="(isAdmin$ | async) === false">
<a ngbNavLink>{{tabs[TabID.Cover].title}}</a>
<ng-template ngbNavContent>
<app-cover-image-chooser [(imageUrls)]="imageUrls"
@ -108,7 +108,7 @@
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Files]" [disabled]="!(isAdmin$ | async)">
<li [ngbNavItem]="tabs[TabID.Files]" [disabled]="(isAdmin$ | async) === false">
<a ngbNavLink>{{tabs[TabID.Files].title}}</a>
<ng-template ngbNavContent>
<h4 *ngIf="!utilityService.isChapter(data)">{{utilityService.formatChapterName(libraryType) + 's'}}</h4>
@ -145,7 +145,7 @@
<div class="col" *ngIf="data.hasOwnProperty('created')">
Added:
<!-- TODO: This data.created can be removed after v0.5.5 release -->
<ng-container *ngIf="file.created == '0001-01-01T00:00:00'; else fileDate">
<ng-container *ngIf="file.created === '0001-01-01T00:00:00'; else fileDate">
{{data.created | date: 'short' | defaultDate}}
</ng-container>
<ng-template #fileDate>

View file

@ -7,7 +7,7 @@
</span>
<span *ngIf="header !== undefined && header.length > 0">
{{header}}&nbsp;
<span class="badge bg-primary rounded-pill" attr.aria-label="{{pagination.totalItems}} total items" *ngIf="pagination != undefined">{{pagination.totalItems}}</span>
<span class="badge bg-primary rounded-pill" attr.aria-label="{{pagination.totalItems}} total items" *ngIf="pagination !== undefined">{{pagination.totalItems}}</span>
</span>
</h2>
</div>

View file

@ -9,9 +9,9 @@
<ng-template #submenu let-list="list">
<ng-container *ngFor="let action of list">
<!-- Non Submenu items -->
<ng-container *ngIf="action.children === undefined || action?.children?.length === 0 || action.dynamicList != undefined ; else submenuDropdown">
<ng-container *ngIf="action.children === undefined || action?.children?.length === 0 || action.dynamicList !== undefined ; else submenuDropdown">
<ng-container *ngIf="action.dynamicList != undefined && (action.dynamicList | async | dynamicList) as dList; else justItem">
<ng-container *ngIf="action.dynamicList !== undefined && (action.dynamicList | async | dynamicList) as dList; else justItem">
<ng-container *ngFor="let dynamicItem of dList">
<button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button>
</ng-container>

View file

@ -41,7 +41,7 @@
<span class="visually-hidden">(promoted)</span>
</span>
<ng-container *ngIf="format | mangaFormat as formatString">
<i class="fa {{format | mangaFormatIcon}} me-1" aria-hidden="true" *ngIf="format != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
<i class="fa {{format | mangaFormatIcon}} me-1" aria-hidden="true" *ngIf="format !== MangaFormat.UNKNOWN" title="{{formatString}}"></i>
<span class="visually-hidden">{{formatString}}</span>
</ng-container>
{{title}}

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
@Component({
@ -7,10 +7,8 @@ import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
styleUrls: ['./chapter-metadata-detail.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChapterMetadataDetailComponent implements OnInit {
export class ChapterMetadataDetailComponent {
@Input() chapter: ChapterMetadata | undefined;
constructor() { }
ngOnInit(): void {}
}

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Observable } from 'rxjs';
import { Download } from 'src/app/shared/_models/download';
import { DownloadEvent } from 'src/app/shared/_services/download.service';
@ -9,16 +9,12 @@ import { DownloadEvent } from 'src/app/shared/_services/download.service';
styleUrls: ['./download-indicator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DownloadIndicatorComponent implements OnInit {
export class DownloadIndicatorComponent {
/**
* Observable that represents when the download completes
*/
@Input() download$!: Observable<Download | DownloadEvent | null> | null;
constructor(private readonly cdRef: ChangeDetectorRef) { }
ngOnInit(): void {
}
constructor() { }
}

View file

@ -1,26 +1,26 @@
<ng-container [ngSwitch]="libraryType">
<ng-container *ngSwitchCase="LibraryType.Comic">
<ng-container *ngIf="titleName != '' && prioritizeTitleName; else fullComicTitle">
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullComicTitle">
{{titleName}}
</ng-container>
<ng-template #fullComicTitle>
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
<ng-container *ngIf="includeVolume && volumeTitle != ''">
{{entity.number != 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
</ng-container>
{{entity.number != 0 ? (isChapter ? 'Issue #' + entity.number : volumeTitle) : 'Special'}}
{{entity.number !== 0 ? (isChapter ? 'Issue #' + entity.number : volumeTitle) : 'Special'}}
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="LibraryType.Manga">
<ng-container *ngIf="titleName != '' && prioritizeTitleName; else fullMangaTitle">
<ng-container *ngIf="titleName !== '' && prioritizeTitleName; else fullMangaTitle">
{{titleName}}
</ng-container>
<ng-template #fullMangaTitle>
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
<ng-container *ngIf="includeVolume && volumeTitle != ''">
{{entity.number != 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
</ng-container>
{{entity.number != 0 ? (isChapter ? 'Chapter ' + entity.number : volumeTitle) : 'Special'}}
{{entity.number !== 0 ? (isChapter ? 'Chapter ' + entity.number : volumeTitle) : 'Special'}}
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="LibraryType.Book">

View file

@ -22,7 +22,7 @@
</button>
</h5>
<!-- This isn't perfect, but it might work. TODO: Polish this-->
<h6 class="text-muted" [ngClass]="{'subtitle-with-actionables' : actions.length > 0}" *ngIf="Title != '' && showTitle">{{Title}}</h6>
<h6 class="text-muted" [ngClass]="{'subtitle-with-actionables' : actions.length > 0}" *ngIf="Title !== '' && showTitle">{{Title}}</h6>
<ng-container *ngIf="summary.length > 0">
<div class="mt-2 ps-2">
<app-read-more [text]="summary" [blur]="pagesRead === 0 && blur" [maxLength]="250"></app-read-more>

View file

@ -19,7 +19,7 @@ import { ManagaReaderService } from '../../_series/managa-reader.service';
styleUrls: ['./double-no-cover-renderer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DoubleNoCoverRendererComponent implements OnInit {
export class DoubleNoCoverRendererComponent implements OnInit, OnDestroy {
@Input() readerSettings$!: Observable<ReaderSetting>;
@Input() image$!: Observable<HTMLImageElement | null>;

View file

@ -117,7 +117,7 @@
<div class="fixed-bottom overlay" *ngIf="menuOpen" [@slideFromBottom]="menuOpen">
<div class="mb-3" *ngIf="pageOptions != undefined && pageOptions.ceil != undefined">
<div class="mb-3" *ngIf="pageOptions !== undefined && pageOptions.ceil !== undefined">
<span class="visually-hidden" id="slider-info"></span>
<div class="row g-0">
<button class="btn btn-icon col-1" [disabled]="prevChapterDisabled" (click)="loadPrevChapter();resetMenuCloseTimer();" title="Prev Chapter/Volume"><i class="fa fa-fast-backward" aria-hidden="true"></i></button>

View file

@ -2,7 +2,7 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, E
import { DOCUMENT } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject, debounceTime, distinctUntilChanged, forkJoin, fromEvent, map, merge, Observable, ReplaySubject, Subject, take, takeUntil, tap } from 'rxjs';
import { LabelType, ChangeContext, Options } from '@angular-slider/ngx-slider';
import { LabelType, ChangeContext, Options } from 'ngx-slider-v2';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

View file

@ -4,7 +4,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { MangaReaderRoutingModule } from './manga-reader.router.module';
import { SharedModule } from '../shared/shared.module';
import { NgxSliderModule } from '@angular-slider/ngx-slider';
import { NgxSliderModule } from 'ngx-slider-v2';
import { InfiniteScrollerComponent } from './_components/infinite-scroller/infinite-scroller.component';
import { ReaderSharedModule } from '../reader-shared/reader-shared.module';
import { PipeModule } from '../pipe/pipe.module';

View file

@ -1,5 +1,5 @@
<form [formGroup]="typeaheadForm" class="grouped-typeahead">
<div class="typeahead-input" [ngClass]="{'focused': hasFocus == true}" (click)="onInputFocus($event)">
<div class="typeahead-input" [ngClass]="{'focused': hasFocus}" (click)="onInputFocus($event)">
<div class="search">
<input #input [id]="id" type="text" autocomplete="off" formControlName="typeahead" [placeholder]="placeholder"
aria-haspopup="listbox" aria-owns="dropdown" aria-expanded="hasFocus && (grouppedData.persons.length || grouppedData.collections.length || grouppedData.series.length || grouppedData.persons.length || grouppedData.tags.length || grouppedData.genres.length)"

View file

@ -6,7 +6,7 @@ import { Pipe, PipeTransform } from '@angular/core';
export class DefaultValuePipe implements PipeTransform {
transform(value: any, replacementString = '—'): string {
if (value === null || value === undefined || value === '' || value === Infinity || value === NaN) return replacementString;
if (value === null || value === undefined || value === '' || value === Infinity || Number.isNaN(value)) return replacementString;
return value;
}

View file

@ -1,5 +1,5 @@
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
export interface IndexUpdateEvent {
fromPosition: number;
@ -18,7 +18,7 @@ export interface ItemRemoveEvent {
styleUrls: ['./draggable-ordered-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DraggableOrderedListComponent implements OnInit {
export class DraggableOrderedListComponent {
@Input() accessibilityMode: boolean = false;
/**
@ -32,9 +32,6 @@ export class DraggableOrderedListComponent implements OnInit {
constructor(private readonly cdRef: ChangeDetectorRef) { }
ngOnInit(): void {
}
drop(event: CdkDragDrop<string[]>) {
if (event.previousIndex === event.currentIndex) return;
moveItemInArray(this.items, event.previousIndex, event.currentIndex);

View file

@ -29,7 +29,7 @@
</h5>
<div class="ps-1 d-none d-md-inline-block">
<ng-container *ngIf="item.seriesFormat | mangaFormat as formatString">
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat !== MangaFormat.UNKNOWN" title="{{formatString}}"></i>
<span class="visually-hidden">{{formatString}}</span>&nbsp;
</ng-container>

View file

@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UtilityService } from 'src/app/shared/_services/utility.service';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { LibraryType } from 'src/app/_models/library';
import { MangaFormat } from 'src/app/_models/manga-format';
import { ReadingListItem } from 'src/app/_models/reading-list';
@ -11,7 +10,7 @@ import { ImageService } from 'src/app/_services/image.service';
styleUrls: ['./reading-list-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ReadingListItemComponent implements OnInit {
export class ReadingListItemComponent {
@Input() item!: ReadingListItem;
@Input() position: number = 0;
@ -28,10 +27,7 @@ export class ReadingListItemComponent implements OnInit {
return MangaFormat;
}
constructor(public imageService: ImageService, private utilityService: UtilityService,
private readonly cdRef: ChangeDetectorRef) { }
ngOnInit(): void {}
constructor(public imageService: ImageService) { }
readChapter(item: ReadingListItem) {
this.read.emit(item);

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
@ -15,7 +15,7 @@ import { MemberService } from 'src/app/_services/member.service';
styleUrls: ['./register.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RegisterComponent implements OnInit {
export class RegisterComponent {
registerForm: FormGroup = new FormGroup({
email: new FormControl('', [Validators.email]),
@ -34,9 +34,6 @@ export class RegisterComponent implements OnInit {
});
}
ngOnInit(): void {
}
submit() {
const model = this.registerForm.getRawValue();
this.accountService.register(model).subscribe((user) => {

View file

@ -138,7 +138,7 @@
<div class="card-container row g-0" #container>
<ng-container *ngFor="let item of scroll.viewPortItems; let idx = index; trackBy: trackByStoryLineIdentity">
<ng-container *ngIf="!item.isChapter; else chapterCardItem">
<app-card-item class="col-auto mt-2 mb-2" *ngIf="item.volume.number != 0" [entity]="item.volume" [title]="item.volume.name" (click)="openVolume(item.volume)"
<app-card-item class="col-auto mt-2 mb-2" *ngIf="item.volume.number !== 0" [entity]="item.volume" [title]="item.volume.name" (click)="openVolume(item.volume)"
[imageUrl]="imageService.getVolumeCoverImage(item.volume.id)"
[read]="item.volume.pagesRead" [total]="item.volume.pages" [actions]="volumeActions"
(selection)="bulkSelectionService.handleCardSelection('volume', scroll.viewPortInfo.startIndexWithBuffer + idx, volumes.length, $event)"
@ -159,7 +159,7 @@
<ng-container *ngFor="let item of scroll.viewPortItems; let idx = index; trackBy: trackByStoryLineIdentity">
<ng-container *ngIf="!item.isChapter; else chapterListItem">
<app-list-item [imageUrl]="imageService.getVolumeCoverImage(item.volume.id)"
[seriesName]="series.name" [entity]="item.volume" *ngIf="item.volume.number != 0"
[seriesName]="series.name" [entity]="item.volume" *ngIf="item.volume.number !== 0"
[actions]="volumeActions" [libraryType]="libraryType" imageWidth="130px" imageHeight=""
[pagesRead]="item.volume.pagesRead" [totalPages]="item.volume.pages" (read)="openVolume(item.volume)"
[blur]="user?.preferences?.blurUnreadSummaries || false">

View file

@ -1,9 +1,10 @@
import { Directive, Input, HostListener, OnInit, ElementRef, Inject } from '@angular/core';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[a11y-click]'
})
export class A11yClickDirective {
export class A11yClickDirective implements OnInit {
@Input('a11y-click') keyCodes!: string;
keyCodeArray!: string[];

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'app-loading',
@ -6,7 +6,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit }
styleUrls: ['./loading.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LoadingComponent implements OnInit {
export class LoadingComponent {
@Input() loading: boolean = false;
@Input() message: string = '';
@ -15,10 +15,5 @@ export class LoadingComponent implements OnInit {
*/
@Input() absolute: boolean = false;
constructor(private readonly cdRef: ChangeDetectorRef) { }
ngOnInit(): void {
console.log('absolute: ', this.absolute);
}
constructor() { }
}

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Person } from '../../_models/metadata/person';
@Component({
@ -7,13 +7,9 @@ import { Person } from '../../_models/metadata/person';
styleUrls: ['./person-badge.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonBadgeComponent implements OnInit {
export class PersonBadgeComponent {
@Input() person!: Person;
constructor() { }
ngOnInit(): void {
}
}

View file

@ -1,4 +1,4 @@
<ng-container *ngIf="format != MangaFormat.UNKNOWN">
<ng-container *ngIf="format !== MangaFormat.UNKNOWN">
<i class="fa {{format | mangaFormatIcon}}" aria-hidden="true" title="{{format | mangaFormat}}"></i>&nbsp;
<ng-content></ng-content>
</ng-container>

View file

@ -15,7 +15,7 @@
<div class="active-highlight"></div>
<span class="phone-hidden" title="{{title}}">
<div>
<ng-container *ngIf="imageUrl != null && imageUrl != ''; else iconImg">
<ng-container *ngIf="imageUrl !== null && imageUrl !== ''; else iconImg">
<img [src]="imageUrl" alt="icon" class="side-nav-img">
</ng-container>
<ng-template #iconImg><i class="fa {{icon}}" aria-hidden="true"></i></ng-template>

View file

@ -1,5 +1,5 @@
<ng-container>
<div class="side-nav" [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'hidden' :!(navService.sideNavVisibility$ | async)}" *ngIf="accountService.currentUser$ | async as user">
<div class="side-nav" [ngClass]="{'closed' : (navService.sideNavCollapsed$ | async), 'hidden': (navService.sideNavVisibility$ | async) === false}" *ngIf="accountService.currentUser$ | async as user">
<!-- <app-side-nav-item icon="fa-user-circle align-self-center phone-hidden" [title]="user.username | sentenceCase" link="/preferences/">
<ng-container actions>
Todo: This will be customize dashboard/side nav controls
@ -13,7 +13,7 @@
<app-side-nav-item icon="fa-list-ol" title="Reading Lists" link="/lists/"></app-side-nav-item>
<app-side-nav-item icon="fa-bookmark" title="Bookmarks" link="/bookmarks/"></app-side-nav-item>
<app-side-nav-item icon="fa-regular fa-rectangle-list" title="All Series" link="/all-series/" *ngIf="libraries.length > 0"></app-side-nav-item>
<div class="mb-2 mt-3 ms-2 me-2" *ngIf="libraries.length > 10 && !(navService?.sideNavCollapsed$ | async)">
<div class="mb-2 mt-3 ms-2 me-2" *ngIf="libraries.length > 10 && (navService?.sideNavCollapsed$ | async) === false">
<label for="filter" class="form-label visually-hidden">Filter</label>
<div class="form-group">
<input id="filter" autocomplete="off" class="form-control" [(ngModel)]="filterQuery" type="text" aria-describedby="reset-input">

View file

@ -40,7 +40,7 @@
</div>
<div *ngIf="!isAddLibrary">
Last Scanned:
<span *ngIf="library.lastScanned == '0001-01-01T00:00:00'; else activeDate">Never</span>
<span *ngIf="library.lastScanned === '0001-01-01T00:00:00'; else activeDate">Never</span>
<ng-template #activeDate>
{{library.lastScanned | date: 'short'}}
</ng-template>
@ -157,7 +157,7 @@
<button type="button" class="btn btn-light" (click)="reset()">Reset</button>
<button type="button" class="btn btn-secondary" (click)="close()">Cancel</button>
<ng-container *ngIf="isAddLibrary && setupStep != 3; else editLibraryButton">
<ng-container *ngIf="isAddLibrary && setupStep !== 3; else editLibraryButton">
<button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="isNextDisabled() || libraryForm.invalid">Next</button>
</ng-container>
<ng-template #editLibraryButton>

View file

@ -1,9 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy } from '@angular/core';
import { FormControl } from '@angular/forms';
import { LegendPosition } from '@swimlane/ngx-charts';
import { Subject, combineLatest, map, takeUntil, Observable } from 'rxjs';
import { Subject, map, takeUntil, Observable } from 'rxjs';
import { DayOfWeek, StatisticsService } from 'src/app/_services/statistics.service';
import { compare } from 'src/app/_single-module/table/_directives/sortable-header.directive';
import { PieDataItem } from '../../_models/pie-data-item';
import { StatCount } from '../../_models/stat-count';
import { DayOfWeekPipe } from '../../_pipes/day-of-week.pipe';
@ -13,7 +12,7 @@ import { DayOfWeekPipe } from '../../_pipes/day-of-week.pipe';
templateUrl: './day-breakdown.component.html',
styleUrls: ['./day-breakdown.component.scss']
})
export class DayBreakdownComponent implements OnInit {
export class DayBreakdownComponent implements OnDestroy {
private readonly onDestroy = new Subject<void>();
@ -42,11 +41,6 @@ export class DayBreakdownComponent implements OnInit {
);
}
ngOnInit(): void {
this.onDestroy.next();
this.onDestroy.complete();
}
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
import { FormControl } from '@angular/forms';
import { LegendPosition } from '@swimlane/ngx-charts';
import { Observable, Subject, map, takeUntil, combineLatest, BehaviorSubject } from 'rxjs';
@ -12,7 +12,7 @@ import { PieDataItem } from '../../_models/pie-data-item';
styleUrls: ['./publication-status-stats.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PublicationStatusStatsComponent implements OnInit {
export class PublicationStatusStatsComponent implements OnDestroy {
@ViewChildren(SortableHeader<PieDataItem>) headers!: QueryList<SortableHeader<PieDataItem>>;
@ -48,11 +48,6 @@ export class PublicationStatusStatsComponent implements OnInit {
);
}
ngOnInit(): void {
this.onDestroy.next();
this.onDestroy.complete();
}
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();

View file

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, HostListener, OnDestroy, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, HostListener, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { BehaviorSubject, map, Observable, of, shareReplay, Subject, takeUntil, tap } from 'rxjs';
import { BehaviorSubject, map, Observable, shareReplay, Subject, takeUntil } from 'rxjs';
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
import { Series } from 'src/app/_models/series';
@ -18,7 +18,7 @@ import { GenericListModalComponent } from '../_modals/generic-list-modal/generic
styleUrls: ['./server-stats.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ServerStatsComponent implements OnInit, OnDestroy {
export class ServerStatsComponent implements OnDestroy {
releaseYears$!: Observable<Array<PieDataItem>>;
mostActiveUsers$!: Observable<Array<PieDataItem>>;
@ -89,9 +89,6 @@ export class ServerStatsComponent implements OnInit, OnDestroy {
);
}
ngOnInit(): void {
}
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();

View file

@ -5,7 +5,7 @@
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="tooltip" role="button" tabindex="0" *ngIf="description && description.length > 0"></i>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item" [ngClass]="{'underline': handleClick != undefined}" *ngFor="let item of data" (click)="doClick(item)">
<li class="list-group-item" [ngClass]="{'underline': handleClick !== undefined}" *ngFor="let item of data" (click)="doClick(item)">
<ng-container *ngIf="image && image(item) as url">
<app-image *ngIf="url && url.length > 0" width="32px" maxHeight="32px" class="img-top me-1" [imageUrl]="url"></app-image>
</ng-container>

View file

@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { CompactNumberPipe } from 'src/app/pipe/compact-number.pipe';
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
import { StatisticsService } from 'src/app/_services/statistics.service';
import { GenericListModalComponent } from '../_modals/generic-list-modal/generic-list-modal.component';
@ -11,7 +10,7 @@ import { GenericListModalComponent } from '../_modals/generic-list-modal/generic
styleUrls: ['./user-stats-info-cards.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserStatsInfoCardsComponent implements OnInit {
export class UserStatsInfoCardsComponent {
@Input() totalPagesRead: number = 0;
@Input() totalWordsRead: number = 0;
@ -22,9 +21,6 @@ export class UserStatsInfoCardsComponent implements OnInit {
constructor(private statsService: StatisticsService, private modalService: NgbModal) { }
ngOnInit(): void {
}
openPageByYearList() {
const numberPipe = new CompactNumberPipe();
this.statsService.getPagesPerYear().subscribe(yearCounts => {

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit } from '@angular/core';
import { ToastrService } from 'ngx-toastr';
import { Observable, of, Subject, takeUntil, shareReplay, map, take } from 'rxjs';
import { AgeRestriction } from 'src/app/_models/metadata/age-restriction';
@ -12,7 +12,7 @@ import { AccountService } from 'src/app/_services/account.service';
styleUrls: ['./change-age-restriction.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChangeAgeRestrictionComponent implements OnInit {
export class ChangeAgeRestrictionComponent implements OnInit, OnDestroy {
user: User | undefined = undefined;
hasChangeAgeRestrictionAbility: Observable<boolean> = of(false);

View file

@ -19,7 +19,7 @@
<div class="row g-0 mt-2">
<h4>Devices</h4>
<p *ngIf="devices.length == 0">
<p *ngIf="devices.length === 0">
There are no devices setup yet
</p>
<ng-container *ngFor="let device of devices">

View file

@ -12,7 +12,7 @@ import { AccountService } from 'src/app/_services/account.service';
styleUrls: ['./theme-manager.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ThemeManagerComponent implements OnInit, OnDestroy {
export class ThemeManagerComponent implements OnDestroy {
currentTheme: SiteTheme | undefined;
isAdmin: boolean = false;
@ -40,9 +40,6 @@ export class ThemeManagerComponent implements OnInit, OnDestroy {
});
}
ngOnInit(): void {
}
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();

View file

@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject, take, debounceTime, takeUntil } from 'rxjs';
@ -27,7 +27,7 @@ import { SeriesService } from 'src/app/_services/series.service';
styleUrls: ['./want-to-read.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class WantToReadComponent implements OnInit, OnDestroy {
export class WantToReadComponent implements OnInit, OnDestroy, AfterContentChecked {
@ViewChild('scrollingBlock') scrollingBlock: ElementRef<HTMLDivElement> | undefined;
@ViewChild('companionBar') companionBar: ElementRef<HTMLDivElement> | undefined;

View file

@ -1,3 +1,5 @@
/// <reference types="@angular/localize" />
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

View file

@ -1,67 +0,0 @@
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View file

@ -1,4 +1,4 @@
@use '../node_modules/swiper/swiper.scss' as swiper;
@import '../node_modules/swiper/swiper';
// Import themes which define the css variables we use to customize the app
@ -9,7 +9,8 @@
@import './theme/variables';
// Bootstrap must be after _colors since we define the colors there
@import '~bootstrap/scss/bootstrap';
@import '../node_modules/bootstrap/scss/bootstrap';
// Import all the customized theme overrides

View file

@ -1,6 +1,5 @@
/* Default styles for Kavita */
:root {
//@import './dark.scss'; // Just re-import variables from dark since that's all we support
--color-scheme: dark;
--primary-color: #4ac694;
--primary-color-dark-shade: #3B9E76;