feat: re-enable connection indicator for SSE status
All checks were successful
CI / Deploy / test (pull_request) Successful in 15s
CI / Deploy / lint (pull_request) Successful in 24s
CI / Deploy / deploy (pull_request) Has been skipped

Add ConnectionIndicator to Connect 4 game page (Snake already had it).
Both games now patch the indicator on initial connect and every 10s
heartbeat, giving users visual feedback that SSE is connected.
This commit is contained in:
Ryan Hamamura
2026-03-03 12:34:13 -10:00
parent de78ba6d39
commit cedcadfe3c
3 changed files with 19 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ import (
"github.com/ryanhamamura/games/connect4" "github.com/ryanhamamura/games/connect4"
"github.com/ryanhamamura/games/features/c4game/pages" "github.com/ryanhamamura/games/features/c4game/pages"
"github.com/ryanhamamura/games/features/c4game/services" "github.com/ryanhamamura/games/features/c4game/services"
sharedcomponents "github.com/ryanhamamura/games/features/common/components"
"github.com/ryanhamamura/games/sessions" "github.com/ryanhamamura/games/sessions"
) )
@@ -112,7 +113,10 @@ func HandleGameEvents(store *connect4.Store, svc *services.GameService, sm *scs.
return sse.PatchElementTempl(pages.GameContent(g, myColor, room.Messages(), chatCfg)) return sse.PatchElementTempl(pages.GameContent(g, myColor, room.Messages(), chatCfg))
} }
// Send initial state // Send initial connection indicator and state
if err := sse.PatchElementTempl(sharedcomponents.ConnectionIndicator(time.Now().UnixMilli())); err != nil {
return
}
if err := patchAll(); err != nil { if err := patchAll(); err != nil {
return return
} }
@@ -147,7 +151,10 @@ func HandleGameEvents(store *connect4.Store, svc *services.GameService, sm *scs.
} }
case <-heartbeat.C: case <-heartbeat.C:
// Heartbeat just keeps the connection alive by triggering a game state refresh // Heartbeat updates connection indicator and refreshes game state
if err := sse.PatchElementTempl(sharedcomponents.ConnectionIndicator(time.Now().UnixMilli())); err != nil {
return
}
if err := patchAll(); err != nil { if err := patchAll(); err != nil {
return return
} }

View File

@@ -18,6 +18,7 @@ templ GamePage(g *connect4.Game, myColor int, messages []chat.Message, chatCfg c
data-signals="{chatMsg: ''}" data-signals="{chatMsg: ''}"
data-init={ fmt.Sprintf("@get('/games/%s/events',{requestCancellation:'disabled'})", g.ID) } data-init={ fmt.Sprintf("@get('/games/%s/events',{requestCancellation:'disabled'})", g.ID) }
> >
@sharedcomponents.ConnectionIndicator(0)
@GameContent(g, myColor, messages, chatCfg) @GameContent(g, myColor, messages, chatCfg)
</main> </main>
} }

View File

@@ -12,6 +12,7 @@ import (
"github.com/ryanhamamura/games/chat" "github.com/ryanhamamura/games/chat"
chatcomponents "github.com/ryanhamamura/games/chat/components" chatcomponents "github.com/ryanhamamura/games/chat/components"
sharedcomponents "github.com/ryanhamamura/games/features/common/components"
"github.com/ryanhamamura/games/features/snakegame/pages" "github.com/ryanhamamura/games/features/snakegame/pages"
"github.com/ryanhamamura/games/features/snakegame/services" "github.com/ryanhamamura/games/features/snakegame/services"
"github.com/ryanhamamura/games/sessions" "github.com/ryanhamamura/games/sessions"
@@ -117,7 +118,10 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, svc *services.GameService,
return sse.PatchElementTempl(pages.GameContent(sg, mySlot, chatMessages(), chatCfg, gameID)) return sse.PatchElementTempl(pages.GameContent(sg, mySlot, chatMessages(), chatCfg, gameID))
} }
// Send initial render // Send initial connection indicator and render
if err := sse.PatchElementTempl(sharedcomponents.ConnectionIndicator(time.Now().UnixMilli())); err != nil {
return
}
if err := patchAll(); err != nil { if err := patchAll(); err != nil {
return return
} }
@@ -141,7 +145,10 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, svc *services.GameService,
return return
case <-heartbeat.C: case <-heartbeat.C:
// Heartbeat just refreshes game state // Heartbeat updates connection indicator and refreshes game state
if err := sse.PatchElementTempl(sharedcomponents.ConnectionIndicator(time.Now().UnixMilli())); err != nil {
return
}
if err := patchAll(); err != nil { if err := patchAll(); err != nil {
return return
} }