Merge branch 'zeldaret:master' into lost-page

This commit is contained in:
Parker Burnett
2022-10-04 22:49:17 -07:00
committed by GitHub
17 changed files with 270 additions and 35 deletions
@@ -16,7 +16,6 @@ section {
$color-bg-hl 80%,
$color-bg);
max-width: 800px;
margin: auto;
}
@@ -0,0 +1,26 @@
<mat-card>
<a [href]="data.routingURL">
<mat-card-header>
<mat-card-title>
{{ data.title }}
<fa-icon [icon]="external"></fa-icon>
</mat-card-title>
<mat-card-subtitle>
<ng-container *ngIf="total != undefined">Code decompiled: <strong>{{ total }}</strong></ng-container>
&nbsp;
</mat-card-subtitle>
</mat-card-header>
</a>
<mat-card-actions>
<a mat-button *ngIf="data.links.hasOwnProperty('github')" [href]="data.links.github" title="Github">
<fa-icon [icon]="github"></fa-icon>
<div class="caption">GitHub</div>
</a>
<a mat-button *ngIf="data.links.hasOwnProperty('progress')" [href]="data.links.progress" title="Progress">
<fa-icon [icon]="chart"></fa-icon>
<div class="caption">Progress history</div>
<!-- <mat-icon [svgIcon]="chart"></mat-icon> -->
</a>
</mat-card-actions>
</mat-card>
@@ -0,0 +1,25 @@
mat-card > a {
text-decoration: none;
}
img[mat-card-avatar] {
border-radius: 0;
object-fit: contain;
}
mat-card-actions {
text-align: center;
}
mat-card-title > fa-icon {
font-size: 16px;
vertical-align: center;
}
mat-card-actions fa-icon {
vertical-align: center;
}
mat-card-actions a {
color: white;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GameComponent } from '../../game/game.component';
describe('GameComponent', () => {
let component: GameComponent;
let fixture: ComponentFixture<GameComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GameComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GameComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,48 @@
import { Component, Input, OnChanges } from '@angular/core';
import { IExternalGame } from '../../games.service.interface';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
import { faChartLine, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
import { GamesService } from '../../games.service';
/**
* Displays an externally-linked game's name, decomp progress, and links.
*/
@Component({
selector: 'external-game-summary',
templateUrl: './external-game-summary.component.html',
styleUrls: ['./external-game-summary.component.scss']
})
export class ExternalGameSummaryComponent implements OnChanges {
constructor(private gamesService: GamesService) { }
/**
* The game data to use.
*/
@Input() data: IExternalGame;
/**
* Shield data.
*/
shield: string;
/**
* The percentage to display for completion.
*/
total?: string;
/**
* Icon definitions.
*/
github = faGithub;
external = faExternalLinkAlt;
chart = faChartLine;
ngOnChanges(): void {
if (this.data.shieldURL) {
this.gamesService.getGameShield(`${this.data.shieldURL}`).subscribe(
res => this.total = res.message,
err => console.error(err)
);
}
}
}
@@ -1,3 +1,4 @@
<div fxLayout="row wrap" fxLayoutAlign="center">
<game-summary fxFlex="25%" fxFlex.xs="100%" fxFlex.sm="33%" *ngFor="let game of games" [data]="game"></game-summary>
<external-game-summary fxFlex="25%" fxFlex.xs="100%" fxFlex.sm="33%" *ngFor="let game of externalGames" [data]="game"></external-game-summary>
</div>
@@ -2,7 +2,12 @@ div {
margin-right: -1em;
}
game-summary {
h2 {
margin: auto;
width: max-content;
}
game-summary, external-game-summary {
flex-grow: 0;
margin-bottom: 12px;
margin-right: 1em;
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { GamesService } from '../games.service';
import { IGame } from '../games.service.interface';
import { IGame, IExternalGame } from '../games.service.interface';
/**
* Container for all game summaries.
@@ -18,12 +18,20 @@ export class GameSummariesComponent implements OnInit {
* List of games to use for "GameSummary" components.
*/
games: IGame[] = [];
/**
* List of external games to use for "ExternalGameSummary" components.
*/
externalGames: IExternalGame[] = [];
ngOnInit(): void {
this.gamesService.getGames().subscribe(
res => this.games = res,
err => console.error(err)
)
this.gamesService.getExternalGames().subscribe(
res => this.externalGames = res,
err => console.error(err)
)
}
}
@@ -1,18 +1,19 @@
<mat-card>
<a [routerLink]="'/games/' + data.slug">
<a [routerLink]="data.routingURL">
<mat-card-header>
<mat-card-title>
{{ data.title }}
</mat-card-title>
<mat-card-subtitle *ngIf="total!=null">Percent completion: <strong>{{ (total * 100).toFixed(2) }}%</strong></mat-card-subtitle>
<mat-card-subtitle>
<ng-container *ngIf="total != undefined">Code decompiled: <strong>{{ total }}</strong></ng-container>
&nbsp;
</mat-card-subtitle>
</mat-card-header>
</a>
<mat-card-actions>
<a mat-icon-button *ngIf="data.links.hasOwnProperty('github')" [href]="data.links.github" title="Github">
<a mat-button *ngIf="data.links.hasOwnProperty('github')" [href]="data.links.github" title="Github">
<fa-icon [icon]="github"></fa-icon>
</a>
<a mat-icon-button *ngIf="data.links.hasOwnProperty('trello')" [href]="data.links.trello" title="Trello">
<fa-icon [icon]="trello"></fa-icon>
<div class="caption">GitHub</div>
</a>
</mat-card-actions>
</mat-card>
@@ -10,3 +10,11 @@ img[mat-card-avatar] {
mat-card-actions {
text-align: center;
}
mat-card-actions fa-icon {
vertical-align: center;
}
mat-card-actions a {
color: white;
}
@@ -22,29 +22,20 @@ export class GameSummaryComponent implements OnChanges {
/**
* The percentage to display for completion.
*/
total: number = null;
total?: string;
/**
* Icon definition.
* Icon definitions.
*/
github = faGithub;
/**
* Icon definition.
*/
trello = faTrello;
ngOnChanges(): void {
// Matching is second
this.gamesService.getGameCSV(this.data.csvs.matching).subscribe(
res => {
const points = res.split("\n").filter((line) => line != "");
const latestPoint = points[points.length - 1];
const column = latestPoint.split(",");
let i = this.data.progressMeta[0].index;
this.total = +column[i] / +column[i + 1];
},
err => console.error(err)
);
if (this.data.shieldURL) {
this.gamesService.getGameShield(`${this.data.shieldURL}`).subscribe(
res => this.total = res.message,
err => console.error(err)
);
}
}
}
+9 -7
View File
@@ -39,13 +39,15 @@ export class GameComponent implements OnInit {
}
else {
this.game = res[index];
forkJoin([
this.gamesService.getGameCSV(this.game.csvs.nonmatching),
this.gamesService.getGameCSV(this.game.csvs.matching)
]).subscribe(
res => this.csvData = res,
err => console.error(err)
);
if (this.game.progressMeta.length > 0) {
forkJoin([
this.gamesService.getGameCSV(this.game.csvs.nonmatching),
this.gamesService.getGameCSV(this.game.csvs.matching)
]).subscribe(
res => this.csvData = res,
err => console.error(err)
);
}
}
},
err => {
+5 -1
View File
@@ -5,6 +5,7 @@ import { GamesService } from './games.service';
import { GameSummariesComponent } from './game-summaries/game-summaries.component';
import { GameSummaryComponent } from './game-summaries/game-summary/game-summary.component';
import { GameComponent } from './game/game.component';
import { ExternalGameSummaryComponent } from './game-summaries/external-game-summary/external-game-summary.component'
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatGridListModule } from '@angular/material/grid-list';
@@ -17,6 +18,8 @@ import { ChartModule } from 'angular-highcharts';
import { GameFAQComponent } from './game/game-faq/game-faq.component';
import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown';
// function that returns `MarkedOptions` with renderer override
export function markedOptionsFactory(): MarkedOptions {
const renderer = new MarkedRenderer();
@@ -48,7 +51,8 @@ export function markedOptionsFactory(): MarkedOptions {
GameComponent,
GameProgressComponent,
GameChartComponent,
GameFAQComponent
GameFAQComponent,
ExternalGameSummaryComponent
],
imports: [
CommonModule,
+65
View File
@@ -12,6 +12,14 @@ export interface IGame {
* The game's full title.
*/
title: string;
/**
* The URL for the card title to link to.
*/
routingURL: string;
/**
* The URL of the shield json to read.
*/
shieldURL: string;
/**
* The name of the "FAQ" MD to read.
*/
@@ -34,6 +42,32 @@ export interface IGame {
progressMeta: IChart[];
}
/**
* Game information to display and for project progress processing.
*/
export interface IExternalGame {
/**
* The string to use to identify the game.
*/
slug: string;
/**
* The game's full title.
*/
title: string;
/**
* The URL for the card title to link to.
*/
routingURL: string;
/**
* Links to display as an icon. The key defines the icon, and the value is the link href.
*/
links: {[type: string]: string};
/**
* The URL of the shield json to read.
*/
shieldURL: string;
}
/**
* Resources to display within the Resources tab.
*/
@@ -133,6 +167,25 @@ export interface ISubdivision {
description: string;
}
/**
* Describes a game's shield output.
*/
export interface IShield {
/**
* Static label in left half.
*/
label: string;
/**
* Generally the progress number.
*/
message: string;
/**
* Colour of the message background.
*/
color: string;
}
/**
* Functions to implement within "GamesService".
*/
@@ -143,6 +196,11 @@ export interface IGamesService {
*/
getGames(): Observable<IGame[]>;
/**
* Get the information for all supported external games.
*/
getExternalGames(): Observable<IExternalGame[]>;
/**
* Get all of the resources.
*/
@@ -155,6 +213,13 @@ export interface IGamesService {
*/
getGameCSV(filename: string): Observable<string>;
/**
* Get the Shield data for a game.
*
* @param filename The filename of the desired CSV.
*/
getGameShield(filename: string): Observable<IShield>;
/**
* Get the FAQ data for a game.
*
+9 -1
View File
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { IGame, IGamesService, IResource } from './games.service.interface';
import { IExternalGame, IGame, IGamesService, IResource, IShield } from './games.service.interface';
/**
* Obtains data relating to the games.
@@ -17,6 +17,10 @@ export class GamesService implements IGamesService {
return this.http.get<IGame[]>("/assets/json/games.json");
}
getExternalGames(): Observable<IExternalGame[]> {
return this.http.get<IExternalGame[]>("/assets/json/external-games.json");
}
getResources(): Observable<IResource[]> {
return this.http.get<IResource[]>("/assets/json/resources.json");
}
@@ -25,6 +29,10 @@ export class GamesService implements IGamesService {
return this.http.get(`/assets/csv/${filename}.csv`, { responseType: "text" });
}
getGameShield(filename: string): Observable<IShield> {
return this.http.get<IShield>(filename);
}
getGameFAQ(filename: string): Observable<string> {
return this.http.get(`/assets/md/${filename}.md`, { responseType: "text" });
}
+12
View File
@@ -0,0 +1,12 @@
[
{
"slug": "botw",
"title": "Breath of the Wild",
"routingURL": "https://botw.link",
"links": {
"github": "https://github.com/zeldaret/botw",
"progress": "https://botw.link/progress"
},
"shieldURL": "https://botw.link/badges/progress.json"
}
]
+7
View File
@@ -2,9 +2,11 @@
{
"slug": "oot",
"title": "Ocarina of Time",
"routingURL": "games/oot",
"links": {
"github": "https://www.github.com/zeldaret/oot"
},
"shieldURL": "assets/csv/progress-oot-shield.json",
"faq": "oot_faq",
"csvs": {
"matching": "progress-oot-matching",
@@ -44,9 +46,11 @@
{
"slug": "mm",
"title": "Majora's Mask",
"routingURL": "games/mm",
"links": {
"github": "https://www.github.com/zeldaret/mm"
},
"shieldURL": "assets/csv/progress-mm-shield.json",
"faq": "mm_faq",
"csvs": {
"matching": "progress-mm-matching",
@@ -130,9 +134,11 @@
{
"slug": "tmc",
"title": "The Minish Cap",
"routingURL": "games/tmc",
"links": {
"github": "https://www.github.com/zeldaret/tmc"
},
"shieldURL": "assets/csv/progress-tmc-shield.json",
"faq": "tmc_faq",
"csvs": {
"matching": "progress-tmc-matching",
@@ -175,6 +181,7 @@
{
"slug": "tp",
"title": "Twilight Princess",
"routingURL": "games/tp",
"links": {
"github": "https://www.github.com/zeldaret/tp"
},