Skip to content

Commit

Permalink
Merge pull request #2 from halomademeapc/feature/game
Browse files Browse the repository at this point in the history
First usable version
  • Loading branch information
Alex Griffith committed Jan 21, 2019
2 parents 0f3b862 + e527000 commit 56be08e
Show file tree
Hide file tree
Showing 36 changed files with 764 additions and 84 deletions.
14 changes: 7 additions & 7 deletions ClientApp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
"@ng-bootstrap/ng-bootstrap": "^4.0.1",
"core-js": "^2.5.4",
"rxjs": "~6.3.3",
"zone.js": "~0.8.26"
"zone.js": "^0.8.28"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.12.1",
"@angular/cli": "~7.0.4",
"@angular/compiler-cli": "~7.0.0",
"@angular/language-service": "~7.0.0",
"@types/jasmine": "~2.8.8",
"@types/jasmine": "^2.8.15",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"@types/signalr": "^2.2.35",
Expand Down
7 changes: 7 additions & 0 deletions ClientApp/src/app/models/entities/user.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { Room } from './room';
import { Emblem } from './emblem';
import { Card } from './card';

export class User {
public id: string;
public roomId: string;
public displayName: string;
public isHost: boolean;
public emblemId: number;
public currentCardId: number;
public room: Room;
public emblem: Emblem;
public currentCard: Card;
}
2 changes: 2 additions & 0 deletions ClientApp/src/app/pages/ingame/ingame.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
</div>
</div>
<div *ngIf="isInGame">
<app-round-status></app-round-status>
<app-playfield></app-playfield>
<app-card-list [cards]="room?.deck?.cards"></app-card-list>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions ClientApp/src/app/pages/landing/landing.component.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<div class="full-height d-flex flex-column justify-content-center">
<div class="container">
<div class="card my-5 p-3">
<h2 class="mb-3 display-4">Planning Poker</h2>
<h2 class="display-4">Planning Poker</h2>
<small class="mb-3 text-muted">Made by <a href="https://www.halomademeapc.com">Alex Griffith</a></small>
<app-create-room></app-create-room>
</div>
</div>
</div>
</div>
9 changes: 7 additions & 2 deletions ClientApp/src/app/services/notification.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Injectable } from '@angular/core';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class NotificationService {
public messages: EventEmitter<string>;

constructor() { }
constructor() {
this.messages = new EventEmitter<string>();
}

public notify = (msg: string) => this.messages.emit(msg);
}
100 changes: 95 additions & 5 deletions ClientApp/src/app/services/poker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { User } from '../models/entities/user';
import { ListChange } from '../models/list-change';
import { Emblem } from '../models/entities/emblem';
import { Card } from '../models/entities/card';
import { NotificationService } from './notification.service';
import { Router } from '@angular/router';

@Injectable({
providedIn: 'root'
Expand All @@ -27,11 +29,22 @@ export class PokerService {

public userJoins: EventEmitter<ListChange<User>> = new EventEmitter<ListChange<User>>();
public userLeaves: EventEmitter<ListChange<User>> = new EventEmitter<ListChange<User>>();
public roomClosing: EventEmitter<void> = new EventEmitter<void>();
public cardPlays: EventEmitter<ListChange<User>> = new EventEmitter<ListChange<User>>();
public roundStarts: EventEmitter<void> = new EventEmitter<void>();
public roundEnds: EventEmitter<void> = new EventEmitter<void>();
public taglineUpdated: EventEmitter<string> = new EventEmitter<string>();
public timerStarts: EventEmitter<number> = new EventEmitter<number>();
public playerChanges: EventEmitter<User> = new EventEmitter<User>();

constructor(private http: HttpClient) {
public player: User;

constructor(private http: HttpClient, private notifications: NotificationService, private router: Router) {
this.initializeHubWatches().subscribe();
}

public getEmblemUrl = (id: number): string => `api/emblems/${id}/image`;

public reset = (): void => this.room = null;

public createRoom = (request: CreateRoomRequest): Observable<void> =>
Expand All @@ -46,15 +59,48 @@ export class PokerService {
this.http.get<Emblem[]>('api/emblems').pipe(map(e => this.emblems = e))

public getRoom = (roomId: string): Observable<Room> =>
this.room ? of(this.room) :
this.http.get<Room>(`api/rooms/${roomId}`).pipe(map(r => this.room = r))
this.http.get<Room>(`api/rooms/${roomId}`).pipe(map(r => this.room = r))

public getPlayers = (): Observable<Array<User>> =>
this.room
? this.http.get<Room>(`api/rooms/${this.room.id}`).pipe(map(r => r.users))
: of(new Array<User>())

public getTagline = (): Observable<string> =>
this.room
? this.http.get<Room>(`api/rooms/${this.room.id}`).pipe(map(r => r.tagLine))
: of('')

public joinRoom = (request: JoinRoomRequest): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('joinRoom', request))))

public leaveRoom = (): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('leaveRoom'))))

public playCard = (cardId: number): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('playCard', cardId))))

public startRound = (): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('startRound'))))

public updateTagline = (newTagline: string): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('updateTagline', newTagline))))

public storeTagline = (newTagline: string): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('storeTagline', newTagline))))

public startTimer = (milliseconds: number): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('startTimer', milliseconds))))

public endRound = (): Observable<void> =>
this.getHub().pipe(flatMap((hub: HubConnection) => from(hub.invoke('endRound'))))

public checkAvailability = (id: string): Observable<boolean> =>
this.http.get(`api/rooms/available/${id}`, { observe: 'response', responseType: 'text' as 'json' })
.pipe(map(r => r.body === 'true'))

public getCards = (deckId: number): Observable<Card[]> =>
this.getDecks().pipe(map(ds => {
console.log(ds, deckId);
const deck = ds.find(d => d.id === deckId);
return deck.cards.sort(this.orderCards);
}))
Expand All @@ -78,7 +124,15 @@ export class PokerService {

private initializeHubWatches = (): Observable<void> => forkJoin(
this.watchUsersJoin(),
this.watchusersLeave()
this.watchusersLeave(),
this.watchRoomClose(),
this.watchCardPlays(),
this.watchRoundStarts(),
this.watchRoundEnds(),
this.watchTaglineUpdates(),
this.watchTimerStarts(),
this.watchSelfInfo(),
this.watchMessages()
).pipe(map(() => { }))

private watchUsersJoin = (): Observable<void> =>
Expand All @@ -92,4 +146,40 @@ export class PokerService {
this.users = d.collection;
this.userLeaves.emit(d);
})))

private watchRoomClose = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('RoomClosed', () => {
this.roomClosing.emit();
this.router.navigate(['/']);
})))

private watchCardPlays = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('CardPlayed', (c: ListChange<User>) => {
this.users = c.collection;
this.cardPlays.emit(c);
})))

private watchRoundStarts = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('RoundStarted', () => this.roundStarts.emit())))

private watchRoundEnds = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('RoundEnded', () => this.roundEnds.emit())))

private watchTaglineUpdates = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('TaglineUpdated', (t: string) => {
this.room.tagLine = t;
this.taglineUpdated.emit(t);
})))

private watchTimerStarts = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('TimerStarted', (d: number) => this.timerStarts.emit(d))))

private watchSelfInfo = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('Self', (u: User) => {
this.player = u;
this.playerChanges.emit(u);
})))

private watchMessages = (): Observable<void> =>
this.getHub().pipe(map(h => h.on('Message', this.notifications.notify)))
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
import { Component, Input, SimpleChanges, Output, EventEmitter, OnChanges } from '@angular/core';
import { Component, Input, SimpleChanges, Output, EventEmitter, OnChanges, OnInit } from '@angular/core';
import { Card } from 'src/app/models/entities/card';
import { PokerService } from 'src/app/services/poker.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
selector: 'app-card-list',
templateUrl: './card-list.component.html',
styleUrls: ['./card-list.component.scss']
})
export class CardListComponent implements OnChanges {
export class CardListComponent implements OnChanges, OnInit {
@Input() cards: Array<Card>;
@Input() isEnabled: boolean;
@Output() cardSelected: EventEmitter<number> = new EventEmitter<number>();
isEnabled = true;
selectedCardId: number = null;

constructor() { }
constructor(private service: PokerService) { }

ngOnChanges(changes: SimpleChanges): void {
if (changes.isEnabled && changes.isEnabled.currentValue !== changes.isEnabled.previousValue) {
this.selectedCardId = null;
}
}

ngOnInit(): void {
this.watchRoundEnd().subscribe();
this.watchRoundStart().subscribe();
}

onCardSelected = (cardId: number): void => {
this.cardSelected.emit(cardId);
this.service.playCard(cardId).subscribe();
this.selectedCardId = cardId;
}

watchRoundEnd = (): Observable<boolean> =>
this.service.roundEnds.pipe(map(() => this.isEnabled = false))

watchRoundStart = (): Observable<void> =>
this.service.roundStarts.pipe(map(() => {
this.isEnabled = true;
this.selectedCardId = null;
}))
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
<div class="input-group-prepend">
<label for="id" class="input-group-text">Room Id</label>
</div>
<input class="form-control" name="id" type="number" [(ngModel)]="model.id">
<input class="form-control" [class.is-valid]="isAvailable" [class.is-invalid]="isAvailable==false" name="id" type="number"
[(ngModel)]="model.id" (input)="onInput($event.target.value)" required>
<div *ngIf="isAvailable==true" class="valid-feedback">
<i class="mdi mdi-check"></i>
This room is available. </div>
<div *ngIf="isAvailable==false" class="invalid-feedback">
<i class="mdi mdi-close"></i>
This room is taken.
<a [routerLink]="['/room', model.id]">Join it</a>
</div>
</div>

<div class="input-group mb-3">
<div class="input-group-prepend">
<label for="name" class="input-group-text">Name</label>
</div>
<input class="form-control" type="text" name="name" [(ngModel)]="model.name">
<input class="form-control" type="text" name="name" [(ngModel)]="model.name" required>
</div>

<div class="input-group mb-3">
Expand All @@ -25,7 +34,7 @@
<div class="input-group-prepend">
<label class="input-group-text" for="deck">Deck</label>
</div>
<select class="form-control" name="deck" [(ngModel)]="model.deckId">
<select class="form-control" name="deck" [(ngModel)]="model.deckId" required>
<option *ngFor="let d of decks" [value]="d.id">{{d.name}}</option>
</select>
</div>
Expand All @@ -40,6 +49,7 @@
</div>

<div class="d-flex flex-row justify-content-end">
<button type="submit" class="btn btn-primary">Create Room</button>
<button type="submit" class="btn btn-primary" [disabled]="!isAvailable || !model.deckId || !model.id || !model.name">Create
Room</button>
</div>
</form>
</form>
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, EventEmitter } from '@angular/core';
import { CreateRoomRequest } from 'src/app/models/create-room-request';
import { PokerService } from 'src/app/services/poker.service';
import { Deck } from 'src/app/models/entities/deck';
import { map } from 'rxjs/operators';
import { map, debounceTime, flatMap } from 'rxjs/operators';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';

@Component({
selector: 'app-create-room',
Expand All @@ -13,11 +14,15 @@ import { Router } from '@angular/router';
export class CreateRoomComponent implements OnInit {
model: CreateRoomRequest = new CreateRoomRequest();
decks: Array<Deck> = new Array<Deck>();
isAvailable: boolean;

private idInputs: EventEmitter<string> = new EventEmitter<string>();

constructor(private service: PokerService, private router: Router) { }

ngOnInit() {
this.service.getDecks().subscribe(d => this.decks = d);
this.checkId().subscribe();
}

// tslint:disable-next-line:triple-equals
Expand All @@ -28,4 +33,20 @@ export class CreateRoomComponent implements OnInit {
this.router.navigate(['/room', this.model.id]);
})).subscribe();
}

onInput = (currentId: string) => {
this.isAvailable = null;
if (currentId) {
this.idInputs.emit(currentId);
}
}

checkId = (): Observable<void> => this.idInputs
.pipe(debounceTime(300))
.pipe(flatMap(i => {
return this.service.checkAvailability(i);
}))
.pipe(map(r => {
this.isAvailable = r;
}))
}
Loading

0 comments on commit 56be08e

Please sign in to comment.