mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
WIP: Proceed with frontend implementation
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
package de.grimsi.gameyfin.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class GameOverviewDto {
|
||||||
|
private String slug;
|
||||||
|
private String title;
|
||||||
|
private String coverId;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.grimsi.gameyfin.mapper;
|
package de.grimsi.gameyfin.mapper;
|
||||||
|
|
||||||
import com.igdb.proto.Igdb;
|
import com.igdb.proto.Igdb;
|
||||||
|
import de.grimsi.gameyfin.dto.GameOverviewDto;
|
||||||
import de.grimsi.gameyfin.entities.DetectedGame;
|
import de.grimsi.gameyfin.entities.DetectedGame;
|
||||||
import de.grimsi.gameyfin.util.ProtobufUtils;
|
import de.grimsi.gameyfin.util.ProtobufUtils;
|
||||||
|
|
||||||
@@ -40,6 +41,14 @@ public class GameMapper {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GameOverviewDto toGameOverviewDto(DetectedGame game) {
|
||||||
|
return GameOverviewDto.builder()
|
||||||
|
.slug(game.getSlug())
|
||||||
|
.title(game.getTitle())
|
||||||
|
.coverId(game.getCoverId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean hasOfflineCoop(List<Igdb.MultiplayerMode> modes) {
|
private static boolean hasOfflineCoop(List<Igdb.MultiplayerMode> modes) {
|
||||||
return modes.stream().anyMatch(Igdb.MultiplayerMode::getOfflinecoop);
|
return modes.stream().anyMatch(Igdb.MultiplayerMode::getOfflinecoop);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package de.grimsi.gameyfin.rest;
|
package de.grimsi.gameyfin.rest;
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.dto.GameOverviewDto;
|
||||||
import de.grimsi.gameyfin.entities.DetectedGame;
|
import de.grimsi.gameyfin.entities.DetectedGame;
|
||||||
import de.grimsi.gameyfin.service.GameService;
|
import de.grimsi.gameyfin.service.GameService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -26,8 +28,20 @@ public class GamesController {
|
|||||||
return gameService.getAllDetectedGames();
|
return gameService.getAllDetectedGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/game/{slug}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public DetectedGame getGame(@PathVariable String slug) {
|
||||||
|
return gameService.getDetectedGame(slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/game-overviews", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public List<GameOverviewDto> getGameOverviews() {
|
||||||
|
return gameService.getGameOverviews();
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/game-mappings", produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(value = "/game-mappings", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public Map<String, String> getGameMappings() {
|
public Map<String, String> getGameMappings() {
|
||||||
return gameService.getAllMappings();
|
return gameService.getAllMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.grimsi.gameyfin.service;
|
package de.grimsi.gameyfin.service;
|
||||||
|
|
||||||
import com.igdb.proto.Igdb;
|
import com.igdb.proto.Igdb;
|
||||||
|
import de.grimsi.gameyfin.dto.GameOverviewDto;
|
||||||
import de.grimsi.gameyfin.entities.DetectedGame;
|
import de.grimsi.gameyfin.entities.DetectedGame;
|
||||||
import de.grimsi.gameyfin.entities.UnmappableFile;
|
import de.grimsi.gameyfin.entities.UnmappableFile;
|
||||||
import de.grimsi.gameyfin.igdb.IgdbWrapper;
|
import de.grimsi.gameyfin.igdb.IgdbWrapper;
|
||||||
@@ -33,6 +34,10 @@ public class GameService {
|
|||||||
return detectedGameRepository.findAll();
|
return detectedGameRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DetectedGame getDetectedGame(String slug) {
|
||||||
|
return detectedGameRepository.findById(slug).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Game with slug '%s' not found in library.".formatted(slug)));
|
||||||
|
}
|
||||||
|
|
||||||
public List<UnmappableFile> getAllUnmappedFiles() {
|
public List<UnmappableFile> getAllUnmappedFiles() {
|
||||||
return unmappableFileRepository.findAll();
|
return unmappableFileRepository.findAll();
|
||||||
}
|
}
|
||||||
@@ -41,6 +46,10 @@ public class GameService {
|
|||||||
return detectedGameRepository.findAll().stream().collect(Collectors.toMap(DetectedGame::getPath, DetectedGame::getTitle));
|
return detectedGameRepository.findAll().stream().collect(Collectors.toMap(DetectedGame::getPath, DetectedGame::getTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<GameOverviewDto> getGameOverviews() {
|
||||||
|
return detectedGameRepository.findAll().stream().map(GameMapper::toGameOverviewDto).toList();
|
||||||
|
}
|
||||||
|
|
||||||
public DetectedGame mapUnmappedFile(Long unmappedGameId, String igdbSlug) {
|
public DetectedGame mapUnmappedFile(Long unmappedGameId, String igdbSlug) {
|
||||||
|
|
||||||
UnmappableFile unmappableFile = unmappableFileRepository.findById(unmappedGameId)
|
UnmappableFile unmappableFile = unmappableFileRepository.findById(unmappedGameId)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {DetectedGameDto} from "../models/dtos/DetectedGameDto";
|
import {DetectedGameDto} from "../models/dtos/DetectedGameDto";
|
||||||
|
import {GameOverviewDto} from "../models/dtos/GameOverviewDto";
|
||||||
|
|
||||||
export interface GamesApi {
|
export interface GamesApi {
|
||||||
getAllGames(): Observable<DetectedGameDto[]>;
|
getAllGames(): Observable<DetectedGameDto[]>;
|
||||||
|
getGame(slug: String): Observable<DetectedGameDto>;
|
||||||
|
getGameOverviews(): Observable<GameOverviewDto[]>;
|
||||||
getAllGameMappings(): Observable<Map<string, string>>;
|
getAllGameMappings(): Observable<Map<string, string>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {PageNotFoundComponent} from "./components/page-not-found/page-not-found.
|
|||||||
import {NavbarLayoutComponent} from "./layouts/navbar-layout/navbar-layout.component";
|
import {NavbarLayoutComponent} from "./layouts/navbar-layout/navbar-layout.component";
|
||||||
import {NotImplementedComponent} from "./components/not-implemented/not-implemented.component";
|
import {NotImplementedComponent} from "./components/not-implemented/not-implemented.component";
|
||||||
import {LibraryOverviewComponent} from "./components/library-overview/library-overview.component";
|
import {LibraryOverviewComponent} from "./components/library-overview/library-overview.component";
|
||||||
|
import {GameDetailViewComponent} from "./components/game-detail-view/game-detail-view.component";
|
||||||
|
|
||||||
const appRoutes: Routes = [
|
const appRoutes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -16,16 +17,8 @@ const appRoutes: Routes = [
|
|||||||
component: LibraryOverviewComponent
|
component: LibraryOverviewComponent
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'games',
|
path: 'game/:slug',
|
||||||
component: NotImplementedComponent
|
component: GameDetailViewComponent
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'info',
|
|
||||||
component: NotImplementedComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'config',
|
|
||||||
component: NotImplementedComponent
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="game-cover-container">
|
<div class="game-cover-container">
|
||||||
<a routerLink="game/{{game.slug}}">
|
<a routerLink="/game/{{game.slug}}">
|
||||||
<img src="{{'v1/images/' + game.coverId}}" alt="Cover of {{game.title}}">
|
<img src="{{'v1/images/' + game.coverId}}" alt="Cover of {{game.title}}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
import {GameOverviewDto} from "../../models/dtos/GameOverviewDto";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'game-cover',
|
selector: 'game-cover',
|
||||||
@@ -8,9 +8,10 @@ import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
|||||||
})
|
})
|
||||||
export class GameCoverComponent implements OnInit {
|
export class GameCoverComponent implements OnInit {
|
||||||
|
|
||||||
@Input() game!: DetectedGameDto;
|
@Input() game!: GameOverviewDto;
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<p>game-detail-view works!</p>
|
<pre><code>{{game | json}}</code></pre>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
||||||
|
import {GamesService} from "../../services/games.service";
|
||||||
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-game-detail-view',
|
selector: 'app-game-detail-view',
|
||||||
@@ -7,7 +11,24 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class GameDetailViewComponent implements OnInit {
|
export class GameDetailViewComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
game!: DetectedGameDto;
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private gamesService: GamesService) {
|
||||||
|
this.route.params.subscribe( params => {
|
||||||
|
this.gamesService.getGame(params['slug']).subscribe({
|
||||||
|
next: game => this.game = game,
|
||||||
|
error: error => {
|
||||||
|
if(error.status === 404) {
|
||||||
|
this.router.navigate(['/library']);
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,2 @@
|
|||||||
<mat-toolbar role="heading">
|
<mat-toolbar role="heading">
|
||||||
|
|
||||||
<object type="image/svg+xml" data="../../../assets/graphics/game-radar-logo-tilted-interactive.svg" class="logo">
|
|
||||||
Game-Radar Logo
|
|
||||||
</object>
|
|
||||||
|
|
||||||
<nav mat-tab-nav-bar>
|
|
||||||
<a mat-tab-link *ngFor="let item of tabNavItems"
|
|
||||||
(click)="setActiveItem(item)"
|
|
||||||
[disabled]="!item.enabled"
|
|
||||||
routerLink="{{item.route}}"
|
|
||||||
routerLinkActive #rla="routerLinkActive"
|
|
||||||
[active]="rla.isActive">
|
|
||||||
<mat-icon class="menu-item-icon">{{item.icon}}</mat-icon>
|
|
||||||
{{item.title}}
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<span class="spacer"></span>
|
|
||||||
|
|
||||||
<p id="username" class="mat-hint">TestUser</p>
|
|
||||||
|
|
||||||
<button mat-icon-button [matMenuTriggerFor]="headerDropDown">
|
|
||||||
<mat-icon>more_vert</mat-icon>
|
|
||||||
</button>
|
|
||||||
<mat-menu #headerDropDown="matMenu">
|
|
||||||
<button mat-menu-item *ngFor="let item of dropDownItems"
|
|
||||||
(click)="item.action()">
|
|
||||||
<mat-icon>{{item.icon}}</mat-icon>
|
|
||||||
<span>{{item.title}}</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {AfterViewInit, Component} from '@angular/core';
|
import {AfterViewInit, Component} from '@angular/core';
|
||||||
import {GamesService} from "../../services/games.service";
|
import {GamesService} from "../../services/games.service";
|
||||||
import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
||||||
|
import {GameOverviewDto} from "../../models/dtos/GameOverviewDto";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gameserver-list',
|
selector: 'app-gameserver-list',
|
||||||
@@ -9,15 +10,15 @@ import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
|
|||||||
})
|
})
|
||||||
export class LibraryOverviewComponent implements AfterViewInit {
|
export class LibraryOverviewComponent implements AfterViewInit {
|
||||||
|
|
||||||
detectedGames: DetectedGameDto[] = [];
|
detectedGames: GameOverviewDto[] = [];
|
||||||
loading: boolean = true;
|
loading: boolean = true;
|
||||||
|
|
||||||
constructor(private gameServerService: GamesService) {
|
constructor(private gameServerService: GamesService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.gameServerService.getAllGames().subscribe(
|
this.gameServerService.getGameOverviews().subscribe(
|
||||||
(detectedGames: DetectedGameDto[]) => {
|
(detectedGames: GameOverviewDto[]) => {
|
||||||
this.detectedGames = detectedGames;
|
this.detectedGames = detectedGames;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export class GameOverviewDto {
|
||||||
|
slug!: string;
|
||||||
|
title!: string;
|
||||||
|
coverId!: string;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import {GamesApi} from "../api/GamesApi";
|
|||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {DetectedGameDto} from "../models/dtos/DetectedGameDto";
|
import {DetectedGameDto} from "../models/dtos/DetectedGameDto";
|
||||||
|
import {GameOverviewDto} from "../models/dtos/GameOverviewDto";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -18,6 +19,14 @@ export class GamesService implements GamesApi {
|
|||||||
return this.http.get<DetectedGameDto[]>(this.apiPath);
|
return this.http.get<DetectedGameDto[]>(this.apiPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGame(slug: String): Observable<DetectedGameDto> {
|
||||||
|
return this.http.get<DetectedGameDto>(`${this.apiPath}/game/${slug}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGameOverviews(): Observable<GameOverviewDto[]> {
|
||||||
|
return this.http.get<GameOverviewDto[]>(`${this.apiPath}/game-overviews`);
|
||||||
|
}
|
||||||
|
|
||||||
getAllGameMappings(): Observable<Map<string, string>> {
|
getAllGameMappings(): Observable<Map<string, string>> {
|
||||||
return this.http.get<Map<string, string>>(`${this.apiPath}/game-mappings`);
|
return this.http.get<Map<string, string>>(`${this.apiPath}/game-mappings`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user