diff --git a/Kyoo/ClientApp/package-lock.json b/Kyoo/ClientApp/package-lock.json index a96258e7..8be5d0b2 100644 --- a/Kyoo/ClientApp/package-lock.json +++ b/Kyoo/ClientApp/package-lock.json @@ -133,6 +133,23 @@ "tslib": "^1.9.0" } }, + "@angular/cdk": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.1.3.tgz", + "integrity": "sha512-+DOS6x05/nNdnoRmEi3bgQxKym34PeCRGD6dimdw0l7ZgM57qhlaBWo0dXB7QSyR9E44uVT91e4h8ye+/ne1DQ==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^1.7.1" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "optional": true + } + } + }, "@angular/cli": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.2.1.tgz", @@ -976,6 +993,14 @@ "integrity": "sha512-BTCYLiSz1TUKAyIGnYI3X5zhf4R5wGQZjYIaUnl8iWLIGG6zAmz++9mgDRlnP12u/6KEotJYzqllH72blxWGHw==", "dev": true }, + "@angular/material": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-8.1.3.tgz", + "integrity": "sha512-qZVWrJ/EO1y0lJCy7pe536RlYiih3p3fQzj7tgus7JdOpspyF+zBLzn8gNrdAFACXpVWwq2kLorieoR3BB47ZQ==", + "requires": { + "tslib": "^1.7.1" + } + }, "@angular/platform-browser": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.1.tgz", @@ -5931,7 +5956,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } diff --git a/Kyoo/ClientApp/package.json b/Kyoo/ClientApp/package.json index 79bae8d9..8ba062f2 100644 --- a/Kyoo/ClientApp/package.json +++ b/Kyoo/ClientApp/package.json @@ -12,10 +12,12 @@ "private": true, "dependencies": { "@angular/animations": "~8.2.0", + "@angular/cdk": "^8.1.3", "@angular/common": "~8.2.0", "@angular/compiler": "~8.2.0", "@angular/core": "~8.2.0", "@angular/forms": "~8.2.0", + "@angular/material": "^8.1.3", "@angular/platform-browser": "~8.2.0", "@angular/platform-browser-dynamic": "~8.2.0", "@angular/router": "~8.2.0", diff --git a/Kyoo/ClientApp/src/app/app-routing.module.ts b/Kyoo/ClientApp/src/app/app-routing.module.ts index 763aef18..63d5a5bd 100644 --- a/Kyoo/ClientApp/src/app/app-routing.module.ts +++ b/Kyoo/ClientApp/src/app/app-routing.module.ts @@ -4,17 +4,20 @@ import { Routes, RouterModule } from '@angular/router'; import { BrowseComponent } from './browse/browse.component'; import { ShowDetailsComponent } from './show-details/show-details.component'; import { NotFoundComponent } from './not-found/not-found.component'; +import { ShowResolverService } from './services/show-resolver.service'; +import { LibraryResolverService } from './services/library-resolver.service'; const routes: Routes = [ - { path: "browse", component: BrowseComponent, pathMatch: "full" }, - { path: "browse/:library-slug", component: BrowseComponent }, - { path: "shows/:show-slug", component: ShowDetailsComponent }, + { path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } }, + { path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } }, + { path: "shows/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } }, { path: "**", component: NotFoundComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] + exports: [RouterModule], + providers: [LibraryResolverService, ShowResolverService] }) export class AppRoutingModule { } diff --git a/Kyoo/ClientApp/src/app/app.component.html b/Kyoo/ClientApp/src/app/app.component.html index 0768366e..0195916a 100644 --- a/Kyoo/ClientApp/src/app/app.component.html +++ b/Kyoo/ClientApp/src/app/app.component.html @@ -1,35 +1,38 @@ -
- + +
diff --git a/Kyoo/ClientApp/src/app/app.component.ts b/Kyoo/ClientApp/src/app/app.component.ts index 1a831ee6..906c5a81 100644 --- a/Kyoo/ClientApp/src/app/app.component.ts +++ b/Kyoo/ClientApp/src/app/app.component.ts @@ -1,5 +1,6 @@ -import { Component, Inject } from '@angular/core'; +import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router'; @Component({ selector: 'app-root', @@ -9,13 +10,38 @@ import { HttpClient } from '@angular/common/http'; export class AppComponent { libraries: Library[]; + isLoading: boolean = false; - constructor(http: HttpClient) + constructor(http: HttpClient, private router: Router) { http.get("api/libraries").subscribe(result => { this.libraries = result; }, error => console.error(error)); + + this.router.events.subscribe((event: Event) => + { + switch (true) + { + case event instanceof NavigationStart: + { + this.isLoading = true; + break; + } + + case event instanceof NavigationEnd: + case event instanceof NavigationCancel: + case event instanceof NavigationError: + { + this.isLoading = false; + break; + } + default: + { + break; + } + } + }); } } diff --git a/Kyoo/ClientApp/src/app/app.module.ts b/Kyoo/ClientApp/src/app/app.module.ts index 19dc2c28..2c889cad 100644 --- a/Kyoo/ClientApp/src/app/app.module.ts +++ b/Kyoo/ClientApp/src/app/app.module.ts @@ -7,6 +7,9 @@ import { AppComponent } from './app.component'; import { NotFoundComponent } from './not-found/not-found.component'; import { BrowseComponent } from './browse/browse.component'; import { ShowDetailsComponent } from './show-details/show-details.component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ declarations: [ @@ -18,7 +21,10 @@ import { ShowDetailsComponent } from './show-details/show-details.component'; imports: [ BrowserModule, HttpClientModule, - AppRoutingModule + AppRoutingModule, + BrowserAnimationsModule, + MatSnackBarModule, + MatProgressBarModule ], providers: [], bootstrap: [AppComponent] diff --git a/Kyoo/ClientApp/src/app/browse/browse.component.ts b/Kyoo/ClientApp/src/app/browse/browse.component.ts index d51dc9b7..c878befc 100644 --- a/Kyoo/ClientApp/src/app/browse/browse.component.ts +++ b/Kyoo/ClientApp/src/app/browse/browse.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { HttpClient } from '@angular/common/http'; -import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'app-browse', @@ -12,36 +11,11 @@ export class BrowseComponent implements OnInit { shows: Show[]; - private watch: any; - - constructor(private http: HttpClient, private route: ActivatedRoute, private sanitizer: DomSanitizer) {} + constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer) { } ngOnInit() { - this.watch = this.route.params.subscribe(params => - { - var slug: string = params["library-slug"]; - - if (slug == null) - { - this.http.get("api/shows").subscribe(result => - { - this.shows = result; - }, error => console.log(error)); - } - else - { - this.http.get("api/library/" + slug).subscribe(result => - { - this.shows = result; - }, error => console.log(error)); - } - }); - } - - ngOnDestroy() - { - this.watch.unsubscribe(); + this.shows = this.route.snapshot.data.shows; } getThumb(slug: string) diff --git a/Kyoo/ClientApp/src/app/services/library-resolver.service.ts b/Kyoo/ClientApp/src/app/services/library-resolver.service.ts new file mode 100644 index 00000000..209886aa --- /dev/null +++ b/Kyoo/ClientApp/src/app/services/library-resolver.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Observable, EMPTY } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Injectable() +export class LibraryResolverService implements Resolve +{ + constructor(private http: HttpClient, private snackBar: MatSnackBar) { console.log("Library Resolver Created"); } + + resolve(route: ActivatedRouteSnapshot): Show[] | Observable | Promise + { + let slug: string = route.paramMap.get("library-slug"); + + if (slug == null) + { + return this.http.get("api/shows").pipe(catchError((error: HttpErrorResponse) => + { + console.log(error.status + " - " + error.message); + this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); + return EMPTY; + })); + } + else + { + return this.http.get("api/library/" + slug).pipe(catchError((error: HttpErrorResponse) => + { + console.log(error.status + " - " + error.message); + if (error.status == 404) + { + this.snackBar.open("Library \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); + } + else + { + this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); + } + return EMPTY; + })); + } + } +} diff --git a/Kyoo/ClientApp/src/app/services/show-resolver.service.ts b/Kyoo/ClientApp/src/app/services/show-resolver.service.ts new file mode 100644 index 00000000..9774924a --- /dev/null +++ b/Kyoo/ClientApp/src/app/services/show-resolver.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Observable, EMPTY } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Injectable() +export class ShowResolverService implements Resolve +{ + constructor(private http: HttpClient, private snackBar: MatSnackBar) { } + + resolve(route: ActivatedRouteSnapshot): Show | Observable | Promise + { + let slug: string = route.paramMap.get("show-slug"); + return this.http.get("api/shows/" + slug).pipe(catchError((error: HttpErrorResponse) => + { + console.log(error.status + " - " + error.message); + if (error.status == 404) + { + this.snackBar.open("Show \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); + } + else + { + this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 }); + } + return EMPTY; + })); + } +} diff --git a/Kyoo/ClientApp/src/app/show-details/show-details.component.ts b/Kyoo/ClientApp/src/app/show-details/show-details.component.ts index b9238c33..59c1c3c3 100644 --- a/Kyoo/ClientApp/src/app/show-details/show-details.component.ts +++ b/Kyoo/ClientApp/src/app/show-details/show-details.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-show-details', @@ -11,26 +10,10 @@ export class ShowDetailsComponent implements OnInit { show: Show; - private watch: any; - - constructor(private http: HttpClient, private route: ActivatedRoute) { } + constructor(private route: ActivatedRoute) { } ngOnInit() { - this.watch = this.route.params.subscribe(params => - { - var slug: string = params["show-slug"]; - - this.http.get("api/shows/" + slug).subscribe(result => - { - this.show = result; - }, error => console.log(error)); - }); + this.show = this.route.snapshot.data.show; } - - ngOnDestroy() - { - this.watch.unsubscribe(); - } - } diff --git a/Kyoo/ClientApp/src/styles.scss b/Kyoo/ClientApp/src/styles.scss index 986f219e..2a7e6a55 100644 --- a/Kyoo/ClientApp/src/styles.scss +++ b/Kyoo/ClientApp/src/styles.scss @@ -1,3 +1,4 @@ +//Bootstrap configuration @import "~bootstrap/scss/functions"; @import "~bootstrap/scss/variables"; @@ -12,3 +13,21 @@ $body-bg: theme-color("primary"); $body-color: theme-color("textPrimary"); @import "~bootstrap/scss/bootstrap"; + + +//Material Angular Configuration +@import '~@angular/material/theming'; +@include mat-core(); + +// Define the default theme (same as the example above). +$primary: (default: #0a1128); +$accent: (default: #e23c00, lighter: #ff9149); +$theme: mat-light-theme($primary, $accent); + +// Include the default theme styles. +@include angular-material-theme($theme); + +.snackError { + background-color: theme-color("accentColor"); + color: theme-color("textPrimary"); +}