Angular 16 (#2007)
* Removed adv, which isn't needed. * Updated zone * Updated to angular 16 * Updated to angular 16 (partially) * Updated to angular 16 * Package update for Angular 16 (and other dependencies) is complete. * Replaced all takeUntil(this.onDestroy) with new takeUntilDestroyed() * Updated all inputs that have ! to be required and deleted all unit tests. * Corrected how takeUntilDestroyed() is supposed to be implemented.
This commit is contained in:
parent
9bc8361381
commit
9c06cccd35
87 changed files with 3964 additions and 20426 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import {DestroyRef, inject, Injectable, OnDestroy} from '@angular/core';
|
||||
import { of, ReplaySubject, Subject } from 'rxjs';
|
||||
import { filter, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
|
@ -14,20 +14,22 @@ import { UpdateEmailResponse } from '../_models/auth/update-email-response';
|
|||
import { AgeRating } from '../_models/metadata/age-rating';
|
||||
import { AgeRestriction } from '../_models/metadata/age-restriction';
|
||||
import { TextResonse } from '../_types/text-response';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
export enum Role {
|
||||
Admin = 'Admin',
|
||||
ChangePassword = 'Change Password',
|
||||
Bookmark = 'Bookmark',
|
||||
Download = 'Download',
|
||||
ChangeRestriction = 'Change Restriction'
|
||||
ChangeRestriction = 'Change Restriction'
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AccountService implements OnDestroy {
|
||||
export class AccountService {
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
baseUrl = environment.apiUrl;
|
||||
userKey = 'kavita-user';
|
||||
public lastLoginKey = 'kavita-lastlogin';
|
||||
|
|
@ -42,21 +44,14 @@ export class AccountService implements OnDestroy {
|
|||
*/
|
||||
private refreshTokenTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
constructor(private httpClient: HttpClient, private router: Router,
|
||||
constructor(private httpClient: HttpClient, private router: Router,
|
||||
private messageHub: MessageHubService, private themeService: ThemeService) {
|
||||
messageHub.messages$.pipe(filter(evt => evt.event === EVENTS.UserUpdate),
|
||||
messageHub.messages$.pipe(filter(evt => evt.event === EVENTS.UserUpdate),
|
||||
map(evt => evt.payload as UserUpdateEvent),
|
||||
filter(userUpdateEvent => userUpdateEvent.userName === this.currentUser?.username),
|
||||
filter(userUpdateEvent => userUpdateEvent.userName === this.currentUser?.username),
|
||||
switchMap(() => this.refreshToken()))
|
||||
.subscribe(() => {});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
hasAdminRole(user: User) {
|
||||
return user && user.roles.includes(Role.Admin);
|
||||
|
|
@ -91,7 +86,7 @@ export class AccountService implements OnDestroy {
|
|||
this.messageHub.createHubConnection(user, this.hasAdminRole(user));
|
||||
}
|
||||
}),
|
||||
takeUntil(this.onDestroy)
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +109,7 @@ export class AccountService implements OnDestroy {
|
|||
|
||||
this.currentUser = user;
|
||||
this.currentUserSource.next(user);
|
||||
|
||||
|
||||
this.stopRefreshTokenTimer();
|
||||
|
||||
if (this.currentUser !== undefined) {
|
||||
|
|
@ -135,15 +130,15 @@ export class AccountService implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Registers the first admin on the account. Only used for that. All other registrations must occur through invite
|
||||
* @param model
|
||||
* @returns
|
||||
* @param model
|
||||
* @returns
|
||||
*/
|
||||
register(model: {username: string, password: string, email: string}) {
|
||||
return this.httpClient.post<User>(this.baseUrl + 'account/register', model).pipe(
|
||||
map((user: User) => {
|
||||
return user;
|
||||
}),
|
||||
takeUntil(this.onDestroy)
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -177,8 +172,8 @@ export class AccountService implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Given a user id, returns a full url for setting up the user account
|
||||
* @param userId
|
||||
* @returns
|
||||
* @param userId
|
||||
* @returns
|
||||
*/
|
||||
getInviteUrl(userId: number, withBaseUrl: boolean = true) {
|
||||
return this.httpClient.get<string>(this.baseUrl + 'account/invite-url?userId=' + userId + '&withBaseUrl=' + withBaseUrl, TextResonse);
|
||||
|
|
@ -214,7 +209,7 @@ export class AccountService implements OnDestroy {
|
|||
|
||||
/**
|
||||
* This will get latest preferences for a user and cache them into user store
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
getPreferences() {
|
||||
return this.httpClient.get<Preferences>(this.baseUrl + 'users/get-preferences').pipe(map(pref => {
|
||||
|
|
@ -223,7 +218,7 @@ export class AccountService implements OnDestroy {
|
|||
this.setCurrentUser(this.currentUser);
|
||||
}
|
||||
return pref;
|
||||
}), takeUntil(this.onDestroy));
|
||||
}), takeUntilDestroyed(this.destroyRef));
|
||||
}
|
||||
|
||||
updatePreferences(userPreferences: Preferences) {
|
||||
|
|
@ -233,13 +228,13 @@ export class AccountService implements OnDestroy {
|
|||
this.setCurrentUser(this.currentUser);
|
||||
}
|
||||
return settings;
|
||||
}), takeUntil(this.onDestroy));
|
||||
}), takeUntilDestroyed(this.destroyRef));
|
||||
}
|
||||
|
||||
getUserFromLocalStorage(): User | undefined {
|
||||
|
||||
const userString = localStorage.getItem(this.userKey);
|
||||
|
||||
|
||||
if (userString) {
|
||||
return JSON.parse(userString)
|
||||
};
|
||||
|
|
@ -254,7 +249,7 @@ export class AccountService implements OnDestroy {
|
|||
user.apiKey = key;
|
||||
|
||||
localStorage.setItem(this.userKey, JSON.stringify(user));
|
||||
|
||||
|
||||
this.currentUserSource.next(user);
|
||||
this.currentUser = user;
|
||||
}
|
||||
|
|
@ -270,7 +265,7 @@ export class AccountService implements OnDestroy {
|
|||
this.currentUser.token = user.token;
|
||||
this.currentUser.refreshToken = user.refreshToken;
|
||||
}
|
||||
|
||||
|
||||
this.setCurrentUser(this.currentUser);
|
||||
return user;
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import {DestroyRef, inject, Injectable, OnDestroy} from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { ThemeService } from './theme.service';
|
||||
import { RecentlyAddedItem } from '../_models/recently-added-item';
|
||||
import { AccountService } from './account.service';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ImageService implements OnDestroy {
|
||||
|
||||
export class ImageService {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
baseUrl = environment.apiUrl;
|
||||
apiKey: string = '';
|
||||
encodedKey: string = '';
|
||||
|
|
@ -19,10 +20,8 @@ export class ImageService implements OnDestroy {
|
|||
public resetCoverImage = 'assets/images/image-reset-cover-min.png';
|
||||
public errorWebLinkImage = 'assets/images/broken-white-32x32.png';
|
||||
|
||||
private onDestroy: Subject<void> = new Subject();
|
||||
|
||||
constructor(private accountService: AccountService, private themeService: ThemeService) {
|
||||
this.themeService.currentTheme$.pipe(takeUntil(this.onDestroy)).subscribe(theme => {
|
||||
this.themeService.currentTheme$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(theme => {
|
||||
if (this.themeService.isDarkTheme()) {
|
||||
this.placeholderImage = 'assets/images/image-placeholder.dark-min.png';
|
||||
this.errorImage = 'assets/images/error-placeholder2.dark-min.png';
|
||||
|
|
@ -34,7 +33,7 @@ export class ImageService implements OnDestroy {
|
|||
}
|
||||
});
|
||||
|
||||
this.accountService.currentUser$.pipe(takeUntil(this.onDestroy)).subscribe(user => {
|
||||
this.accountService.currentUser$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(user => {
|
||||
if (user) {
|
||||
this.apiKey = user.apiKey;
|
||||
this.encodedKey = encodeURIComponent(this.apiKey);
|
||||
|
|
@ -42,11 +41,6 @@ export class ImageService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
getRecentlyAddedItem(item: RecentlyAddedItem) {
|
||||
if (item.chapterId === 0) {
|
||||
return this.getVolumeCoverImage(item.volumeId);
|
||||
|
|
@ -56,8 +50,8 @@ export class ImageService implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Returns the entity type from a cover image url. Undefied if not applicable
|
||||
* @param url
|
||||
* @returns
|
||||
* @param url
|
||||
* @returns
|
||||
*/
|
||||
getEntityTypeFromUrl(url: string) {
|
||||
if (url.indexOf('?') < 0) return undefined;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {DestroyRef, inject, Injectable} from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { Router } from '@angular/router';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
|
@ -19,6 +19,7 @@ import { TextResonse } from '../_types/text-response';
|
|||
import { AccountService } from './account.service';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { OnDestroy } from '@angular/core';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
export const CHAPTER_ID_DOESNT_EXIST = -1;
|
||||
export const CHAPTER_ID_NOT_FETCHED = -2;
|
||||
|
|
@ -26,29 +27,25 @@ export const CHAPTER_ID_NOT_FETCHED = -2;
|
|||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ReaderService implements OnDestroy {
|
||||
export class ReaderService {
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
baseUrl = environment.apiUrl;
|
||||
encodedKey: string = '';
|
||||
private onDestroy: Subject<void> = new Subject();
|
||||
|
||||
// Override background color for reader and restore it onDestroy
|
||||
private originalBodyColor!: string;
|
||||
|
||||
constructor(private httpClient: HttpClient, private router: Router,
|
||||
constructor(private httpClient: HttpClient, private router: Router,
|
||||
private location: Location, private utilityService: UtilityService,
|
||||
private filterUtilitySerivce: FilterUtilitiesService, private accountService: AccountService) {
|
||||
this.accountService.currentUser$.pipe(takeUntil(this.onDestroy)).subscribe(user => {
|
||||
private filterUtilityService: FilterUtilitiesService, private accountService: AccountService) {
|
||||
this.accountService.currentUser$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(user => {
|
||||
if (user) {
|
||||
this.encodedKey = encodeURIComponent(user.apiKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
getNavigationArray(libraryId: number, seriesId: number, chapterId: number, format: MangaFormat) {
|
||||
if (format === undefined) format = MangaFormat.ARCHIVE;
|
||||
|
|
@ -77,7 +74,7 @@ export class ReaderService implements OnDestroy {
|
|||
getAllBookmarks(filter: SeriesFilter | undefined) {
|
||||
let params = new HttpParams();
|
||||
params = this.utilityService.addPaginationIfExists(params, undefined, undefined);
|
||||
const data = this.filterUtilitySerivce.createSeriesFilter(filter);
|
||||
const data = this.filterUtilityService.createSeriesFilter(filter);
|
||||
|
||||
return this.httpClient.post<PageBookmark[]>(this.baseUrl + 'reader/all-bookmarks', data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
import { DOCUMENT } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable, OnDestroy, Renderer2, RendererFactory2, SecurityContext } from '@angular/core';
|
||||
import {
|
||||
DestroyRef,
|
||||
inject,
|
||||
Inject,
|
||||
Injectable,
|
||||
OnDestroy,
|
||||
Renderer2,
|
||||
RendererFactory2,
|
||||
SecurityContext
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { map, ReplaySubject, Subject, takeUntil, take } from 'rxjs';
|
||||
|
|
@ -10,13 +19,15 @@ import { NotificationProgressEvent } from '../_models/events/notification-progre
|
|||
import { SiteTheme, ThemeProvider } from '../_models/preferences/site-theme';
|
||||
import { TextResonse } from '../_types/text-response';
|
||||
import { EVENTS, MessageHubService } from './message-hub.service';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ThemeService implements OnDestroy {
|
||||
export class ThemeService {
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
public defaultTheme: string = 'dark';
|
||||
public defaultBookTheme: string = 'Dark';
|
||||
|
||||
|
|
@ -25,13 +36,12 @@ export class ThemeService implements OnDestroy {
|
|||
|
||||
private themesSource = new ReplaySubject<SiteTheme[]>(1);
|
||||
public themes$ = this.themesSource.asObservable();
|
||||
|
||||
|
||||
/**
|
||||
* Maintain a cache of themes. SignalR will inform us if we need to refresh cache
|
||||
*/
|
||||
private themeCache: Array<SiteTheme> = [];
|
||||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
private renderer: Renderer2;
|
||||
private baseUrl = environment.apiUrl;
|
||||
|
||||
|
|
@ -42,7 +52,7 @@ export class ThemeService implements OnDestroy {
|
|||
|
||||
this.getThemes();
|
||||
|
||||
messageHub.messages$.pipe(takeUntil(this.onDestroy)).subscribe(message => {
|
||||
messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(message => {
|
||||
|
||||
if (message.event !== EVENTS.NotificationProgress) return;
|
||||
const notificationEvent = (message.payload as NotificationProgressEvent);
|
||||
|
|
@ -56,31 +66,26 @@ export class ThemeService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
getColorScheme() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--color-scheme').trim();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* --theme-color from theme. Updates the meta tag
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
getThemeColor() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--theme-color').trim();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* --msapplication-TileColor from theme. Updates the meta tag
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
getTileColor() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--title-color').trim();
|
||||
}
|
||||
|
||||
|
||||
getCssVariable(variable: string) {
|
||||
return getComputedStyle(this.document.body).getPropertyValue(variable).trim();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue