The default Datastar requestCancellation:'auto' was causing SSE connections to be cancelled whenever users interacted with the page (making moves, sending chat messages, etc.), breaking live updates.
84 lines
2.8 KiB
Plaintext
84 lines
2.8 KiB
Plaintext
package pages
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/ryanhamamura/games/chat"
|
|
chatcomponents "github.com/ryanhamamura/games/chat/components"
|
|
"github.com/ryanhamamura/games/features/common/components"
|
|
"github.com/ryanhamamura/games/features/common/layouts"
|
|
snakecomponents "github.com/ryanhamamura/games/features/snakegame/components"
|
|
"github.com/ryanhamamura/games/snake"
|
|
"github.com/starfederation/datastar-go/datastar"
|
|
)
|
|
|
|
// keydownScript builds the inline JS for a single data-on:keydown handler
|
|
// that dispatches WASD/arrow keys to direction POST endpoints.
|
|
func keydownScript(gameID string) string {
|
|
return fmt.Sprintf(
|
|
"const k=evt.key;"+
|
|
"if(k==='w'||k==='ArrowUp'){evt.preventDefault();%s}"+
|
|
"else if(k==='s'||k==='ArrowDown'){evt.preventDefault();%s}"+
|
|
"else if(k==='a'||k==='ArrowLeft'){evt.preventDefault();%s}"+
|
|
"else if(k==='d'||k==='ArrowRight'){evt.preventDefault();%s}",
|
|
datastar.PostSSE("/snake/%s/dir?d=0", gameID),
|
|
datastar.PostSSE("/snake/%s/dir?d=1", gameID),
|
|
datastar.PostSSE("/snake/%s/dir?d=2", gameID),
|
|
datastar.PostSSE("/snake/%s/dir?d=3", gameID),
|
|
)
|
|
}
|
|
|
|
templ GamePage(sg *snake.SnakeGame, mySlot int, messages []chat.Message, chatCfg chatcomponents.Config, gameID string) {
|
|
@layouts.Base("Snake") {
|
|
<main
|
|
class="snake-wrapper flex flex-col items-center gap-4 p-4"
|
|
data-signals={ `{"chatMsg":""}` }
|
|
data-init={ fmt.Sprintf("@get('/snake/%s/events',{requestCancellation:'disabled'})", gameID) }
|
|
data-on:keydown__throttle.100ms={ keydownScript(gameID) }
|
|
tabindex="0"
|
|
>
|
|
@GameContent(sg, mySlot, messages, chatCfg, gameID)
|
|
</main>
|
|
}
|
|
}
|
|
|
|
templ GameContent(sg *snake.SnakeGame, mySlot int, messages []chat.Message, chatCfg chatcomponents.Config, gameID string) {
|
|
<div id="game-content">
|
|
@components.BackToLobby()
|
|
<h1 class="text-3xl font-bold">~~~~</h1>
|
|
@snakecomponents.PlayerList(sg, mySlot)
|
|
@snakecomponents.StatusBanner(sg, mySlot, gameID)
|
|
if sg.Status == snake.StatusInProgress || sg.Status == snake.StatusFinished {
|
|
if sg.Mode == snake.ModeMultiplayer {
|
|
<div class="snake-game-area">
|
|
@snakecomponents.Board(sg)
|
|
@chatcomponents.Chat(messages, chatCfg)
|
|
</div>
|
|
} else {
|
|
@snakecomponents.Board(sg)
|
|
}
|
|
} else if sg.Mode == snake.ModeMultiplayer {
|
|
@chatcomponents.Chat(messages, chatCfg)
|
|
}
|
|
if sg.Mode == snake.ModeMultiplayer && (sg.Status == snake.StatusWaitingForPlayers || sg.Status == snake.StatusCountdown) {
|
|
@snakecomponents.InviteLink(gameID)
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ JoinPage(gameID string) {
|
|
@layouts.Base("Snake - Join") {
|
|
@components.GameJoinPrompt(
|
|
fmt.Sprintf("/login?return_url=/snake/%s", gameID),
|
|
fmt.Sprintf("/register?return_url=/snake/%s", gameID),
|
|
fmt.Sprintf("/snake/%s", gameID),
|
|
)
|
|
}
|
|
}
|
|
|
|
templ NicknamePage(gameID string) {
|
|
@layouts.Base("Snake - Join") {
|
|
@components.NicknamePrompt(fmt.Sprintf("/snake/%s/join", gameID))
|
|
}
|
|
}
|