diff --git a/client/index.html b/client/index.html index 20e2c5b..eeb72a6 100644 --- a/client/index.html +++ b/client/index.html @@ -8,6 +8,7 @@

Chat Grid

+
diff --git a/client/public/version.js b/client/public/version.js index b6a76a0..137053c 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.22 R179"; +window.CHGRID_WEB_VERSION = "2026.02.22 R180"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/main.ts b/client/src/main.ts index 5ded141..3cee7e1 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -89,6 +89,7 @@ declare global { } type Dom = { + connectionStatus: HTMLElement; appVersion: HTMLElement; updatesSection: HTMLElement; updatesToggle: HTMLButtonElement; @@ -111,6 +112,7 @@ type Dom = { }; const dom: Dom = { + connectionStatus: requiredById('connectionStatus'), appVersion: requiredById('appVersion'), updatesSection: requiredById('updatesSection'), updatesToggle: requiredById('updatesToggle'), @@ -419,6 +421,11 @@ function updateStatus(message: string): void { }, 4000); } +/** Updates persistent connection/update status shown under the page heading. */ +function setConnectionStatus(message: string): void { + dom.connectionStatus.textContent = String(message).trim(); +} + /** Sanitizes user nicknames to printable/safe characters and enforces max length. */ function sanitizeName(value: string): string { return value.replace(/[\u0000-\u001F\u007F<>]/g, '').trim().slice(0, NICKNAME_MAX_LENGTH); @@ -577,11 +584,13 @@ function handleSignalingStatus(message: string): void { return; } if (message === 'Disconnected.' && state.running && !reconnectInFlight) { + setConnectionStatus('Disconnected from server. Reconnecting...'); pushChatMessage('Disconnected from server. Reconnecting...'); void reconnectAfterSocketClose(); return; } if (message === 'Disconnected.') { + setConnectionStatus('Disconnected from server.'); pushChatMessage('Disconnected from server.'); return; } @@ -1240,6 +1249,11 @@ function getConnectionFlowDeps(): ConnectFlowDeps { dom, sanitizeName, updateStatus: (message) => { + if (message === 'Disconnected.') { + setConnectionStatus('Disconnected.'); + } else if (message.startsWith('Connect failed.')) { + setConnectionStatus(message); + } if (reconnectInFlight && message === 'Disconnected.') { return; } @@ -1271,6 +1285,7 @@ function getConnectionFlowDeps(): ConnectFlowDeps { /** Performs end-to-end connect flow: validation, media setup, then signaling connection. */ async function connect(): Promise { + setConnectionStatus('Connecting...'); await runConnectFlow(getConnectionFlowDeps()); } @@ -1278,6 +1293,7 @@ async function connect(): Promise { function disconnect(): void { stopHeartbeat(); runDisconnectFlow(getConnectionFlowDeps()); + setConnectionStatus('Disconnected.'); pendingEscapeDisconnect = false; restoreLoopbackAfterMicGainEdit(); subscriptionRefreshPending = false; @@ -1377,10 +1393,12 @@ async function onSignalingMessage(message: IncomingMessage): Promise { await onAppMessage(message); applyConfiguredPeerListenGains(); if (restartAnnouncement) { + setConnectionStatus(restartAnnouncement); pushChatMessage(restartAnnouncement); audio.sfxUiConfirm(); } if (connectedAnnouncement) { + setConnectionStatus(connectedAnnouncement); pushChatMessage(connectedAnnouncement); } } @@ -2295,3 +2313,4 @@ updateStatus( ? 'Client updated, please reconnect.' : 'Welcome to the Chat Grid. Press the Settings button to configure your audio, then Connect to join the grid.', ); +setConnectionStatus(isVersionReloadedSession() ? 'Client updated, please reconnect.' : 'Not connected.'); diff --git a/client/src/styles.css b/client/src/styles.css index 73ef1ae..8374215 100644 --- a/client/src/styles.css +++ b/client/src/styles.css @@ -18,6 +18,12 @@ body { text-align: center; } +#connectionStatus { + color: #93c5fd; + min-height: 1.25rem; + margin: 0.25rem 0 0.5rem; +} + #appVersion { display: block; color: #94a3b8;