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)