chore: gitignore generated _templ.go files, track .templ sources
Generated _templ.go files are deterministic output from .templ sources, same as output.css from input.css. Remove them from version control to reduce diff noise and merge conflicts. Add build:templ and live:templ tasks to the Taskfile so generation happens as part of the build.
This commit is contained in:
137
features/snakegame/components/status.templ
Normal file
137
features/snakegame/components/status.templ
Normal file
@@ -0,0 +1,137 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/ryanhamamura/c4/config"
|
||||
"github.com/ryanhamamura/c4/snake"
|
||||
"github.com/starfederation/datastar-go/datastar"
|
||||
)
|
||||
|
||||
templ StatusBanner(sg *snake.SnakeGame, mySlot int, gameID string) {
|
||||
<div id="snake-status">
|
||||
switch sg.Status {
|
||||
case snake.StatusWaitingForPlayers:
|
||||
if sg.Mode == snake.ModeSinglePlayer {
|
||||
<div class="alert bg-base-200 text-xl font-bold">Ready?</div>
|
||||
} else {
|
||||
<div class="alert bg-base-200 text-xl font-bold">Waiting for players...</div>
|
||||
}
|
||||
case snake.StatusCountdown:
|
||||
{{ remaining := time.Until(sg.CountdownEnd) }}
|
||||
{{ secs := int(math.Ceil(remaining.Seconds())) }}
|
||||
if secs < 0 {
|
||||
{{ secs = 0 }}
|
||||
}
|
||||
<div class="alert alert-info text-xl font-bold">
|
||||
{ fmt.Sprintf("Starting in %d...", secs) }
|
||||
</div>
|
||||
case snake.StatusInProgress:
|
||||
if sg.State != nil && mySlot >= 0 && mySlot < len(sg.State.Snakes) && sg.State.Snakes[mySlot] != nil && !sg.State.Snakes[mySlot].Alive {
|
||||
<div class="alert alert-error text-xl font-bold">You're out!</div>
|
||||
} else if sg.Mode == snake.ModeSinglePlayer {
|
||||
<div class="alert alert-success text-xl font-bold">
|
||||
{ fmt.Sprintf("Score: %d", sg.Score) }
|
||||
</div>
|
||||
} else {
|
||||
<div class="alert alert-success text-xl font-bold">Go!</div>
|
||||
}
|
||||
case snake.StatusFinished:
|
||||
@finishedBanner(sg, mySlot, gameID)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ finishedBanner(sg *snake.SnakeGame, mySlot int, gameID string) {
|
||||
if sg.Mode == snake.ModeSinglePlayer {
|
||||
<div class="alert alert-info text-xl font-bold">
|
||||
{ fmt.Sprintf("Game Over! Score: %d", sg.Score) }
|
||||
@rematchOrJoin(sg, gameID)
|
||||
</div>
|
||||
} else if sg.Winner != nil {
|
||||
if sg.Winner.Slot == mySlot {
|
||||
<div class="alert alert-success text-xl font-bold">
|
||||
You win!
|
||||
@rematchOrJoin(sg, gameID)
|
||||
</div>
|
||||
} else {
|
||||
<div class="alert alert-error text-xl font-bold">
|
||||
{ sg.Winner.Nickname + " wins!" }
|
||||
@rematchOrJoin(sg, gameID)
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
<div class="alert alert-warning text-xl font-bold">
|
||||
It's a draw!
|
||||
@rematchOrJoin(sg, gameID)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ rematchOrJoin(sg *snake.SnakeGame, gameID string) {
|
||||
if sg.RematchGameID != nil {
|
||||
<a class="btn btn-sm bg-white text-gray-800 border-none ml-4" href={ templ.SafeURL("/snake/" + *sg.RematchGameID) }>
|
||||
Join Rematch
|
||||
</a>
|
||||
} else {
|
||||
<button
|
||||
class="btn btn-sm bg-white text-gray-800 border-none ml-4"
|
||||
type="button"
|
||||
data-on:click={ datastar.PostSSE("/snake/%s/rematch", gameID) }
|
||||
>
|
||||
Play again
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
templ PlayerList(sg *snake.SnakeGame, mySlot int) {
|
||||
<div id="snake-players" class="flex flex-wrap gap-4 mb-2">
|
||||
for i, p := range sg.Players {
|
||||
if p != nil {
|
||||
<div class="flex items-center gap-2">
|
||||
<span style={ fmt.Sprintf("width:16px;height:16px;border-radius:50%%;background:%s;display:inline-block;", snakeColor(i)) }></span>
|
||||
<span>
|
||||
{ p.Nickname }
|
||||
if i == mySlot {
|
||||
{ " (You)" }
|
||||
}
|
||||
</span>
|
||||
if sg.Status == snake.StatusInProgress || sg.Status == snake.StatusFinished {
|
||||
if sg.State != nil && i < len(sg.State.Snakes) && sg.State.Snakes[i] != nil {
|
||||
if sg.State.Snakes[i].Alive {
|
||||
<span class="text-sm opacity-60">
|
||||
{ fmt.Sprintf("(%d)", len(sg.State.Snakes[i].Body)) }
|
||||
</span>
|
||||
} else {
|
||||
<span class="text-sm opacity-40">(dead)</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ InviteLink(gameID string) {
|
||||
{{ fullURL := config.Global.AppURL + "/snake/" + gameID }}
|
||||
<div id="snake-invite" class="mt-4 text-center">
|
||||
<p>Share this link to invite players:</p>
|
||||
<div class="bg-base-200 p-4 rounded-lg font-mono break-all my-2">
|
||||
{ fullURL }
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-sm mt-2"
|
||||
type="button"
|
||||
onclick={ copyToClipboard(fullURL) }
|
||||
>
|
||||
Copy Link
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
script copyToClipboard(url string) {
|
||||
navigator.clipboard.writeText(url)
|
||||
}
|
||||
Reference in New Issue
Block a user