1
0

Added pre and post game UI states

This commit is contained in:
2020-05-13 12:55:31 -04:00
parent 6e842bc392
commit 3aec16246a
12 changed files with 190 additions and 11 deletions

View File

@@ -0,0 +1,7 @@
.app-body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 1rem;
}

View File

@@ -6,6 +6,11 @@
<div class="app-body"> <div class="app-body">
<!-- score-card component --> <!-- score-card component -->
<!-- game-card (reactive form) component --> <ng-container *ngIf="(gameStatus$ | async) !== 'post'; else matchResults">
<app-game-card></app-game-card>
</ng-container>
<ng-template #matchResults>
<app-match-results></app-match-results>
</ng-template>
<!-- game-history component --> <!-- game-history component -->
</div> </div>

View File

@@ -1,9 +1,17 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { GameFacade } from './store/game';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css']
}) })
export class AppComponent { export class AppComponent {
gameStatus$ = this.gameFacade.gameStatus$;
constructor(
private readonly gameFacade: GameFacade
) { }
} }

View File

@@ -1,26 +1,47 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { StoreModule } from '@ngrx/store'; import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects'; import { EffectsModule } from '@ngrx/effects';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatRadioModule } from '@angular/material/radio';
import { MatToolbarModule } from '@angular/material/toolbar';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { GameStoreModule } from './store'; import { GameStoreModule } from './store';
import { DirectivesModule } from './directives/directives.module';
import { GameCardComponent } from './game-card/game-card.component';
import { MatchResultsComponent } from './match-results/match-results.component';
const materialModules = [
MatButtonModule,
MatCardModule,
MatIconModule,
MatRadioModule,
MatToolbarModule
];
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent AppComponent,
GameCardComponent,
MatchResultsComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
BrowserAnimationsModule, BrowserAnimationsModule,
MatButtonModule, ReactiveFormsModule,
MatToolbarModule, FormsModule,
...materialModules,
StoreModule.forRoot({}, {}), StoreModule.forRoot({}, {}),
EffectsModule.forRoot([]), EffectsModule.forRoot([]),
DirectivesModule,
GameStoreModule GameStoreModule
], ],
providers: [], providers: [],

View File

@@ -0,0 +1,24 @@
:host {
display: block;
}
.game-card {
width: 420px;
}
.full-width-field {
width: 100%;
}
:host ::ng-deep .mat-radio-group {
display: flex;
flex-direction: row;
justify-content: space-around;
margin: 1rem 0;
}
:host ::ng-deep .mat-radio-label {
display: flex;
flex-direction: column-reverse;
margin: 1rem 0;
}

View File

@@ -0,0 +1,16 @@
<mat-card class="game-card">
<form [formGroup]="form" #gameForm="ngForm" (ngSubmit)="submit($event, gameForm)" novalidate>
<mat-card-content>
<mat-radio-group class="full-width-field" formControlName="playerChoice">
<mat-radio-button class="rps-radio-button" *ngFor="let choice of choices" [value]="choice">
<img src="./assets/{{ choice }}-50.png" [alt]="choice" />
</mat-radio-button>
</mat-radio-group>
</mat-card-content>
<mat-card-actions align="center">
<button type="submit" mat-raised-button color="primary" [disabled]="!form.valid">
<mat-icon>play_arrow</mat-icon>
</button>
</mat-card-actions>
</form>
</mat-card>

View File

@@ -0,0 +1,33 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { GameFacade } from "../store/game";
@Component({
selector: 'app-game-card',
templateUrl: './game-card.component.html',
styleUrls: ['./game-card.component.css']
})
export class GameCardComponent implements OnInit {
form: FormGroup;
choices = ['rock', 'paper', 'scissors'];
constructor(
private readonly fb: FormBuilder,
private readonly gameFacade: GameFacade
) { }
ngOnInit(): void {
this.form = this.fb.group({
playerChoice: [undefined, [Validators.required]]
});
}
submit(event, gameForm): void {
event.preventDefault();
this.gameFacade.play(this.form.value.playerChoice);
this.form.reset();
gameForm.resetForm();
}
}

View File

@@ -0,0 +1,23 @@
:host {
display: block;
}
.match-results {
width: 420px;
}
.match-results-text {
font-weight: bold;
}
.match-visual {
display: flex;
flex-direction: row;
margin: 1rem 0;
justify-content: space-around;
width: 100%;
}
.match-visual .vs {
font-size: 3rem;
}

View File

@@ -0,0 +1,17 @@
<mat-card *ngLet="results$ | async; let results;" class="match-results">
<mat-card-title>Match Results</mat-card-title>
<!-- {"playerChoice":"rock","computerChoice":"scissors","result":"win"} -->
<mat-card-content>
<div class="match-results-text">You {{ results.result }}!</div>
<div class="match-visual">
<img src="./assets/{{ results.playerChoice }}-50.png" [alt]="results.playerChoice">
<span class="vs">vs</span>
<img src="./assets/{{ results.computerChoice }}-50.png" [alt]="results.computerChoice">
</div>
</mat-card-content>
<mat-card-actions align="center">
<button type="button" mat-raised-button color="primary" (click)="playAgain()">
Play Again
</button>
</mat-card-actions>
</mat-card>

View File

@@ -0,0 +1,21 @@
import { Component } from '@angular/core';
import { GameFacade } from '../store/game';
@Component({
selector: 'app-match-results',
templateUrl: './match-results.component.html',
styleUrls: ['./match-results.component.css']
})
export class MatchResultsComponent {
results$ = this.gameFacade.recentGameResults$;
constructor(
private readonly gameFacade: GameFacade
) { }
playAgain(): void {
this.gameFacade.reset();
}
}

View File

@@ -1,23 +1,25 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import * as gameActions from './game.actions'; import * as GameActions from './game.actions';
import * as GameQuery from './game.selectors';
@Injectable() @Injectable()
export class GameFacade { export class GameFacade {
recentGameResults$ = this.store.pipe(select(GameQuery.selectRecentMatchResults));
// gameHistory$ = this.store.pipe(select(getGameHistory)); // gameHistory$ = this.store.pipe(select(getGameHistory));
// scores$ = this.store.pipe(select(getScores)); // scores$ = this.store.pipe(select(getScores));
// gameStatus$ = this.store.pipe(select(getGameStatus)); gameStatus$ = this.store.pipe(select(GameQuery.selectGameStatus));
constructor( constructor(
private readonly store: Store private readonly store: Store
) { } ) { }
play(playerChoice: string): void { play(playerChoice: string): void {
this.store.dispatch(gameActions.playMatch({ playerChoice })); this.store.dispatch(GameActions.playMatch({ playerChoice }));
} }
reset(): void { reset(): void {
this.store.dispatch(gameActions.resetMatch()); this.store.dispatch(GameActions.resetMatch());
} }
} }

View File

@@ -4,3 +4,5 @@ import * as fromGame from './game.reducer';
export const selectGameState = createFeatureSelector<fromGame.State>( export const selectGameState = createFeatureSelector<fromGame.State>(
fromGame.gameFeatureKey fromGame.gameFeatureKey
); );
export const selectGameStatus = createSelector(selectGameState, fromGame.getGameStatus);
export const selectRecentMatchResults = createSelector(selectGameState, fromGame.getRecentMatchHistory);