Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dynamic tables #14

Merged
merged 1 commit into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")
)
Loading
Loading