Skip to content

Commit

Permalink
dynamic tables (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
codephobia committed Mar 9, 2024
1 parent eb586cd commit 7b5a214
Show file tree
Hide file tree
Showing 26 changed files with 715 additions and 177 deletions.
1 change: 1 addition & 0 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ POSTGRES_DB=pool
POSTGRES_PORT=5432
CHALLONGE_API_KEY=secret
CHALLONGE_USERNAME=username
MAX_TABLE_COUNT=11
13 changes: 10 additions & 3 deletions apps/api/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"strconv"

"github.com/codephobia/pool-overlay/libs/go/api"
"github.com/codephobia/pool-overlay/libs/go/challonge"
Expand Down Expand Up @@ -47,9 +48,15 @@ func NewCore() (*Core, error) {

// Initialize game state.
tables := map[int]*state.State{}
tables[1] = state.NewState(db, 1)
tables[2] = state.NewState(db, 2)
tables[3] = state.NewState(db, 3)

// Add tables to game state based on default provided in env file.
maxTableCount, err := strconv.Atoi(os.Getenv("MAX_TABLE_COUNT"))
if err != nil {
maxTableCount = 3
}
for i := 1; i <= maxTableCount; i++ {
tables[i] = state.NewState(db, i)
}

// Initialize Challonge.
challonge := challonge.NewChallonge(os.Getenv("CHALLONGE_API_KEY"), os.Getenv("CHALLONGE_USERNAME"), db, overlay, tables)
Expand Down
8 changes: 8 additions & 0 deletions apps/dashboard/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { NgModule, isDevMode } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreRouterConnectingModule, routerReducer } from '@ngrx/router-store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';

Expand All @@ -11,6 +13,7 @@ import { AppComponent } from './components/app/app.component';
import { SideNavComponent } from './components/side-nav/side-nav.component';
import { ENV_CONFIG } from './models/environment-config.model';
import { environment } from '../environments/environment';
import * as fromTables from './core/tables';

const COMPONENTS = [
AppComponent,
Expand All @@ -24,11 +27,16 @@ const COMPONENTS = [
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
FontAwesomeModule,
AppRoutingModule,
StoreModule.forRoot({
[fromTables.stateKey]: fromTables.reducer,
router: routerReducer,
}),
EffectsModule.forRoot([
fromTables.TablesEffects,
]),
StoreRouterConnectingModule.forRoot(),
StoreDevtoolsModule.instrument({
maxAge: 25,
Expand Down
4 changes: 4 additions & 0 deletions apps/dashboard/src/app/core/tables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './tables.actions';
export * from './tables.effects';
export * from './tables.reducer';
export * from './tables.selectors';
11 changes: 11 additions & 0 deletions apps/dashboard/src/app/core/tables/tables.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createActionGroup, emptyProps, props } from '@ngrx/store';

export const TablesActions = createActionGroup({
source: 'Tables',
events: {
'Get Count': emptyProps(),
'Get Count Success': props<{ count: number }>(),
'Get Count Error': props<{ error: string }>(),
'Set Count': props<{ count: number }>(),
},
});
27 changes: 27 additions & 0 deletions apps/dashboard/src/app/core/tables/tables.effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { Actions, ROOT_EFFECTS_INIT, createEffect, ofType } from '@ngrx/effects';

import { TablesActions } from './tables.actions';
import { TableService } from '../../services/table.service';
import { catchError, map, of, switchMap } from 'rxjs';

@Injectable()
export class TablesEffects {
constructor(
private actions$: Actions,
private tableService: TableService,
) { }

init$ = createEffect(() => this.actions$.pipe(
ofType(ROOT_EFFECTS_INIT),
map(() => TablesActions.getCount()),
));

getCount$ = createEffect(() => this.actions$.pipe(
ofType(TablesActions.getCount),
switchMap(() => this.tableService.count().pipe(
map(({ count }) => TablesActions.getCountSuccess({ count })),
catchError((error) => of(TablesActions.getCountError({ error }))),
)),
));
}
24 changes: 24 additions & 0 deletions apps/dashboard/src/app/core/tables/tables.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createReducer, on } from '@ngrx/store';
import { TablesActions } from './tables.actions';

export const stateKey = 'table-count';

export interface State {
count: number;
}

export const initialState: State = {
count: 1,
}

export const reducer = createReducer(
initialState,
on(
TablesActions.getCountSuccess,
TablesActions.setCount,
(state, { count }) => ({
...state,
count,
})
)
);
9 changes: 9 additions & 0 deletions apps/dashboard/src/app/core/tables/tables.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { State, stateKey } from './tables.reducer';

export const selectTablesState = createFeatureSelector<State>(stateKey);

export const selectTablesCount = createSelector(
selectTablesState,
(state: State) => state.count
);
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400 p-4">
<li class="mr-2" *ngFor="let table of tables">
<button
type="button"
(click)="setTable(table)"
class="inline-block py-3 px-4 rounded-lg"
[ngClass]="{ 'text-white bg-blue-600': table === currentTable, 'hover:bg-gray-800 hover:text-white': table != currentTable }"
>
Table {{ table }}
</button>
</li>
<li>
<a class="inline-block py-3 px-4 text-gray-400 cursor-not-allowed dark:text-gray-500"> <fa-icon [icon]="faPlus"></fa-icon> Add Table </a>
</li>
</ul>
<div class="flex flex-row flex-none mt-5 h-52 justify-center items-center">
<ng-container *ngFor="let table of tables">
<pool-overlay-scoreboard [hidden]="table !== currentTable" [table]="table"></pool-overlay-scoreboard>
</ng-container>
</div>
<div>
<ng-container *ngFor="let table of tables">
<pool-overlay-controller [hidden]="table !== currentTable" [table]="table"></pool-overlay-controller>
</ng-container>
</div>
<ng-container *ngIf="tables$ | async as tables">
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400 p-4">
<li class="mr-2" *ngFor="let table of tables">
<button
type="button"
(click)="setTable(table)"
class="inline-block py-3 px-4 rounded-lg"
[ngClass]="{ 'text-white bg-blue-600': table === currentTable, 'hover:bg-gray-800 hover:text-white': table != currentTable }"
>
Table {{ table }}
</button>
</li>
<li>
<a class="inline-block py-3 px-4 text-gray-400 cursor-not-allowed dark:text-gray-500"> <fa-icon [icon]="faPlus"></fa-icon> Add Table </a>
</li>
</ul>
<div class="flex flex-row flex-none mt-5 h-52 justify-center items-center">
<ng-container *ngFor="let table of tables">
<pool-overlay-scoreboard [hidden]="table !== currentTable" [table]="table"></pool-overlay-scoreboard>
</ng-container>
</div>
<div>
<ng-container *ngFor="let table of tables">
<pool-overlay-controller [hidden]="table !== currentTable" [table]="table"></pool-overlay-controller>
</ng-container>
</div>
</ng-container>
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { Component } from '@angular/core';
import { faPlus } from '@fortawesome/pro-regular-svg-icons';
import { Store } from '@ngrx/store';
import * as fromTables from '../../../core/tables';
import { map } from 'rxjs';

@Component({
selector: 'pool-overlay-home-page',
templateUrl: './home-page.component.html',
})
export class HomePageComponent {
public faPlus = faPlus;
public tables: number[] = [1, 2, 3];
public currentTable = 1;
public tables$ = this.store.select(fromTables.selectTablesCount).pipe(
map((count) => Array.from(new Array(count), (x, i) => i + 1)),
);

constructor(private store: Store) { }

public setTable(table: number): void {
this.currentTable = table;
Expand Down
26 changes: 26 additions & 0 deletions apps/dashboard/src/app/services/table.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

import { EnvironmentConfig, ENV_CONFIG } from '../models/environment-config.model';
import { ICount } from '../models/count.model';

@Injectable({ providedIn: 'root' })
export class TableService {
private apiURL: string;
private apiVersion: string;
private endpoint = 'table';

constructor(
@Inject(ENV_CONFIG) config: EnvironmentConfig,
private http: HttpClient,
) {
this.apiURL = config.environment.apiURL;
this.apiVersion = config.environment.apiVersion;
}

public count(): Observable<ICount> {
let url = `${this.apiURL}/${this.apiVersion}/${this.endpoint}/count`;
return this.http.get<ICount>(url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ <h2 class="text-white uppercase">{{ vm.tournament?.name }}</h2>
</div>
<div class="flex flex-row flex-grow bg-sad-table-odd">
<div class="flex flex-col flex-grow p-5 gap-5">
<div class="flex flex-row gap-5">
<div class="flex flex-col flex-grow text-white w-1/3" *ngFor="let table of tables">
<div class="grid grid-cols-3 gap-5">
<div class="flex flex-col text-white" *ngFor="let table of vm.tablesArr">
<div class="flex flex-row h-66px justify-between items-center py-2.5 px-5 border-l border-r border-t border-sad-active rounded-t bg-sad-table-even uppercase">
<div>Table {{ table }}</div>
<div>
Expand All @@ -20,7 +20,7 @@ <h2 class="text-white uppercase">{{ vm.tournament?.name }}</h2>
</button>
<ng-template #menu>
<div class="border bg-sad-input border-sad-active rounded mt-2.5 py-2.5 text-white" cdkMenu>
<ng-container *ngFor="let menuTable of tables">
<ng-container *ngFor="let menuTable of vm.tablesArr">
<button type="button" *ngIf="table !== menuTable" class="block hover:bg-sad-active py-2.5 px-5" cdkMenuItem (click)="swapTables(table, menuTable)">
Swap with Table {{ menuTable }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export class TournamentLoadedComponent {
readonly faLock = faLock;
readonly faEllipsisVertical = faEllipsisVertical
readonly gameType = GameType;
readonly tables = [1, 2, 3];
readonly vm$ = this.store.vm$;

constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { IGame, OverlayState, Tournament } from '@pool-overlay/models';
import { switchMap, tap } from 'rxjs';
import { TablesService } from '../../services/tables.service';
import { TournamentsService } from '../../services/tournament.service';
import { Store } from '@ngrx/store';
import * as fromTables from '../../../core/tables';

export enum LoadingState {
INIT,
Expand Down Expand Up @@ -33,6 +35,7 @@ export const initialState: TournamentLoadedState = {
export class TournamentLoadedStore extends ComponentStore<TournamentLoadedState> {
constructor(
private router: Router,
private store: Store,
private tournamentsService: TournamentsService,
private tablesService: TablesService,
) {
Expand Down Expand Up @@ -81,10 +84,12 @@ export class TournamentLoadedStore extends ComponentStore<TournamentLoadedState>
this.isLoaded$,
this.tournament$,
this.tables$,
(isLoaded, tournament, tables) => ({
this.store.select(fromTables.selectTablesCount),
(isLoaded, tournament, tables, tablesCount) => ({
isLoaded,
tournament,
tables,
tablesArr: Array.from(new Array(tablesCount), (x, i) => i + 1),
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ <h2 class="text-white uppercase">{{ vm.tournament?.name }}</h2>
<div class="flex flex-row flex-grow bg-sad-table-odd p-5 border border-sad-active rounded-b">
<div class="flex flex-col gap-2.5">
<div class="flex flex-row gap-2.5">
<button type="button" class="px-5 py-2.5 rounded text-white" [ngClass]="vm.maxTables === 1 ? 'bg-blue-700' : 'bg-gray-700'" (click)="updateMaxTables(1)">1</button>
<button type="button" class="px-5 py-2.5 rounded text-white" [ngClass]="vm.maxTables === 2 ? 'bg-blue-700' : 'bg-gray-700'" (click)="updateMaxTables(2)">2</button>
<button type="button" class="px-5 py-2.5 rounded text-white" [ngClass]="vm.maxTables === 3 ? 'bg-blue-700' : 'bg-gray-700'" (click)="updateMaxTables(3)">3</button>
<button type="button" class="px-5 py-2.5 rounded text-white" [ngClass]="vm.maxTables === 4 ? 'bg-blue-700' : 'bg-gray-700'" (click)="updateMaxTables(4)">4</button>
<ng-container *ngFor="let table of vm.tables">
<button
type="button"
class="px-5 py-2.5 rounded text-white"
[ngClass]="vm.maxTables === table ? 'bg-blue-700' : 'bg-gray-700'"
(click)="updateMaxTables(table)"
>
{{ table }}
</button>
</ng-container>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { GameType, Tournament } from '@pool-overlay/models';
import { switchMap, tap, withLatestFrom } from 'rxjs';
import { GameType, Tournament } from '@pool-overlay/models';
import { TournamentsService } from '../../services/tournament.service';
import * as fromTables from '../../../core/tables';

export enum LoadingState {
INIT,
Expand Down Expand Up @@ -43,6 +45,7 @@ export const initialState: TournamentSetupState = {
export class TournamentSetupStore extends ComponentStore<TournamentSetupState> {
constructor(
private router: Router,
private store: Store,
private tournamentsService: TournamentsService,
) {
super(initialState);
Expand Down Expand Up @@ -120,6 +123,7 @@ export class TournamentSetupStore extends ComponentStore<TournamentSetupState> {
readonly vm$ = this.select(
this.isLoaded$,
this.tournament$,
this.store.select(fromTables.selectTablesCount),
this.maxTables$,
this.isHandicapped$,
this.showOverlay$,
Expand All @@ -129,9 +133,10 @@ export class TournamentSetupStore extends ComponentStore<TournamentSetupState> {
this.gameType$,
this.aSideRaceTo$,
this.bSideRaceTo$,
(isLoaded, tournament, maxTables, isHandicapped, showOverlay, showFlags, showFargo, showScore, gameType, aSideRaceTo, bSideRaceTo) => ({
(isLoaded, tournament, tablesCount, maxTables, isHandicapped, showOverlay, showFlags, showFargo, showScore, gameType, aSideRaceTo, bSideRaceTo) => ({
isLoaded,
tournament,
tables: Array.from(new Array(tablesCount), (x, i) => i + 1),
maxTables,
isHandicapped,
showOverlay,
Expand Down
2 changes: 2 additions & 0 deletions libs/go/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ var (
ErrInvalidTournamentDetails = errors.New("invalid tournament details")
// ErrInvalidTableNumber - Invalid table number.
ErrInvalidTableNumber = errors.New("invalid table number")
// ErrRemoveOnlyTable - Cannot remove only table.
ErrRemoveOnlyTable = errors.New("cannot remove only table")
)

0 comments on commit 7b5a214

Please sign in to comment.