From 4faf4f73b078d0be3c493403ab472dc691c28660 Mon Sep 17 00:00:00 2001 From: Ryan Hamamura <58859899+ryanhamamura@users.noreply.github.com> Date: Mon, 2 Mar 2026 22:34:20 -1000 Subject: [PATCH] refactor: patch entire game content for snake SSE handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same approach as connect4 — extract GameContent component and patch it as a single element, letting DOM morphing handle the diff. --- features/snakegame/handlers.go | 57 ++++++++++++++--------------- features/snakegame/pages/game.templ | 44 ++++++++++++---------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/features/snakegame/handlers.go b/features/snakegame/handlers.go index effa3ff..9620377 100644 --- a/features/snakegame/handlers.go +++ b/features/snakegame/handlers.go @@ -12,7 +12,6 @@ import ( "github.com/ryanhamamura/games/chat" chatcomponents "github.com/ryanhamamura/games/chat/components" - "github.com/ryanhamamura/games/features/snakegame/components" "github.com/ryanhamamura/games/features/snakegame/pages" "github.com/ryanhamamura/games/sessions" "github.com/ryanhamamura/games/snake" @@ -100,16 +99,33 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.Sess chatCfg := snakeChatConfig(gameID) - // Send initial render + // Chat room (multiplayer only) + var room *chat.Room sg := si.GetGame() - sse.PatchElementTempl(components.Board(sg)) //nolint:errcheck - sse.PatchElementTempl(components.StatusBanner(sg, mySlot, gameID)) //nolint:errcheck - sse.PatchElementTempl(components.PlayerList(sg, mySlot)) //nolint:errcheck if sg.Mode == snake.ModeMultiplayer { - sse.PatchElementTempl(chatcomponents.Chat(nil, chatCfg)) //nolint:errcheck - if sg.Status == snake.StatusWaitingForPlayers || sg.Status == snake.StatusCountdown { - sse.PatchElementTempl(components.InviteLink(gameID)) //nolint:errcheck + room = chat.NewRoom(nc, snake.ChatSubject(gameID)) + } + + chatMessages := func() []chat.Message { + if room == nil { + return nil } + return room.Messages() + } + + patchAll := func() error { + si, ok = snakeStore.Get(gameID) + if !ok { + return fmt.Errorf("game not found") + } + mySlot = si.GetPlayerSlot(playerID) + sg = si.GetGame() + return sse.PatchElementTempl(pages.GameContent(sg, mySlot, chatMessages(), chatCfg, gameID)) + } + + // Send initial render + if err := patchAll(); err != nil { + return } // Subscribe to game updates via NATS @@ -123,10 +139,8 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.Sess // Chat subscription (multiplayer only) var chatCh chan *nats.Msg var chatSub *nats.Subscription - var room *chat.Room - if sg.Mode == snake.ModeMultiplayer { - room = chat.NewRoom(nc, snake.ChatSubject(gameID)) + if room != nil { chatCh, chatSub, err = room.Subscribe() if err != nil { return @@ -150,19 +164,7 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.Sess } } drained: - si, ok = snakeStore.Get(gameID) - if !ok { - return - } - mySlot = si.GetPlayerSlot(playerID) - sg = si.GetGame() - if err := sse.PatchElementTempl(components.Board(sg)); err != nil { - return - } - if err := sse.PatchElementTempl(components.StatusBanner(sg, mySlot, gameID)); err != nil { - return - } - if err := sse.PatchElementTempl(components.PlayerList(sg, mySlot)); err != nil { + if err := patchAll(); err != nil { return } @@ -170,11 +172,8 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.Sess if msg == nil { continue } - _, snapshot := room.Receive(msg.Data) - if snapshot == nil { - continue - } - if err := sse.PatchElementTempl(chatcomponents.Chat(snapshot, chatCfg)); err != nil { + room.Receive(msg.Data) + if err := patchAll(); err != nil { return } } diff --git a/features/snakegame/pages/game.templ b/features/snakegame/pages/game.templ index 63bda61..98b98fb 100644 --- a/features/snakegame/pages/game.templ +++ b/features/snakegame/pages/game.templ @@ -37,29 +37,35 @@ templ GamePage(sg *snake.SnakeGame, mySlot int, messages []chat.Message, chatCfg data-on:keydown.throttle_100ms={ keydownScript(gameID) } tabindex="0" > - @components.BackToLobby() -