From 89a7d7ea81d3876cffb9a21d14b17c2046614c40 Mon Sep 17 00:00:00 2001 From: grimsi <9295182+grimsi@users.noreply.github.com> Date: Mon, 15 Aug 2022 15:49:19 +0200 Subject: [PATCH] Implemented sorting for library overview --- frontend/src/app/app.module.ts | 78 +++++++-------- .../library-overview.component.html | 23 ++++- .../library-overview.component.scss | 4 + .../library-overview.component.ts | 97 +++++++++++++------ .../navbar-layout/navbar-layout.component.ts | 2 +- 5 files changed, 132 insertions(+), 72 deletions(-) diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index cf3c62d..bc2d9d4 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -50,6 +50,7 @@ import {MatAutocompleteModule} from "@angular/material/autocomplete"; import { NgModelChangeDebouncedDirective } from './directives/ng-model-change-debounced.directive'; import { FooterComponent } from './components/footer/footer.component'; import {MatExpansionModule} from "@angular/material/expansion"; +import {MatSelectModule} from "@angular/material/select"; @NgModule({ declarations: [ @@ -70,44 +71,45 @@ import {MatExpansionModule} from "@angular/material/expansion"; NgModelChangeDebouncedDirective, FooterComponent ], - imports: [ - BrowserModule, - AppRoutingModule, - BrowserAnimationsModule, - FormsModule, - MatFormFieldModule, - MatCardModule, - MatTabsModule, - MatToolbarModule, - MatMenuModule, - MatIconModule, - HttpClientModule, - FormsModule, - ReactiveFormsModule, - MatDialogModule, - MatButtonModule, - MatInputModule, - FlexModule, - MatProgressSpinnerModule, - MatTableModule, - MatPaginatorModule, - MatSortModule, - MatSnackBarModule, - MatGridListModule, - FlexLayoutModule, - GridModule, - YouTubePlayerModule, - MatChipsModule, - MatTooltipModule, - MatSlideToggleModule, - MatCheckboxModule, - A11yModule, - MatTableFilterModule, - MatDividerModule, - MatListModule, - MatAutocompleteModule, - MatExpansionModule - ], + imports: [ + BrowserModule, + AppRoutingModule, + BrowserAnimationsModule, + FormsModule, + MatFormFieldModule, + MatCardModule, + MatTabsModule, + MatToolbarModule, + MatMenuModule, + MatIconModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + MatDialogModule, + MatButtonModule, + MatInputModule, + FlexModule, + MatProgressSpinnerModule, + MatTableModule, + MatPaginatorModule, + MatSortModule, + MatSnackBarModule, + MatGridListModule, + FlexLayoutModule, + GridModule, + YouTubePlayerModule, + MatChipsModule, + MatTooltipModule, + MatSlideToggleModule, + MatCheckboxModule, + A11yModule, + MatTableFilterModule, + MatDividerModule, + MatListModule, + MatAutocompleteModule, + MatExpansionModule, + MatSelectModule + ], providers: [ { provide: HTTP_INTERCEPTORS, diff --git a/frontend/src/app/components/library-overview/library-overview.component.html b/frontend/src/app/components/library-overview/library-overview.component.html index 8f84cca..88a3c49 100644 --- a/frontend/src/app/components/library-overview/library-overview.component.html +++ b/frontend/src/app/components/library-overview/library-overview.component.html @@ -26,8 +26,9 @@
- - @@ -46,6 +47,15 @@ + +

Sort by:

+ + + {{sortOption.title}} + + +
+ @@ -55,11 +65,14 @@
- Offline Co-op + Offline + Co-op - Online Co-op + Online + Co-op - LAN Support + LAN + Support
diff --git a/frontend/src/app/components/library-overview/library-overview.component.scss b/frontend/src/app/components/library-overview/library-overview.component.scss index 28825b2..c3d9f7e 100644 --- a/frontend/src/app/components/library-overview/library-overview.component.scss +++ b/frontend/src/app/components/library-overview/library-overview.component.scss @@ -38,6 +38,10 @@ margin-bottom: 0; } +.mat-card-48 { + height: 48px; +} + ::ng-deep .mat-checkbox-frame { $config: mat.get-color-config($dark-theme); $primary-palette: map.get($config, 'primary'); diff --git a/frontend/src/app/components/library-overview/library-overview.component.ts b/frontend/src/app/components/library-overview/library-overview.component.ts index e5b176f..8e9c1b9 100644 --- a/frontend/src/app/components/library-overview/library-overview.component.ts +++ b/frontend/src/app/components/library-overview/library-overview.component.ts @@ -1,9 +1,22 @@ -import {AfterContentInit, AfterViewInit, Component, Input} from '@angular/core'; +import {AfterContentInit, Component} from '@angular/core'; import {GamesService} from "../../services/games.service"; import {DetectedGameDto} from "../../models/dtos/DetectedGameDto"; import {GenreDto} from "../../models/dtos/GenreDto"; import {ThemeDto} from "../../models/dtos/ThemeDto"; -import {forkJoin, Observable} from "rxjs"; +import {firstValueFrom, forkJoin, Observable, pipe} from "rxjs"; +import {SortDirection} from "@angular/material/sort"; + +class SortOption { + title: string; + field: string; + direction: SortDirection; + + constructor(title: string, field: string, direction: SortDirection) { + this.title = title; + this.field = field; + this.direction = direction; + } +} @Component({ selector: 'app-gameserver-list', @@ -12,7 +25,24 @@ import {forkJoin, Observable} from "rxjs"; }) export class LibraryOverviewComponent implements AfterContentInit { + defaultSortOption: SortOption = new SortOption("Title (A-Z)", "title", "asc"); + + sortOptions: SortOption[] = [ + this.defaultSortOption, + new SortOption("Title (Z-A)", "title", "desc"), + + new SortOption("Release (newest first)", "releaseDate", "desc"), + new SortOption("Release (oldest first)", "releaseDate", "asc"), + + new SortOption("Added to library (newest first)", "addedToLibrary", "desc"), + new SortOption("Added to library (oldest first)", "addedToLibrary", "asc"), + + new SortOption("Rating (highest first)", "totalRating", "desc"), + new SortOption("Rating (lowest first)", "totalRating", "asc") + ]; + searchTerm: string = ""; + selectedSortOption: SortOption = this.defaultSortOption; offlineCoopFilterEnabled: boolean = false; onlineCoopFilterEnabled: boolean = false; lanSupportFilterEnabled: boolean = false; @@ -32,7 +62,7 @@ export class LibraryOverviewComponent implements AfterContentInit { ngAfterContentInit(): void { this.gameServerService.getAllGames().subscribe( detectedGames => { - if(detectedGames.length === 0) { + if (detectedGames.length === 0) { this.gameLibraryIsEmpty = true; this.loading = false; return; @@ -46,15 +76,15 @@ export class LibraryOverviewComponent implements AfterContentInit { forkJoin([themeObservable, genreObservable]).subscribe(result => { this.availableThemes = result[0]; this.availableGenres = result[1]; - this.refreshLibraryView(); - this.loading = false; + this.refreshLibraryView().then(() => this.loading = false); }); } ); } - refreshLibraryView(): void { - this.filterGames(); + async refreshLibraryView(): Promise { + let games: DetectedGameDto[] = await firstValueFrom(this.gameServerService.getAllGames()); + this.games = this.sortGames(this.filterGames(games)); } clearSearchTerm(): void { @@ -62,32 +92,43 @@ export class LibraryOverviewComponent implements AfterContentInit { this.refreshLibraryView(); } - filterGames(): void { - this.gameServerService.getAllGames().subscribe(games => { - let filteredGames: DetectedGameDto[] = games; + filterGames(games: DetectedGameDto[]): DetectedGameDto[] { + if (this.searchTerm.trim().toLowerCase().length > 0) { + games = games.filter(game => game.title.trim().toLowerCase().includes(this.searchTerm.trim().toLowerCase())); + } - if(this.searchTerm.trim().toLowerCase().length > 0) { - filteredGames = filteredGames.filter(game => game.title.trim().toLowerCase().includes(this.searchTerm.trim().toLowerCase())); - } + if (this.offlineCoopFilterEnabled || this.onlineCoopFilterEnabled || this.lanSupportFilterEnabled) { + games = games.filter(game => (game.offlineCoop === this.offlineCoopFilterEnabled || game.onlineCoop === this.onlineCoopFilterEnabled || game.lanSupport === this.lanSupportFilterEnabled)); + } - if(this.offlineCoopFilterEnabled || this.onlineCoopFilterEnabled || this.lanSupportFilterEnabled) { - filteredGames = filteredGames.filter(game => (game.offlineCoop === this.offlineCoopFilterEnabled || game.onlineCoop === this.onlineCoopFilterEnabled || game.lanSupport === this.lanSupportFilterEnabled)); - } + if (this.activeGenreFilters.length > 0) { + games = games.filter(game => this.activeGenreFilters.every(activeGenreFilter => game.genres?.map(g => g.slug).includes(activeGenreFilter))); + } - if(this.activeGenreFilters.length > 0) { - filteredGames = filteredGames.filter(game => this.activeGenreFilters.every(activeGenreFilter => game.genres?.map(g => g.slug).includes(activeGenreFilter))); - } + if (this.activeThemeFilters.length > 0) { + games = games.filter(game => this.activeThemeFilters.every(activeThemeFilter => game.themes?.map(g => g.slug).includes(activeThemeFilter))); + } - if(this.activeThemeFilters.length > 0) { - filteredGames = filteredGames.filter(game => this.activeThemeFilters.every(activeThemeFilter => game.themes?.map(g => g.slug).includes(activeThemeFilter))); - } + return games; + } - this.games = filteredGames; - }) + sortGames(games: DetectedGameDto[]): DetectedGameDto[] { + games = games.sort((g1, g2) => { + // @ts-ignore + let f1 = g1[this.selectedSortOption.field]; + // @ts-ignore + let f2 = g2[this.selectedSortOption.field]; + + if(f1 > f2) return 1; + if(f1 < f2) return -1; + return 0; + }); + if(this.selectedSortOption.direction === "desc") games = games.reverse(); + return games; } toggleGenreFilter(slug: string): void { - if(this.activeGenreFilters.includes(slug)) { + if (this.activeGenreFilters.includes(slug)) { const index = this.activeGenreFilters.indexOf(slug, 0); if (index > -1) { @@ -98,11 +139,11 @@ export class LibraryOverviewComponent implements AfterContentInit { this.activeGenreFilters.push(slug); } - this.filterGames(); + this.refreshLibraryView(); } toggleThemeFilter(slug: string) { - if(this.activeThemeFilters.includes(slug)) { + if (this.activeThemeFilters.includes(slug)) { const index = this.activeThemeFilters.indexOf(slug, 0); if (index > -1) { @@ -113,7 +154,7 @@ export class LibraryOverviewComponent implements AfterContentInit { this.activeThemeFilters.push(slug); } - this.filterGames(); + this.refreshLibraryView(); } } diff --git a/frontend/src/app/layouts/navbar-layout/navbar-layout.component.ts b/frontend/src/app/layouts/navbar-layout/navbar-layout.component.ts index 9ce31f1..397b20a 100644 --- a/frontend/src/app/layouts/navbar-layout/navbar-layout.component.ts +++ b/frontend/src/app/layouts/navbar-layout/navbar-layout.component.ts @@ -4,7 +4,7 @@ import {Component, OnInit} from '@angular/core'; selector: 'app-navbar-layout', template: `
-
+