From 7e77e804b6b3aa4962981e0abae405e992b0dd4f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 31 Jul 2020 03:27:07 +0200 Subject: [PATCH] Implementing the browse all page with custom sorts --- src/app/app-routing.module.ts | 16 +++- src/app/app.component.ts | 15 ++-- .../library-item-grid.component.html | 20 +++-- .../library-item-grid.component.ts | 39 ++++------ src/app/services/api.service.ts | 77 +++++++++++++++++++ src/app/services/library.service.ts | 48 ------------ .../resolvers/library-resolver.service.ts | 50 ++++++------ src/models/library-item.ts | 24 +++--- src/models/library.ts | 4 +- 9 files changed, 159 insertions(+), 134 deletions(-) create mode 100644 src/app/services/api.service.ts delete mode 100644 src/app/services/library.service.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index bffcf849..48064f85 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,17 +6,23 @@ import {NotFoundComponent} from './not-found/not-found.component'; import {PlayerComponent} from "./pages/player/player.component"; import {SearchComponent} from "./pages/search/search.component"; import {CollectionResolverService} from "./services/resolvers/collection-resolver.service"; -import {LibraryResolverService} from './services/resolvers/library-resolver.service'; +import {PageResolver} from './services/resolvers/library-resolver.service'; import {PeopleResolverService} from "./services/resolvers/people-resolver.service"; import {SearchResolverService} from "./services/resolvers/search-resolver.service"; import {ShowResolverService} from './services/resolvers/show-resolver.service'; import {StreamResolverService} from "./services/resolvers/stream-resolver.service"; import {ShowDetailsComponent} from './pages/show-details/show-details.component'; import {AuthGuard} from "./auth/misc/authenticated-guard.service"; +import {LibraryItem} from "../models/library-item"; +import {CrudApi, LibraryItemService, LibraryService} from "./services/api.service"; const routes: Routes = [ - {path: "browse", component: LibraryItemGridComponent, pathMatch: "full", resolve: { shows: LibraryResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, - {path: "browse/:library-slug", component: LibraryItemGridComponent, resolve: { shows: LibraryResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, + {path: "browse", component: LibraryItemGridComponent, pathMatch: "full", + resolve: { items: PageResolver.forResource("items") }, + canLoad: [AuthGuard.forPermissions("read")], + canActivate: [AuthGuard.forPermissions("read")] + }, + {path: "browse/:library-slug", component: LibraryItemGridComponent, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, {path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, {path: "collection/:collection-slug", component: CollectionComponent, resolve: { collection: CollectionResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, {path: "people/:people-slug", component: CollectionComponent, resolve: { collection: PeopleResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]}, @@ -32,7 +38,9 @@ const routes: Routes = [ })], exports: [RouterModule], providers: [ - LibraryResolverService, + LibraryService, + LibraryItemService, + PageResolver.resolvers, ShowResolverService, CollectionResolverService, PeopleResolverService, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5653a1e5..a2bf51be 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,14 +1,13 @@ -import { Component } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router'; -import * as $ from "jquery"; -import { Location } from "@angular/common"; +import {Component} from '@angular/core'; +import {Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError} from '@angular/router'; +import {Location} from "@angular/common"; import {MatDialog} from "@angular/material/dialog"; import {Account} from "../models/account"; import {AccountComponent} from "./auth/account/account.component"; import {AuthService} from "./auth/auth.service"; import {Library} from "../models/library"; -import {Page} from "../models/page"; +import {LibraryService} from "./services/api.service"; +import * as $ from "jquery"; @Component({ selector: 'app-root', @@ -20,13 +19,13 @@ export class AppComponent libraries: Library[]; isLoading: boolean = false; - constructor(private http: HttpClient, + constructor(private libraryService: LibraryService, private router: Router, private location: Location, public authManager: AuthService, public dialog: MatDialog) { - http.get>("api/libraries").subscribe(result => + libraryService.getAll().subscribe(result => { this.libraries = result.items; }, error => console.error(error)); diff --git a/src/app/components/library-item-grid/library-item-grid.component.html b/src/app/components/library-item-grid/library-item-grid.component.html index dae2fed1..73314e0c 100644 --- a/src/app/components/library-item-grid/library-item-grid.component.html +++ b/src/app/components/library-item-grid/library-item-grid.component.html @@ -3,28 +3,32 @@ filter_list -
+
diff --git a/src/app/components/library-item-grid/library-item-grid.component.ts b/src/app/components/library-item-grid/library-item-grid.component.ts index 3210ccbf..f9a17c58 100644 --- a/src/app/components/library-item-grid/library-item-grid.component.ts +++ b/src/app/components/library-item-grid/library-item-grid.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit, Input } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { DomSanitizer } from '@angular/platform-browser'; -import { Show } from "../../../models/show"; +import {Component, Input} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {DomSanitizer} from '@angular/platform-browser'; +import {ItemType, LibraryItem} from "../../../models/library-item"; +import {Page} from "../../../models/page"; +import {LibraryItemService} from "../../services/api.service"; @Component({ selector: 'app-browse', @@ -10,18 +12,17 @@ import { Show } from "../../../models/show"; }) export class LibraryItemGridComponent { - @Input() shows: Show[]; + @Input() page: Page; @Input() sortEnabled: boolean = true; sortType: string = "title"; + sortKeys: string[] = ["title", "start year", "end year", "status", "type"] sortUp: boolean = true; - sortTypes: string[] = ["title", "release date"]; - - constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer) + constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private items: LibraryItemService) { this.route.data.subscribe((data) => { - this.shows = data.shows; + this.page = data.items; }); } @@ -30,9 +31,9 @@ export class LibraryItemGridComponent return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")"); } - getLink(show: Show) + getLink(show: LibraryItem) { - if (show.isCollection) + if (show.type == ItemType.Collection) return "/collection/" + show.slug; else return "/show/" + show.slug; @@ -43,19 +44,7 @@ export class LibraryItemGridComponent this.sortType = type; this.sortUp = order; - if (type == this.sortTypes[0]) - { - if (order) - this.shows.sort((a, b) => { if (a.title < b.title) return -1; else if (a.title > b.title) return 1; return 0; }); - else - this.shows.sort((a, b) => { if (a.title < b.title) return 1; else if (a.title > b.title) return -1; return 0; }); - } - else if (type == this.sortTypes[1]) - { - if (order) - this.shows.sort((a, b) => a.startYear - b.startYear); - else - this.shows.sort((a, b) => b.startYear - a.startYear); - } + this.items.getAll({sort: `${this.sortType.replace(/\s/g, "")}:${this.sortUp ? "asc" : "desc"}`}) + .subscribe(x => this.page = x); } } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts new file mode 100644 index 00000000..908dd6d5 --- /dev/null +++ b/src/app/services/api.service.ts @@ -0,0 +1,77 @@ +import {Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {Observable} from "rxjs" +import {map} from "rxjs/operators" +import {Page} from "../../models/page"; +import {IResource} from "../../models/resources/resource"; +import {Library} from "../../models/library"; +import {LibraryItem} from "../../models/library-item"; + +class CrudApi +{ + constructor(private client: HttpClient, private route: string) {} + + get(id: number | string): Observable + { + return this.client.get(`/api/${this.route}/${id}`); + } + + getAll(args: {sort: string} = null): Observable> + { + let params: string = "?"; + if (args && args.sort) + params += "sortBy=" + args.sort; + if (params == "?") + params = ""; + return this.client.get>(`/api/${this.route}${params}`); + } + + loadNext(page: Page): Observable> + { + if (page.next == null) + return; + return this.client.get>(page.next).pipe(map(x => + { + x.items = page.items.concat(x.items); + x.count += page.count; + return x; + })); + } + + create(item: T): Observable + { + return this.client.post(`/api/${this.route}`, item); + } + + edit(item: T): Observable + { + return this.client.put(`/api/${this.route}`, item); + } + + delete(item: T): Observable + { + return this.client.delete(`/api/${this.route}/${item.slug}`); + } +} + +@Injectable({ + providedIn: 'root' +}) +export class LibraryService extends CrudApi +{ + constructor(client: HttpClient) + { + super(client, "libraries"); + } +} + +@Injectable({ + providedIn: 'root' +}) +export class LibraryItemService extends CrudApi +{ + constructor(client: HttpClient) + { + super(client, "items"); + } +} diff --git a/src/app/services/library.service.ts b/src/app/services/library.service.ts deleted file mode 100644 index b171b32d..00000000 --- a/src/app/services/library.service.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {Injectable} from "@angular/core"; -import {HttpClient} from "@angular/common/http"; -import {Observable} from "rxjs" -import {Page} from "../../models/page"; -import {IResource} from "../../models/resources/resource"; - -class CrudApi -{ - constructor(private client: HttpClient, private route: string) {} - - get(id: number | string): Observable - { - return this.client.get(`/api/${this.route}/${id}`); - } - - getAll(id: number | string): Observable> - { - return this.client.get>(`/api/${this.route}`); - } - - create(item: T): Observable - { - return this.client.post(`/api/${this.route}`, item); - } - - edit(item: T): Observable - { - return this.client.put(`/api/${this.route}`, item); - } - - delete(item: T): Observable - { - return this.client.delete(`/api/${this.route}/${item.slug}`); - } -} - -@Injectable({ - providedIn: 'root' -}) -export class LibraryService -{ - constructor() { } - - get() - { - - } -} diff --git a/src/app/services/resolvers/library-resolver.service.ts b/src/app/services/resolvers/library-resolver.service.ts index bf5f9d73..c0c87d67 100644 --- a/src/app/services/resolvers/library-resolver.service.ts +++ b/src/app/services/resolvers/library-resolver.service.ts @@ -4,42 +4,36 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { EMPTY, Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { Show } from "../../../models/show"; - +import {Page} from "../../../models/page"; +import {IResource} from "../../../models/resources/resource"; @Injectable() -export class LibraryResolverService implements Resolve +export class PageResolver { - constructor(private http: HttpClient, private snackBar: MatSnackBar) { } + public static resolvers: any[] = []; - resolve(route: ActivatedRouteSnapshot): Show[] | Observable | Promise + static forResource(resource: string) { - let slug: string = route.paramMap.get("library-slug"); + @Injectable() + class Resolver implements Resolve> + { + constructor(private http: HttpClient, private snackBar: MatSnackBar) { } - if (slug == null) - { - return this.http.get("api/shows").pipe(catchError((error: HttpErrorResponse) => + resolve(route: ActivatedRouteSnapshot): Page | Observable> | Promise> { - 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/libraries/" + slug).pipe(catchError((error: HttpErrorResponse) => - { - console.log(error.status + " - " + error.message); - if (error.status == 404) + return this.http.get>(`api/${resource}`).pipe(catchError((error: HttpErrorResponse) => { - 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; - })); + console.log(error.status + " - " + error.message); + this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, { + horizontalPosition: "left", + panelClass: ['snackError'], + duration: 2500 } + ); + return EMPTY; + })); + } } + PageResolver.resolvers.push(Resolver); + return Resolver; } } diff --git a/src/models/library-item.ts b/src/models/library-item.ts index 18f6162a..28747617 100644 --- a/src/models/library-item.ts +++ b/src/models/library-item.ts @@ -1,20 +1,20 @@ -enum ItemType +import {IResource} from "./resources/resource"; + +export enum ItemType { Show, Movie, Collection } -export interface LibraryItem +export interface LibraryItem extends IResource { - ID: number - Slug: string - Title: string - Overview: string - Status: string - TrailerUrl: string - StartYear: number - EndYear: number - Poster: string - Type: ItemType + title: string + overview: string + status: string + trailerUrl: string + startYear: number + endYear: number + poster: string + type: ItemType } \ No newline at end of file diff --git a/src/models/library.ts b/src/models/library.ts index 99aadb02..bab28944 100644 --- a/src/models/library.ts +++ b/src/models/library.ts @@ -1,4 +1,6 @@ -export interface Library +import {IResource} from "./resources/resource"; + +export interface Library extends IResource { id: number; slug: string;