Add game deletion with authorization check

- Add Delete method to GameStore and Persister interface
- Add delete button to game list on home page
- Verify user owns game before allowing deletion
- Use status constants instead of magic numbers
- Remove unused variable in persister
This commit is contained in:
Ryan Hamamura
2026-01-14 17:44:09 -10:00
parent d96f7dcc29
commit 5f452914f8
5 changed files with 91 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"time"
"github.com/ryanhamamura/c4/game"
"github.com/ryanhamamura/via/h"
)
@@ -15,14 +16,14 @@ type GameListItem struct {
LastPlayed time.Time
}
func GameList(games []GameListItem) h.H {
func GameList(games []GameListItem, deleteClick func(id string) h.H) h.H {
if len(games) == 0 {
return nil
}
var items []h.H
for _, g := range games {
items = append(items, gameListEntry(g))
items = append(items, gameListEntry(g, deleteClick))
}
listItems := []h.H{h.Class("game-list-items")}
@@ -34,27 +35,35 @@ func GameList(games []GameListItem) h.H {
)
}
func gameListEntry(g GameListItem) h.H {
func gameListEntry(g GameListItem, deleteClick func(id string) h.H) h.H {
statusText, statusClass := getStatusDisplay(g)
return h.A(
h.Href("/game/"+g.ID),
h.Class("game-entry"),
h.Div(h.Class("game-entry-main"),
h.Span(h.Class("opponent-name"), h.Text(getOpponentDisplay(g))),
h.Span(h.Class("game-status "+statusClass), h.Text(statusText)),
return h.Div(h.Class("game-entry"),
h.A(
h.Href("/game/"+g.ID),
h.Class("game-entry-link"),
h.Div(h.Class("game-entry-main"),
h.Span(h.Class("opponent-name"), h.Text(getOpponentDisplay(g))),
h.Span(h.Class("game-status "+statusClass), h.Text(statusText)),
),
h.Div(h.Class("game-entry-meta"),
h.Span(h.Class("time-ago"), h.Text(formatTimeAgo(g.LastPlayed))),
),
),
h.Div(h.Class("game-entry-meta"),
h.Span(h.Class("time-ago"), h.Text(formatTimeAgo(g.LastPlayed))),
h.Button(
h.Type("button"),
h.Class("game-delete-btn"),
h.Text("\u00d7"),
deleteClick(g.ID),
),
)
}
func getStatusDisplay(g GameListItem) (string, string) {
switch g.Status {
case 0: // Waiting
switch game.GameStatus(g.Status) {
case game.StatusWaitingForPlayer:
return "Waiting for opponent", "waiting"
case 1: // In progress
case game.StatusInProgress:
if g.IsMyTurn {
return "Your turn!", "your-turn"
}