From 99b37b5456b6bc397d89094723d1e566069c2fa2a4e288b930a6a2e238bec273 Mon Sep 17 00:00:00 2001 From: Lixen Wraith Date: Wed, 25 Feb 2026 10:22:24 -0500 Subject: [PATCH] v0.9.1 web ui fixes and improvements --- .../server/webserver/chess-client-web/app.js | 90 ++++++++++++------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/internal/server/webserver/chess-client-web/app.js b/internal/server/webserver/chess-client-web/app.js index 26f38dc..0fdb10b 100644 --- a/internal/server/webserver/chess-client-web/app.js +++ b/internal/server/webserver/chess-client-web/app.js @@ -5,7 +5,8 @@ let gameState = { turn: 'w', isPlayerWhite: true, isLocked: false, - pollInterval: null, + polling: false, + pollController: null, apiUrl: '', selectedSquare: null, healthCheckInterval: null, @@ -672,41 +673,64 @@ async function triggerComputerMove() { } function startPolling() { - gameState.pollInterval = setInterval(async () => { - try { - const response = await fetch(`${gameState.apiUrl}/api/v1/games/${gameState.gameId}`); - if (!response.ok) { - const errorInfo = handleApiError('poll game state', null, response); - if (response.status === 404) { - stopPolling(); - unlockBoard(); - flashErrorMessage('Game no longer exists'); - gameState.gameId = null; - return; - } - // Non-404: already displayed by handleApiError above, keep polling - return; - } - - const game = await response.json(); - if (game.state !== 'pending') { - stopPolling(); - updateGameDisplay(game); - unlockBoard(); - } - gameState.networkError = false; - updateServerIndicator('healthy'); - } catch (error) { - handleApiError('poll game state', error); - stopPolling(); - unlockBoard(); - } - }, 1500); + gameState.polling = true; + pollOnce(); } function stopPolling() { - clearInterval(gameState.pollInterval); - gameState.pollInterval = null; + gameState.polling = false; + if (gameState.pollController) { + gameState.pollController.abort(); + gameState.pollController = null; + } +} + +async function pollOnce() { + if (!gameState.polling || !gameState.gameId) return; + + const moveCount = (gameState.moveList || []).length; + gameState.pollController = new AbortController(); + + try { + const response = await fetch( + `${gameState.apiUrl}/api/v1/games/${gameState.gameId}?wait=true&moveCount=${moveCount}`, + { signal: gameState.pollController.signal } + ); + + if (!gameState.polling) return; + + if (!response.ok) { + const errorInfo = handleApiError('poll game state', null, response); + if (response.status === 404) { + stopPolling(); + unlockBoard(); + flashErrorMessage('Game no longer exists'); + gameState.gameId = null; + return; + } + // Retry after delay for transient errors + setTimeout(pollOnce, 2000); + return; + } + + const game = await response.json(); + gameState.networkError = false; + updateServerIndicator('healthy'); + + if (game.state !== 'pending') { + stopPolling(); + updateGameDisplay(game); + unlockBoard(); + } else { + // Still pending, long-poll again + pollOnce(); + } + } catch (error) { + if (error.name === 'AbortError') return; + handleApiError('poll game state', error); + stopPolling(); + unlockBoard(); + } } function lockBoard() {