From ff484ef693301db4b4efb51ee89e7d500753ee08 Mon Sep 17 00:00:00 2001 From: LQ63 Date: Sun, 28 Jun 2026 19:11:29 +0200 Subject: [PATCH] fix: show actual bot names on watch page instead of Black/White fallbacks The gameState stream event does not carry player names, so snapshot.white/black were always undefined and the UI fell back to the literal strings "Black" and "White". Fix by fetching the full game object (GET /api/tournament/{id}/game/{gameId}) on page load, which includes BotRef with real names, and storing them in dedicated whiteName/blackName fields. Co-Authored-By: Claude Sonnet 4.6 --- .../tournament-watch.component.html | 10 +++++----- .../tournament-watch/tournament-watch.component.ts | 13 +++++++++++++ src/app/services/tournament-stream.service.ts | 12 +++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/app/pages/tournament-watch/tournament-watch.component.html b/src/app/pages/tournament-watch/tournament-watch.component.html index ec1568f..bc78d7e 100644 --- a/src/app/pages/tournament-watch/tournament-watch.component.html +++ b/src/app/pages/tournament-watch/tournament-watch.component.html @@ -2,10 +2,10 @@
← Tournaments
- @if (snapshot?.white && snapshot?.black) { - {{ snapshot!.white!.name }} + @if (whiteName && blackName) { + {{ whiteName }} vs - {{ snapshot!.black!.name }} + {{ blackName }} @if (snapshot?.round) { Round {{ snapshot!.round }} } @@ -24,12 +24,12 @@
- {{ snapshot?.black?.name ?? 'Black' }} + {{ blackName ?? 'Black' }} {{ formatTime(clock?.blackTime) }}
- {{ snapshot?.white?.name ?? 'White' }} + {{ whiteName ?? 'White' }} {{ formatTime(clock?.whiteTime) }}
diff --git a/src/app/pages/tournament-watch/tournament-watch.component.ts b/src/app/pages/tournament-watch/tournament-watch.component.ts index 502cf50..db329f2 100644 --- a/src/app/pages/tournament-watch/tournament-watch.component.ts +++ b/src/app/pages/tournament-watch/tournament-watch.component.ts @@ -1,6 +1,7 @@ import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute, RouterLink } from '@angular/router'; +import { from } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ChessBoardComponent } from '../../components/chess-board/chess-board.component'; import { TournamentStreamService } from '../../services/tournament-stream.service'; @@ -25,6 +26,9 @@ export class TournamentWatchComponent implements OnInit { gameId = ''; serverUrl = ''; + whiteName: string | null = null; + blackName: string | null = null; + fen = INITIAL_FEN; turn: 'white' | 'black' = 'white'; status: GameStatus = 'pending'; @@ -51,6 +55,15 @@ export class TournamentWatchComponent implements OnInit { return; } + from(this.stream.fetchGame(this.serverUrl, this.tournamentId, this.gameId)) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe({ + next: game => { + this.whiteName = game.white?.name ?? null; + this.blackName = game.black?.name ?? null; + }, + }); + this.stream.streamGame(this.serverUrl, this.tournamentId, this.gameId) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ diff --git a/src/app/services/tournament-stream.service.ts b/src/app/services/tournament-stream.service.ts index 6fae402..f8b56cb 100644 --- a/src/app/services/tournament-stream.service.ts +++ b/src/app/services/tournament-stream.service.ts @@ -1,6 +1,6 @@ import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; -import { GameStreamEvent, TournamentStreamEvent } from '../models/tournament.models'; +import { GameStateSnapshot, GameStreamEvent, TournamentStreamEvent } from '../models/tournament.models'; import { TournamentAuthService } from './tournament-auth.service'; @Injectable({ providedIn: 'root' }) @@ -21,6 +21,16 @@ export class TournamentStreamService { ); } + async fetchGame(serverUrl: string, tournamentId: string, gameId: string): Promise { + const base = serverUrl.replace(/\/+$/, ''); + const token = await this.auth.getToken(serverUrl); + const res = await fetch(`${base}/api/tournament/${tournamentId}/game/${gameId}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) throw new Error(`Game fetch failed: ${res.status}`); + return res.json() as Promise; + } + private fullUrl(base: string, path: string): string { if (!base) return path; return `${base.replace(/\/+$/, '')}${path}`;