Enha: sse update on actions

This commit is contained in:
Grail Finder
2025-05-10 11:03:23 +03:00
parent 416cc63ec0
commit 35e215e26f
12 changed files with 89 additions and 73 deletions

View File

@ -41,7 +41,7 @@
</style> </style>
</head> </head>
<body> <body>
<div id=ancestor> <div id="ancestor" hx-ext="sse" sse-connect="/sub/sse">
{{template "main" .}} {{template "main" .}}
</div> </div>
</body> </body>

View File

@ -22,7 +22,7 @@
color: white; color: white;
text-shadow: 0 2px 4px rgba(0,0,0,0.8); text-shadow: 0 2px 4px rgba(0,0,0,0.8);
cursor: pointer;" cursor: pointer;"
hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.1s"> hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.05s">
{{.Word}} {{.Word}}
</div> </div>
{{end}} {{end}}

View File

@ -1,8 +1,4 @@
{{define "main"}} {{define "main"}}
<div hx-ext="sse" sse-connect="/sub/sse" sse-swap="test">
Contents of this box will be updated in real time
with every SSE message received from the chatroom.
</div>
<!-- user has no username -> login form --> <!-- user has no username -> login form -->
{{ if not . }} {{ if not . }}
{{template "login"}} {{template "login"}}

View File

@ -1,12 +1,12 @@
{{define "teamlist"}} {{define "teamlist"}}
<div class="playerlist border border-gray-300 rounded mb-2"> <div class="playerlist border border-gray-300 text-{{.Color}}-500 rounded mb-2">
<p class=border>Guessers</p> <p class=border>Guessers</p>
{{range .Guessers}} {{range .Guessers}}
<p>{{.}}</p> <p>{{.}}</p>
{{end}} {{end}}
</div> </div>
<hr /> <hr />
<div class="playerlist border border-gray-300 rounded mb-2"> <div class="playerlist border border-gray-300 rounded mb-2 text-{{.Color}}-700">
<p class=border>Mime</p> <p class=border>Mime</p>
<p>{{.Mime}}</p> <p>{{.Mime}}</p>
</div> </div>

View File

@ -1,5 +1,6 @@
{{define "room"}} {{define "room"}}
<div id="hello-user"> <div id="interier" hx-get="/" hx-trigger="sse:roomupdate_{{.State.RoomID}}">
<div id="meta">
<p>Hello {{.State.Username}};</p> <p>Hello {{.State.Username}};</p>
<p>Room created by {{.Room.CreatorName}};</p> <p>Room created by {{.Room.CreatorName}};</p>
<p>Game is running: {{.Room.IsRunning}}</p> <p>Game is running: {{.Room.IsRunning}}</p>
@ -8,37 +9,38 @@
<button hx-get="/start-game" hx-target="#room" class="bg-amber-100 text-black px-4 py-2 rounded">Start Game</button> <button hx-get="/start-game" hx-target="#room" class="bg-amber-100 text-black px-4 py-2 rounded">Start Game</button>
{{end}} {{end}}
</p> </p>
<p>Turn of the {{.Room.TeamTurn}} team</p> <p>Turn of the <span class="text-{{.Room.TeamTurn}}-500">{{.Room.TeamTurn}}</span> team</p>
<p> <p>
{{if eq .State.Team ""}} {{if eq .State.Team ""}}
join the team! join the team!
{{else}} {{else}}
you're on the team {{.State.Team}}! you're on the team <span class="text-{{.State.Team}}-500">{{.State.Team}}</span>!
{{end}} {{end}}
</p> </p>
</div> </div>
<hr /> <hr />
<div class="flex justify-center"> <div class="flex justify-center">
<!-- Left Panel --> <!-- Left Panel -->
{{template "teamlist" .Room.BlueTeam}} {{template "teamlist" .Room.BlueTeam}}
{{if ne .State.Team "blue"}} {{if and (ne .State.Team "blue") (not .Room.IsRunning)}}
{{template "teampew" "blue"}} {{template "teampew" "blue"}}
{{end}} {{end}}
<!-- Right Panel --> <!-- Right Panel -->
{{if ne .State.Team "red"}} {{if and (ne .State.Team "red") (not .Room.IsRunning)}}
{{template "teampew" "red"}} {{template "teampew" "red"}}
{{end}} {{end}}
{{template "teamlist" .Room.RedTeam}} {{template "teamlist" .Room.RedTeam}}
</div> </div>
<hr /> <hr />
<div id="cardtable"> <div id="cardtable">
{{template "cardtable" .Room}} {{template "cardtable" .Room}}
</div> </div>
<div> <div>
{{if eq .State.Role "guesser"}} {{if and (eq .State.Role "guesser") (eq .State.Team .Room.TeamTurn)}}
<button hx-get="/end-turn" hx-target="#room" class="bg-amber-100 text-black px-4 py-2 rounded">End Turn</button> <button hx-get="/end-turn" hx-target="#room" class="bg-amber-100 text-black px-4 py-2 rounded">End Turn</button>
{{else if eq .State.Role "mime"}} {{else if eq .State.Role "mime"}}
{{template "mimeclue"}} {{template "mimeclue"}}
{{end}} {{end}}
</div>
</div> </div>
{{end}} {{end}}

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"golias/broker"
"golias/models" "golias/models"
"golias/utils" "golias/utils"
"strings" "strings"
@ -168,6 +169,7 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error)
return fi, err return fi, err
} }
fi.Room.BlueTeam.Mime = fi.State.Username fi.Room.BlueTeam.Mime = fi.State.Username
fi.Room.BlueTeam.Color = "blue"
fi.State.Team = "blue" fi.State.Team = "blue"
fi.State.Role = "mime" fi.State.Role = "mime"
if fi.Room.RedTeam.Mime == fi.State.Username { if fi.Room.RedTeam.Mime == fi.State.Username {
@ -180,6 +182,7 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error)
return fi, err return fi, err
} }
fi.Room.RedTeam.Mime = fi.State.Username fi.Room.RedTeam.Mime = fi.State.Username
fi.Room.RedTeam.Color = "red"
fi.State.Team = "red" fi.State.Team = "red"
fi.State.Role = "mime" fi.State.Role = "mime"
if fi.Room.BlueTeam.Mime == fi.State.Username { if fi.Room.BlueTeam.Mime == fi.State.Username {
@ -192,10 +195,12 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error)
} else if role == "guesser" { } else if role == "guesser" {
if team == "blue" { if team == "blue" {
fi.Room.BlueTeam.Guessers = append(fi.Room.BlueTeam.Guessers, fi.State.Username) fi.Room.BlueTeam.Guessers = append(fi.Room.BlueTeam.Guessers, fi.State.Username)
fi.Room.BlueTeam.Color = "blue"
fi.State.Team = "blue" fi.State.Team = "blue"
fi.State.Role = "guesser" fi.State.Role = "guesser"
} else if team == "red" { } else if team == "red" {
fi.Room.RedTeam.Guessers = append(fi.Room.RedTeam.Guessers, fi.State.Username) fi.Room.RedTeam.Guessers = append(fi.Room.RedTeam.Guessers, fi.State.Username)
fi.Room.RedTeam.Color = "red"
fi.State.Team = "red" fi.State.Team = "red"
fi.State.Role = "guesser" fi.State.Role = "guesser"
} else { } else {
@ -232,3 +237,10 @@ func listPublicRooms() []*models.Room {
} }
return publicRooms return publicRooms
} }
func notify(event, msg string) {
Notifier.Notifier <- broker.NotificationEvent{
EventName: event,
Payload: msg,
}
}

View File

@ -35,24 +35,23 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
session, ok := ctx.Value(models.CtxSessionKey).(*models.Session) fi, err := getFullInfoByCtx(ctx)
if !ok {
// trying to get color without a session -> error
http.Redirect(w, r, "/", 302)
return
}
state, err := loadState(session.Username)
if err != nil { if err != nil {
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
// TODO: whos move it is? if fi.State.Role != "guesser" {
if state.Role != "guesser" {
err = errors.New("need to guesser to open the card") err = errors.New("need to guesser to open the card")
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
log.Debug("got state", "state", state) // whos move it is?
if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) {
err = errors.New("not your team's move")
abortWithError(w, err.Error())
return
}
log.Debug("got state", "state", fi)
// TODO: update room score // TODO: update room score
color, exists := roundWords[word] color, exists := roundWords[word]
log.Debug("got show-color request", "word", word, "color", color) log.Debug("got show-color request", "word", word, "color", color)
@ -65,5 +64,11 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) {
Color: models.StrToWordColor(color), Color: models.StrToWordColor(color),
Revealed: true, Revealed: true,
} }
fi.Room.RevealSpecificWord(word)
if err := saveFullInfo(fi); err != nil {
abortWithError(w, err.Error())
return
}
notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
tmpl.ExecuteTemplate(w, "cardword", cardword) tmpl.ExecuteTemplate(w, "cardword", cardword)
} }

View File

@ -149,6 +149,7 @@ func HandleJoinTeam(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
tmpl.ExecuteTemplate(w, "base", fi) tmpl.ExecuteTemplate(w, "base", fi)
} }
@ -176,6 +177,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
tmpl.ExecuteTemplate(w, "base", fi) tmpl.ExecuteTemplate(w, "base", fi)
} }
@ -204,6 +206,8 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error()) abortWithError(w, err.Error())
return return
} }
// to update only the room that should be updated
notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
tmpl.ExecuteTemplate(w, "room", fi) tmpl.ExecuteTemplate(w, "room", fi)
} }

View File

@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"golias/broker" "golias/broker"
"golias/config" "golias/config"
"golias/pkg/cache" "golias/pkg/cache"
@ -9,7 +8,6 @@ import (
"log/slog" "log/slog"
"net/http" "net/http"
"os" "os"
"time"
) )
var ( var (
@ -28,18 +26,6 @@ func init() {
cfg = config.LoadConfigOrDefault("") cfg = config.LoadConfigOrDefault("")
Notifier = broker.NewBroker() Notifier = broker.NewBroker()
go Notifier.Listen() go Notifier.Listen()
ticker := time.NewTicker(2 * time.Second)
go func() {
counter := 0
for {
<-ticker.C
Notifier.Notifier <- broker.NotificationEvent{
EventName: "test",
Payload: fmt.Sprintf("%v test call of notifier", counter),
}
counter++
}
}()
} }
var roundWords = map[string]string{ var roundWords = map[string]string{

View File

@ -7,4 +7,6 @@ var (
// cache // cache
CacheRoomPrefix = "room#" CacheRoomPrefix = "room#"
CacheStatePrefix = "state-" CacheStatePrefix = "state-"
// sse
NotifyRoomUpdatePrefix = "roomupdate_"
) )

View File

@ -34,6 +34,7 @@ func StrToWordColor(s string) WordColor {
type Team struct { type Team struct {
Guessers []string Guessers []string
Mime string Mime string
Color string
} }
type Room struct { type Room struct {
@ -109,6 +110,14 @@ func (r *Room) RevealAllCards() {
} }
} }
func (r *Room) RevealSpecificWord(word string) {
for i, card := range r.Cards {
if card.Word == word {
r.Cards[i].Revealed = true
}
}
}
type WordCard struct { type WordCard struct {
Word string Word string
Color WordColor Color WordColor

View File

@ -75,8 +75,8 @@ func MakeTestState(creatorName string) *FullInfo {
{Word: "tomato", Color: "red"}, {Word: "tomato", Color: "red"},
{Word: "cloud", Color: "white"}, {Word: "cloud", Color: "white"},
} }
redTeam := Team{Guessers: []string{"Adam", "Eve"}, Mime: "Serpent"} redTeam := Team{Guessers: []string{"Adam", "Eve"}, Mime: "Serpent", Color: "red"}
blueTeam := Team{Guessers: []string{"Abel", "Kain"}} blueTeam := Team{Guessers: []string{"Abel", "Kain"}, Color: "blue"}
room := &Room{ room := &Room{
ID: "test-id", ID: "test-id",
CreatedAt: time.Now(), CreatedAt: time.Now(),