Skip to content

Commit 6e0cc59

Browse files
committed
Extract UI overlay copy helpers
1 parent 94866ae commit 6e0cc59

File tree

3 files changed

+103
-14
lines changed

3 files changed

+103
-14
lines changed

src/client/ui-screens.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { describe, expect, it } from 'vitest';
22

33
import {
4+
buildGameOverView,
5+
buildReconnectView,
6+
buildRematchPendingView,
47
buildScreenVisibility,
58
buildWaitingScreenCopy,
69
toggleLogVisible,
@@ -50,6 +53,31 @@ describe('ui-screens', () => {
5053
});
5154
});
5255

56+
it('builds game-over, reconnect, and rematch-pending overlay copy', () => {
57+
expect(buildGameOverView(true, 'Fleet eliminated!', {
58+
turns: 12,
59+
myShipsAlive: 2,
60+
myShipsTotal: 3,
61+
enemyShipsAlive: 0,
62+
enemyShipsTotal: 2,
63+
})).toEqual({
64+
titleText: 'VICTORY',
65+
reasonText: 'Fleet eliminated!\n\nTurns: 12 | Your ships: 2/3 | Enemy: 0/2',
66+
rematchText: 'Rematch',
67+
rematchDisabled: false,
68+
});
69+
70+
expect(buildReconnectView(2, 5)).toEqual({
71+
reconnectText: 'Connection lost',
72+
attemptText: 'Attempt 2 of 5',
73+
});
74+
75+
expect(buildRematchPendingView()).toEqual({
76+
rematchText: 'Waiting...',
77+
rematchDisabled: true,
78+
});
79+
});
80+
5381
it('toggles game-log visibility', () => {
5482
expect(toggleLogVisible(true)).toBe(false);
5583
expect(toggleLogVisible(false)).toBe(true);

src/client/ui-screens.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ export interface WaitingScreenCopy {
2626
statusText: string;
2727
}
2828

29+
export interface GameOverStatsLike {
30+
turns: number;
31+
myShipsAlive: number;
32+
myShipsTotal: number;
33+
enemyShipsAlive: number;
34+
enemyShipsTotal: number;
35+
}
36+
37+
export interface GameOverView {
38+
titleText: 'VICTORY' | 'DEFEAT';
39+
reasonText: string;
40+
rematchText: 'Rematch';
41+
rematchDisabled: false;
42+
}
43+
44+
export interface ReconnectView {
45+
reconnectText: 'Connection lost';
46+
attemptText: string;
47+
}
48+
49+
export interface RematchPendingView {
50+
rematchText: 'Waiting...';
51+
rematchDisabled: true;
52+
}
53+
2954
const HIDDEN_VISIBILITY: UIScreenVisibility = {
3055
menu: 'none',
3156
scenario: 'none',
@@ -89,6 +114,39 @@ export function buildWaitingScreenCopy(code: string, connecting: boolean): Waiti
89114
: { codeText: code, statusText: 'Waiting for opponent...' };
90115
}
91116

117+
export function buildGameOverView(
118+
won: boolean,
119+
reason: string,
120+
stats?: GameOverStatsLike,
121+
): GameOverView {
122+
let reasonText = reason;
123+
if (stats) {
124+
reasonText += `\n\nTurns: ${stats.turns}`;
125+
reasonText += ` | Your ships: ${stats.myShipsAlive}/${stats.myShipsTotal}`;
126+
reasonText += ` | Enemy: ${stats.enemyShipsAlive}/${stats.enemyShipsTotal}`;
127+
}
128+
return {
129+
titleText: won ? 'VICTORY' : 'DEFEAT',
130+
reasonText,
131+
rematchText: 'Rematch',
132+
rematchDisabled: false,
133+
};
134+
}
135+
136+
export function buildRematchPendingView(): RematchPendingView {
137+
return {
138+
rematchText: 'Waiting...',
139+
rematchDisabled: true,
140+
};
141+
}
142+
143+
export function buildReconnectView(attempt: number, maxAttempts: number): ReconnectView {
144+
return {
145+
reconnectText: 'Connection lost',
146+
attemptText: `Attempt ${attempt} of ${maxAttempts}`,
147+
};
148+
}
149+
92150
export function toggleLogVisible(logVisible: boolean): boolean {
93151
return !logVisible;
94152
}

src/client/ui.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import {
1515
import { buildHUDView } from './ui-hud';
1616
import { deriveHudLayoutOffsets } from './ui-layout';
1717
import {
18+
buildGameOverView,
19+
buildReconnectView,
20+
buildRematchPendingView,
1821
buildScreenVisibility,
1922
buildWaitingScreenCopy,
2023
toggleLogVisible,
@@ -508,32 +511,32 @@ export class UIManager {
508511
}
509512

510513
showGameOver(won: boolean, reason: string, stats?: { turns: number; myShipsAlive: number; myShipsTotal: number; enemyShipsAlive: number; enemyShipsTotal: number }) {
514+
const view = buildGameOverView(won, reason, stats);
511515
this.gameOverEl.style.display = 'flex';
512-
document.getElementById('gameOverText')!.textContent = won ? 'VICTORY' : 'DEFEAT';
513-
let reasonText = reason;
514-
if (stats) {
515-
reasonText += `\n\nTurns: ${stats.turns}`;
516-
reasonText += ` | Your ships: ${stats.myShipsAlive}/${stats.myShipsTotal}`;
517-
reasonText += ` | Enemy: ${stats.enemyShipsAlive}/${stats.enemyShipsTotal}`;
518-
}
516+
document.getElementById('gameOverText')!.textContent = view.titleText;
519517
const reasonEl = document.getElementById('gameOverReason')!;
520-
reasonEl.textContent = reasonText;
518+
reasonEl.textContent = view.reasonText;
521519
reasonEl.style.whiteSpace = 'pre-line';
522-
document.getElementById('rematchBtn')!.textContent = 'Rematch';
523-
document.getElementById('rematchBtn')!.removeAttribute('disabled');
520+
const rematchBtn = document.getElementById('rematchBtn')!;
521+
rematchBtn.textContent = view.rematchText;
522+
rematchBtn.removeAttribute('disabled');
524523
}
525524

526525
showRematchPending() {
526+
const view = buildRematchPendingView();
527527
const btn = document.getElementById('rematchBtn')!;
528-
btn.textContent = 'Waiting...';
529-
btn.setAttribute('disabled', 'true');
528+
btn.textContent = view.rematchText;
529+
if (view.rematchDisabled) {
530+
btn.setAttribute('disabled', 'true');
531+
}
530532
}
531533

532534
showReconnecting(attempt: number, maxAttempts: number, onCancel: () => void) {
535+
const view = buildReconnectView(attempt, maxAttempts);
533536
const overlay = document.getElementById('reconnectOverlay')!;
534537
overlay.style.display = 'flex';
535-
document.getElementById('reconnectText')!.textContent = 'Connection lost';
536-
document.getElementById('reconnectAttempt')!.textContent = `Attempt ${attempt} of ${maxAttempts}`;
538+
document.getElementById('reconnectText')!.textContent = view.reconnectText;
539+
document.getElementById('reconnectAttempt')!.textContent = view.attemptText;
537540
const cancelBtn = document.getElementById('reconnectCancelBtn')!;
538541
cancelBtn.onclick = () => {
539542
this.hideReconnecting();

0 commit comments

Comments
 (0)