From 155ac2c71a6a6e6e5a51e9ec194c9476dbba4d66 Mon Sep 17 00:00:00 2001 From: Ryan Hamamura <58859899+ryanhamamura@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:52:39 -1000 Subject: [PATCH] feat: replace connection indicator with live clock A ticking clock serves as an implicit connection indicator - if it stops updating, users know the connection is stale. Simpler and more useful than a status dot. --- features/c4game/pages/game.templ | 1 + features/common/components/shared.templ | 60 +++---------------------- features/snakegame/pages/game.templ | 1 + 3 files changed, 8 insertions(+), 54 deletions(-) diff --git a/features/c4game/pages/game.templ b/features/c4game/pages/game.templ index c5d9f11..35d9c84 100644 --- a/features/c4game/pages/game.templ +++ b/features/c4game/pages/game.templ @@ -25,6 +25,7 @@ templ GamePage(g *connect4.Game, myColor int, messages []chat.Message, chatCfg c templ GameContent(g *connect4.Game, myColor int, messages []chat.Message, chatCfg chatcomponents.Config) {
+ @sharedcomponents.LiveClock() @sharedcomponents.BackToLobby() @sharedcomponents.StealthTitle("text-3xl font-bold") @components.PlayerInfo(g, myColor) diff --git a/features/common/components/shared.templ b/features/common/components/shared.templ index d5f68c0..0f92101 100644 --- a/features/common/components/shared.templ +++ b/features/common/components/shared.templ @@ -1,7 +1,7 @@ package components import ( - "fmt" + "time" "github.com/starfederation/datastar-go/datastar" ) @@ -48,60 +48,12 @@ templ NicknamePrompt(returnPath string) { } -func isStale(lastPing int64) bool { - return lastPing == 0 -} - -var connectionWatcherHandle = templ.NewOnceHandle() - -// ConnectionIndicator shows a small dot indicating SSE connection status. -// Server patches this with a timestamp; client JS detects staleness. -templ ConnectionIndicator(lastPing int64) { -
-
-
-
-
+// LiveClock shows the current server time, updated with each SSE patch. +// If the clock stops updating, users know the connection is stale. +templ LiveClock() { +
+ { time.Now().Format("15:04:05") }
- @connectionWatcherHandle.Once() { - @connectionWatcher() - } -} - -script connectionWatcher() { - setInterval(function() { - var el = document.getElementById('connection-indicator'); - var dot = document.getElementById('connection-dot'); - var ping = document.getElementById('connection-ping'); - if (!el || !dot || !ping) return; - - var lastPing = parseInt(el.dataset.lastPing, 10) || 0; - var stale = Date.now() - lastPing > 20000; - - dot.classList.toggle('status-success', !stale); - dot.classList.toggle('status-error', stale); - ping.classList.toggle('status-success', !stale); - ping.classList.toggle('status-error', stale); - ping.classList.toggle('animate-ping', !stale); - }, 1000); } templ GameJoinPrompt(loginURL string, registerURL string, gamePath string) { diff --git a/features/snakegame/pages/game.templ b/features/snakegame/pages/game.templ index 378690c..7ab922a 100644 --- a/features/snakegame/pages/game.templ +++ b/features/snakegame/pages/game.templ @@ -44,6 +44,7 @@ templ GamePage(sg *snake.SnakeGame, mySlot int, messages []chat.Message, chatCfg templ GameContent(sg *snake.SnakeGame, mySlot int, messages []chat.Message, chatCfg chatcomponents.Config, gameID string) {
+ @components.LiveClock() @components.BackToLobby()

~~~~

@snakecomponents.PlayerList(sg, mySlot)