Skip to content

Commit

Permalink
feat: player list page (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Aug 4, 2019
1 parent 21b6689 commit 4a8cb85
Show file tree
Hide file tree
Showing 14 changed files with 107 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/app/core/navigation-bar/navigation-bar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<a class="nav-link" routerLink="/games" routerLinkActive="active">games</a>
</li>

<li class="nav-item">
<a class="nav-link" routerLink="/players" routerLinkActive="active">players</a>
</li>

<li class="nav-item">
<a class="nav-link" routerLink="/servers" routerLinkActive="active">servers</a>
</li>
Expand Down
1 change: 1 addition & 0 deletions src/app/players/models/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Player {
joinedAt: Date;
steamId: string;
avatarUrl: string;
gameCount: number;
role?: PlayerRole;
etf2lProfileId?: number;
skill?: { [gameClass: string]: number };
Expand Down
7 changes: 7 additions & 0 deletions src/app/players/player-list/player-list.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="list-group list-group-flush">
<a *ngFor="let player of players | async"
[routerLink]="['/player', player.id]"
class="list-group-item list-group-item-action">
{{ player.name }}
</a>
</div>
Empty file.
37 changes: 37 additions & 0 deletions src/app/players/player-list/player-list.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PlayerListComponent } from './player-list.component';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { AppState } from '@app/app.state';
import { RouterTestingModule } from '@angular/router/testing';
import { Store } from '@ngrx/store';
import { allPlayers } from '../players.selectors';

describe('PlayerListComponent', () => {
let component: PlayerListComponent;
let fixture: ComponentFixture<PlayerListComponent>;
let store: MockStore<AppState>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PlayerListComponent ],
imports: [ RouterTestingModule ],
providers: [
provideMockStore(),
],
})
.compileComponents();
}));

beforeEach(() => {
store = TestBed.get(Store);
store.overrideSelector(allPlayers, []);

fixture = TestBed.createComponent(PlayerListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
27 changes: 27 additions & 0 deletions src/app/players/player-list/player-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { AppState } from '@app/app.state';
import { allPlayers } from '../players.selectors';
import { Player } from '../models/player';
import { loadPlayers } from '../players.actions';

@Component({
selector: 'app-player-list',
templateUrl: './player-list.component.html',
styleUrls: ['./player-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlayerListComponent implements OnInit {

players: Observable<Player[]> = this.store.select(allPlayers);

constructor(
private store: Store<AppState>,
) { }

ngOnInit() {
this.store.dispatch(loadPlayers());
}

}
2 changes: 2 additions & 0 deletions src/app/players/players-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Routes, RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { PlayerDetailsComponent } from './player-details/player-details.component';
import { PlayerEditComponent } from './player-edit/player-edit.component';
import { PlayerListComponent } from './player-list/player-list.component';

const routes: Routes = [
{ path: 'players', component: PlayerListComponent },
{ path: 'player/:id', component: PlayerDetailsComponent },
{ path: 'player/:id/edit', component: PlayerEditComponent },
];
Expand Down
7 changes: 7 additions & 0 deletions src/app/players/players.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ export const playerSkillLoaded = createAction(
'[API] Player skill loaded',
props<{ playerSkill: PlayerSkill }>(),
);

export const loadPlayers = createAction('[Player list] Load all players');

export const playersLoaded = createAction(
'[API] All players loaded',
props<{ players: Player[] }>(),
);
12 changes: 11 additions & 1 deletion src/app/players/players.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Injectable } from '@angular/core';
import { createEffect, Actions, ofType } from '@ngrx/effects';
import { PlayersService } from './players.service';
import { map, mergeMap } from 'rxjs/operators';
import { loadPlayer, playerLoaded, playerEdited, editPlayer, playerSkillLoaded, loadPlayerSkill } from './players.actions';
import { loadPlayer, playerLoaded, playerEdited, editPlayer, playerSkillLoaded, loadPlayerSkill, loadPlayers,
playersLoaded } from './players.actions';

@Injectable()
export class PlayerEffects {
Expand Down Expand Up @@ -33,6 +34,15 @@ export class PlayerEffects {
)
);

loadAllPlayers = createEffect(() =>
this.actions.pipe(
ofType(loadPlayers),
mergeMap(() => this.playersService.fetchAllPlayers().pipe(
map(players => playersLoaded({ players })),
)),
)
);

constructor(
private actions: Actions,
private playersService: PlayersService,
Expand Down
2 changes: 2 additions & 0 deletions src/app/players/players.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PlayerEditComponent } from './player-edit/player-edit.component';
import { PlayerEditSkillComponent } from './player-edit-skill/player-edit-skill.component';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { PlayerListComponent } from './player-list/player-list.component';

@NgModule({
declarations: [
Expand All @@ -20,6 +21,7 @@ import { TooltipModule } from 'ngx-bootstrap/tooltip';
PlayerDetailsComponent,
PlayerEditComponent,
PlayerEditSkillComponent,
PlayerListComponent,
],
imports: [
CommonModule,
Expand Down
3 changes: 2 additions & 1 deletion src/app/players/players.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EntityState } from '@ngrx/entity';
import { Player } from './models/player';
import { adapter } from './players.adapter';
import { createReducer, Action, on } from '@ngrx/store';
import { playerLoaded, editPlayer, playerUpdated, playerEdited, playerSkillLoaded } from './players.actions';
import { playerLoaded, editPlayer, playerUpdated, playerEdited, playerSkillLoaded, playersLoaded } from './players.actions';

export interface State extends EntityState<Player> {
locked: boolean; // is player editing enabled or not
Expand All @@ -24,6 +24,7 @@ const playerReducer = createReducer(
skill: playerSkill.skill,
}
}, state)),
on(playersLoaded, (state, { players }) => adapter.upsertMany(players, state)),
);

export function reducer(state: State | undefined, action: Action) {
Expand Down
3 changes: 2 additions & 1 deletion src/app/players/players.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { adapter } from './players.adapter';
const playersFeature = createFeatureSelector<AppState, State>('players');
export const playersLocked = createSelector(playersFeature, feature => feature.locked);

const { selectEntities } = adapter.getSelectors();
const { selectEntities, selectAll } = adapter.getSelectors();
const playerEntities = createSelector(playersFeature, selectEntities);
export const allPlayers = createSelector(playersFeature, selectAll);

export const playerById = (playerId: string) => createSelector(playerEntities, entites => entites[playerId]);
2 changes: 1 addition & 1 deletion src/app/players/players.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('PlayersService', () => {
describe('#savePlayer()', () => {
it('should call the endpoint', inject([PlayersService], (service: PlayersService) => {
const player: Player = { id: 'FAKE_ID', name: 'FAKE_NAME', joinedAt: new Date(), steamId: 'FAKE_STEAM_ID',
avatarUrl: 'FAKE_AVATAR_URL', skill: { } };
avatarUrl: 'FAKE_AVATAR_URL', skill: { }, gameCount: 0 };
service.savePlayer(player).subscribe();
const req = httpContoller.expectOne('FAKE_URL/players/FAKE_ID');
expect(req.request.method).toBe('PUT');
Expand Down
4 changes: 4 additions & 0 deletions src/app/players/players.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ export class PlayersService {
return this.http.put<PlayerSkill>(`${this.apiUrl}/players/${playerSkill.player}/skill`, playerSkill);
}

fetchAllPlayers(): Observable<Player[]> {
return this.http.get<Player[]>(`${this.apiUrl}/players`);
}

}

0 comments on commit 4a8cb85

Please sign in to comment.