Added pre and post game UI states
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
.app-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: [],
|
||||||
|
|||||||
24
src/app/game-card/game-card.component.css
Normal file
24
src/app/game-card/game-card.component.css
Normal 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;
|
||||||
|
}
|
||||||
16
src/app/game-card/game-card.component.html
Normal file
16
src/app/game-card/game-card.component.html
Normal 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>
|
||||||
33
src/app/game-card/game-card.component.ts
Normal file
33
src/app/game-card/game-card.component.ts
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
src/app/match-results/match-results.component.css
Normal file
23
src/app/match-results/match-results.component.css
Normal 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;
|
||||||
|
}
|
||||||
17
src/app/match-results/match-results.component.html
Normal file
17
src/app/match-results/match-results.component.html
Normal 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>
|
||||||
21
src/app/match-results/match-results.component.ts
Normal file
21
src/app/match-results/match-results.component.ts
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
Reference in New Issue
Block a user