Angular Upgrade (#1059)

* Upgraded to Angular 12

* Bump ng-bootstrap for upgrade

* Angular 13 upgrade, ng-bootstrap bump

* Angular 13 upgrade (broken)

* Angular 13 upgrade. CSS is broken completely

* Angular 13 upgrade is complete.
This commit is contained in:
Joseph Milazzo 2022-02-11 15:23:26 -08:00 committed by GitHub
parent d7450497a6
commit 97b1249a0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 4993 additions and 8937 deletions

2
.gitignore vendored
View File

@ -526,3 +526,5 @@ API/config/post-metadata/
API.Tests/TestResults/
UI/Web/.vscode/settings.json
/API.Tests/Services/Test Data/ArchiveService/CoverImages/output/*
/UI/Web/.angular/

View File

@ -28,7 +28,6 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/assets",
"src/site.webmanifest"
@ -46,7 +45,12 @@
"node_modules/lazysizes/lazysizes.min.js",
"node_modules/lazysizes/plugins/rias/ls.rias.min.js",
"node_modules/lazysizes/plugins/attrchange/ls.attrchange.min.js"
]
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"optimization": true,
"namedChunks": true
},
"configurations": {
"production": {
@ -60,7 +64,6 @@
"outputHashing": "all",
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true,
"buildOptimizer": true,
"budgets": [
{
@ -75,7 +78,8 @@
}
]
}
}
},
"defaultConfiguration": ""
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",

13698
UI/Web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"prod": "ng build --prod",
"prod": "ng build --configuration production",
"explore": "ng build --stats-json && webpack-bundle-analyzer dist/stats.json",
"test": "jest",
"test:watch": "jest --watch",
@ -16,50 +16,50 @@
"private": true,
"dependencies": {
"@angular-slider/ngx-slider": "^2.0.3",
"@angular/animations": "~11.0.0",
"@angular/cdk": "^12.2.3",
"@angular/common": "~11.0.0",
"@angular/compiler": "~11.0.0",
"@angular/core": "~11.0.0",
"@angular/forms": "~11.0.0",
"@angular/localize": "~11.0.0",
"@angular/platform-browser": "~11.0.0",
"@angular/platform-browser-dynamic": "~11.0.0",
"@angular/router": "~11.0.0",
"@fortawesome/fontawesome-free": "^5.15.1",
"@microsoft/signalr": "^5.0.8",
"@ng-bootstrap/ng-bootstrap": "^9.1.0",
"@ngx-lite/nav-drawer": "^0.4.6",
"@ngx-lite/util": "0.0.0",
"@types/file-saver": "^2.0.1",
"bootstrap": "^4.5.0",
"@angular/animations": "~13.2.2",
"@angular/cdk": "^13.2.2",
"@angular/common": "~13.2.2",
"@angular/compiler": "~13.2.2",
"@angular/core": "~13.2.2",
"@angular/forms": "~13.2.2",
"@angular/localize": "~13.2.2",
"@angular/platform-browser": "~13.2.2",
"@angular/platform-browser-dynamic": "~13.2.2",
"@angular/router": "~13.2.2",
"@fortawesome/fontawesome-free": "^6.0.0",
"@microsoft/signalr": "^6.0.2",
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
"@ngx-lite/nav-drawer": "^0.4.7",
"@ngx-lite/util": "0.0.1",
"@types/file-saver": "^2.0.5",
"bootstrap": "^4.6.1",
"bowser": "^2.11.0",
"file-saver": "^2.0.5",
"lazysizes": "^5.3.2",
"ng-circle-progress": "^1.6.0",
"ng-lazyload-image": "^9.1.0",
"ngx-file-drop": "^11.1.0",
"ngx-toastr": "^13.2.1",
"rxjs": "~6.6.0",
"swiper": "^6.5.8",
"tslib": "^2.0.0",
"webpack-bundle-analyzer": "^4.4.2",
"zone.js": "~0.10.2"
"ng-lazyload-image": "^9.1.2",
"ngx-file-drop": "^13.0.0",
"ngx-toastr": "^14.2.1",
"rxjs": "~7.5.4",
"swiper": "^8.0.3",
"tslib": "^2.3.1",
"webpack-bundle-analyzer": "^4.5.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1100.0",
"@angular/cli": "^11.2.11",
"@angular/compiler-cli": "~11.0.0",
"@types/jest": "^26.0.20",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jest": "^26.6.3",
"jest-preset-angular": "^8.3.2",
"karma-coverage": "~2.0.3",
"@angular-devkit/build-angular": "~13.2.3",
"@angular/cli": "^13.2.3",
"@angular/compiler-cli": "~13.2.2",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.17",
"codelyzer": "^6.0.2",
"jest": "^27.5.1",
"jest-preset-angular": "^11.1.0",
"karma-coverage": "~2.2.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"ts-node": "~10.5.0",
"tslint": "^6.1.3",
"typescript": "~4.0.2"
"typescript": "~4.5.5"
},
"jest": {
"preset": "jest-preset-angular",

View File

@ -3,7 +3,6 @@ import { CanActivate } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { User } from '../_models/user';
import { AccountService } from '../_services/account.service';
@Injectable({
@ -15,8 +14,8 @@ export class AdminGuard implements CanActivate {
canActivate(): Observable<boolean> {
// this automaticallys subs due to being router guard
return this.accountService.currentUser$.pipe(take(1),
map((user: User) => {
if (this.accountService.hasAdminRole(user)) {
map((user) => {
if (user && this.accountService.hasAdminRole(user)) {
return true;
}

View File

@ -3,7 +3,6 @@ import { CanActivate, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { User } from '../_models/user';
import { AccountService } from '../_services/account.service';
@Injectable({
@ -15,7 +14,7 @@ export class AuthGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.accountService.currentUser$.pipe(take(1),
map((user: User) => {
map((user) => {
if (user) {
return true;
}

View File

@ -7,7 +7,6 @@ import {
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { AccountService } from '../_services/account.service';
import { User } from '../_models/user';
import { take } from 'rxjs/operators';
@Injectable()
@ -16,16 +15,13 @@ export class JwtInterceptor implements HttpInterceptor {
constructor(private accountService: AccountService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
let currentUser: User;
// Take 1 means we don't have to unsubscribe because we take 1 then complete
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
currentUser = user;
if (currentUser) {
if (user) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
Authorization: `Bearer ${user.token}`
}
});
}

View File

@ -19,7 +19,10 @@ export class AccountService implements OnDestroy {
currentUser: User | undefined;
// Stores values, when someone subscribes gives (1) of last values seen.
private currentUserSource = new ReplaySubject<User>(1);
private currentUserSource = new ReplaySubject<User | undefined>(1);
/**
*
*/
currentUser$ = this.currentUserSource.asObservable();
/**

View File

@ -35,8 +35,10 @@ export class ManageUsersComponent implements OnInit, OnDestroy {
private confirmService: ConfirmService,
public messageHub: MessageHubService,
private serverService: ServerService) {
this.accountService.currentUser$.pipe(take(1)).subscribe((user: User) => {
this.accountService.currentUser$.pipe(take(1)).subscribe((user) => {
if (user) {
this.loggedInUsername = user.username;
}
});
}

View File

@ -2,14 +2,14 @@
<div>
<h2 style="display: inline-block;"><a href="javascript:void(0)" (click)="sectionClicked($event)" class="section-title">{{title}}</a></h2>
<div class="float-right">
<button class="btn btn-icon" [disabled]="swiper?.isBeginning" (click)="prevPage()"><i class="fa fa-angle-left" aria-hidden="true"></i><span class="sr-only">Previous Items</span></button>
<button class="btn btn-icon" [disabled]="swiper?.isEnd" (click)="nextPage()"><i class="fa fa-angle-right" aria-hidden="true"></i><span class="sr-only">Next Items</span></button>
<button class="btn btn-icon" [disabled]="isBeginning" (click)="prevPage()"><i class="fa fa-angle-left" aria-hidden="true"></i><span class="sr-only">Previous Items</span></button>
<button class="btn btn-icon" [disabled]="isEnd" (click)="nextPage()"><i class="fa fa-angle-right" aria-hidden="true"></i><span class="sr-only">Next Items</span></button>
</div>
</div>
<div>
<swiper
#swiper
[slidesPerView]="'auto'"
(swiper)="onSwiper($event)"
[freeMode]="true">
<ng-template *ngFor="let item of items; index as i;" swiperSlide>
<ng-container [ngTemplateOutlet]="carouselItemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>

View File

@ -1,5 +1,7 @@
import { Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
import Swiper from 'swiper';
import { Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import { SwiperComponent } from 'swiper/angular';
//import Swiper from 'swiper';
//import { SwiperEvents, Swiper } from 'swiper/types';
@Component({
selector: 'app-carousel-reel',
@ -13,10 +15,20 @@ export class CarouselReelComponent implements OnInit {
@Input() title = '';
@Output() sectionClick = new EventEmitter<string>();
@ViewChild('swiper', { static: false }) swiper?: SwiperComponent;
swiper!: Swiper;
//swiper!: Swiper;
trackByIdentity: (index: number, item: any) => string;
get isEnd() {
return this.swiper?.swiperRef.isEnd;
}
get isBeginning() {
return this.swiper?.swiperRef.isBeginning;
}
constructor() {
this.trackByIdentity = (index: number, item: any) => `${this.title}_${item.id}_${item?.name}_${item?.pagesRead}_${index}`;
}
@ -25,13 +37,13 @@ export class CarouselReelComponent implements OnInit {
nextPage() {
if (this.swiper) {
this.swiper.setProgress(this.swiper.progress + 0.25, 600);
this.swiper.swiperRef.setProgress(this.swiper.swiperRef.progress + 0.25, 600);
}
}
prevPage() {
if (this.swiper) {
this.swiper.setProgress(this.swiper.progress - 0.25, 600);
this.swiper.swiperRef.setProgress(this.swiper.swiperRef.progress - 0.25, 600);
}
}
@ -39,7 +51,14 @@ export class CarouselReelComponent implements OnInit {
this.sectionClick.emit(this.title);
}
onSwiper(swiper: any) {
this.swiper = swiper;
}
// onSwiper(eventParams: Parameters<SwiperEvents['init']>) {
// console.log('swiper: ', eventParams);
// [this.swiper] = eventParams;
// }
// onSwiper(params: Swiper) {
// // const [swiper] = params;
// // console.log(swiper);
// // return params;
// }
}

View File

@ -73,11 +73,13 @@ export class LibraryComponent implements OnInit, OnDestroy {
this.isLoading = true;
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
this.user = user;
if (this.user) {
this.isAdmin = this.accountService.hasAdminRole(this.user);
this.libraryService.getLibrariesForMember().pipe(take(1)).subscribe(libraries => {
this.libraries = libraries;
this.isLoading = false;
});
}
});
this.reloadSeries();

View File

@ -1,7 +1,9 @@
@use '../../../../theme/colors';
.clickable {
cursor: pointer;
}
.clickable:hover, .clickable:focus {
background-color: lightgreen;
background-color: colors.$primary-color;
}

View File

@ -83,7 +83,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.titleService.setTitle('Kavita - User Preferences');
this.accountService.currentUser$.pipe(take(1)).subscribe((user: User) => {
this.accountService.currentUser$.pipe(take(1)).subscribe((user) => {
if (user) {
this.user = user;
this.isAdmin = this.accountService.hasAdminRole(user);

View File

@ -15,4 +15,4 @@ export const environment = {
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@ -59,7 +59,7 @@ import '@angular/localize/init';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************

View File

@ -1,14 +1,20 @@
@use '../node_modules/swiper/swiper.scss' as swiper;
@use '~bootstrap/scss/mixins/_breakpoints.scss' as breakpoints;
// Import colors for overrides of bootstrap theme
@import './theme/colors';
@import './theme/toastr';
// Bootstrap must be after _colors since we define the colors there
@import '~bootstrap/scss/bootstrap';
@import '~bootstrap/scss/mixins/_breakpoints.scss';
@import '~swiper/swiper.scss';
@import './assets/themes/dark.scss';
@import './theme/dark.scss';
// Global Styles
button:disabled, .form-control:disabled, .form-control[readonly], .disabled, :disabled {
@ -72,14 +78,16 @@ app-root {
}
// Utiliities
@include media-breakpoint-down(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
@include breakpoints.media-breakpoint-down(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
.phone-hidden {
display: none;
}
}
@include media-breakpoint-up(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
@include breakpoints.media-breakpoint-up(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) {
.not-phone-hidden {
display: none;
}

View File

@ -18,6 +18,26 @@ $dark-item-accent-bg: #292d32;
$white-item-accent-bg: rgba(247, 247, 247, 1);
// Default variables for light mode
:root {
--primary-color: #4ac694;
--error-color: #ff4136;
--bs-body-bg: #fff;
}
// Dark mode theme
:root .bg-dark {
--primary-color: #4ac694;
--error-color: #ff4136;
--bs-body-bg: red; // This is bootstrap v5 only
}
// E-ink theme
:root .bg-eink {
--primary-color: black;
--bs-body-bg: white;
}
//=========================
// Ratings

View File

@ -1,6 +1,10 @@
// All dark style overrides should live here
@use "../../theme/colors";
@use "./colors";
// :root {
// --bs-body-color: #212529;
// }
.bg-dark {
color: $dark-text-color;
@ -155,7 +159,7 @@
&::before {
content: "";
background-image: url('../../assets/images/login-bg.jpg');
background-image: url('../assets/images/login-bg.jpg');
background-size: cover;
position: absolute;
top: 0;