Feat: roomlist & join room
This commit is contained in:
@ -10,10 +10,13 @@
|
|||||||
<div id="create-room" class="create-room-div">
|
<div id="create-room" class="create-room-div">
|
||||||
<button button id="create-form-btn" type="submit" class="justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" hx-get="/room/createform" hx-swap="outerHTML">SHOW ROOM CREATE FORM</button>
|
<button button id="create-form-btn" type="submit" class="justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" hx-get="/room/createform" hx-swap="outerHTML">SHOW ROOM CREATE FORM</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{{template "roomlist" .List}}
|
||||||
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<!-- user has room id => send him to his room -->
|
<!-- instead of having room div; better to replace ancestor completely with room -->
|
||||||
<div id="room">
|
<div id="room">
|
||||||
{{template "room" .}}
|
{{template "room" .}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
27
components/roomlist.html
Normal file
27
components/roomlist.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{{define "roomlist"}}
|
||||||
|
<div id="roomlist">
|
||||||
|
{{range .}}
|
||||||
|
<p>
|
||||||
|
{{.ID}}
|
||||||
|
</p>
|
||||||
|
<div class="room-item mb-3 p-4 border rounded-lg hover:bg-gray-50 transition-colors">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div hx-get="/room-join?id={{.ID}}" hx-target="#ancestor" class="room-info">
|
||||||
|
<div class="text-sm text-gray-500">
|
||||||
|
Created {{.CreatedAt.Format "2 Jan 2006 15:04"}} by
|
||||||
|
<span class="font-medium text-gray-700">{{.CreatorName}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 flex items-center gap-3">
|
||||||
|
<span class="px-2 py-1 text-xs font-medium rounded-full {{if .IsGameRunning}}bg-green-100 text-green-800{{else}}bg-gray-100 text-gray-600{{end}}">
|
||||||
|
{{if .IsRunning}}Game Active{{else}}Waiting Room{{end}}
|
||||||
|
</span>
|
||||||
|
<!-- <span class="text-sm text-gray-600"> -->
|
||||||
|
<!-- {{.PlayersCount}} player{{if ne .PlayersCount 1}}s{{end}} -->
|
||||||
|
<!-- </span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"golias/models"
|
"golias/models"
|
||||||
"golias/utils"
|
"golias/utils"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) {
|
func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) {
|
||||||
@ -22,11 +23,13 @@ func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveRoom(room *models.Room) error {
|
func saveRoom(room *models.Room) error {
|
||||||
|
key := models.CacheRoomPrefix + room.ID
|
||||||
data, err := json.Marshal(room)
|
data, err := json.Marshal(room)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
memcache.Set(models.CacheRoomPrefix+room.ID, data)
|
memcache.Set(models.CacheRoomPrefix+room.ID, data)
|
||||||
|
log.Debug("saved room", "room", room, "key", key)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,3 +211,24 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error)
|
|||||||
}
|
}
|
||||||
return fi, nil
|
return fi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get all rooms
|
||||||
|
func listPublicRooms() []*models.Room {
|
||||||
|
cacheMap := memcache.GetAll()
|
||||||
|
publicRooms := []*models.Room{}
|
||||||
|
// no way to know if room is public until unmarshal -_-;
|
||||||
|
for key, value := range cacheMap {
|
||||||
|
if strings.HasPrefix(key, models.CacheRoomPrefix) {
|
||||||
|
room := &models.Room{}
|
||||||
|
if err := json.Unmarshal(value, &room); err != nil {
|
||||||
|
log.Warn("failed to unmarshal room", "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Debug("consider room for list", "room", room, "key", key)
|
||||||
|
if room.IsPublic {
|
||||||
|
publicRooms = append(publicRooms, room)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicRooms
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func abortWithError(w http.ResponseWriter, msg string) {
|
func abortWithError(w http.ResponseWriter, msg string) {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(200) // must be 200 for htmx to replace components
|
||||||
tmpl := template.Must(template.ParseGlob("components/*.html"))
|
tmpl := template.Must(template.ParseGlob("components/*.html"))
|
||||||
tmpl.ExecuteTemplate(w, "error", msg)
|
tmpl.ExecuteTemplate(w, "error", msg)
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ func makeCookie(username string, remote string) (*http.Cookie, error) {
|
|||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: cookieName,
|
Name: cookieName,
|
||||||
Value: cookieValue,
|
Value: cookieValue,
|
||||||
// Secure: true,
|
Secure: true,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
SameSite: http.SameSiteNoneMode,
|
SameSite: http.SameSiteNoneMode,
|
||||||
Domain: cfg.ServerConfig.Host,
|
Domain: cfg.ServerConfig.Host,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"golias/models"
|
"golias/models"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -45,6 +46,12 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) {
|
|||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// TODO: whos move it is?
|
||||||
|
if state.Role != "guesser" {
|
||||||
|
err = errors.New("need to guesser to open the card")
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Debug("got state", "state", state)
|
log.Debug("got state", "state", state)
|
||||||
// TODO: update room score
|
// TODO: update room score
|
||||||
color, exists := roundWords[word]
|
color, exists := roundWords[word]
|
||||||
|
@ -8,6 +8,16 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// func HandleRoomList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// pubRooms := listPublicRooms()
|
||||||
|
// tmpl, err := template.ParseGlob("components/*.html")
|
||||||
|
// if err != nil {
|
||||||
|
// abortWithError(w, err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// tmpl.ExecuteTemplate(w, "base", pubRooms)
|
||||||
|
// }
|
||||||
|
|
||||||
func HandleCreateRoom(w http.ResponseWriter, r *http.Request) {
|
func HandleCreateRoom(w http.ResponseWriter, r *http.Request) {
|
||||||
// parse payload
|
// parse payload
|
||||||
payload := &models.RoomReq{
|
payload := &models.RoomReq{
|
||||||
@ -32,6 +42,7 @@ func HandleCreateRoom(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
fi.State.RoomID = room.ID
|
fi.State.RoomID = room.ID
|
||||||
fi.Room = room
|
fi.Room = room
|
||||||
|
fi.Room.IsPublic = true // hardcode for local test; move to form
|
||||||
if err := saveFullInfo(fi); err != nil {
|
if err := saveFullInfo(fi); err != nil {
|
||||||
msg := "failed to set current room to session"
|
msg := "failed to set current room to session"
|
||||||
log.Error(msg, "error", err)
|
log.Error(msg, "error", err)
|
||||||
@ -103,9 +114,6 @@ func HandleRoomEnter(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HandleJoinTeam(w http.ResponseWriter, r *http.Request) {
|
func HandleJoinTeam(w http.ResponseWriter, r *http.Request) {
|
||||||
// parse payload
|
|
||||||
// team := r.URL.Query().Get("team")
|
|
||||||
// role := r.URL.Query().Get("role")
|
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
team := r.PostFormValue("team")
|
team := r.PostFormValue("team")
|
||||||
role := r.PostFormValue("role")
|
role := r.PostFormValue("role")
|
||||||
@ -121,11 +129,20 @@ func HandleJoinTeam(w http.ResponseWriter, r *http.Request) {
|
|||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if fi.Room.IsRunning && role == "mime" {
|
||||||
|
err = errors.New("cannot join as mime when game is running")
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
fi, err = joinTeam(r.Context(), role, team)
|
fi, err = joinTeam(r.Context(), role, team)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// reveal all cards
|
||||||
|
if role == "mime" {
|
||||||
|
fi.Room.RevealAllCards()
|
||||||
|
}
|
||||||
// return html
|
// return html
|
||||||
tmpl, err := template.ParseGlob("components/*.html")
|
tmpl, err := template.ParseGlob("components/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -177,6 +194,39 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
|
|||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// reveal all cards
|
||||||
|
if fi.State.Role == "mime" {
|
||||||
|
fi.Room.RevealAllCards()
|
||||||
|
}
|
||||||
|
// return html
|
||||||
|
tmpl, err := template.ParseGlob("components/*.html")
|
||||||
|
if err != nil {
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmpl.ExecuteTemplate(w, "room", fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleJoinRoom(w http.ResponseWriter, r *http.Request) {
|
||||||
|
roomID := r.URL.Query().Get("id")
|
||||||
|
room, err := getRoomByID(roomID)
|
||||||
|
if err != nil {
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fi, err := getFullInfoByCtx(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
room.PlayerList = append(room.PlayerList, fi.State.Username)
|
||||||
|
fi.State.RoomID = room.ID
|
||||||
|
fi.Room = room
|
||||||
|
fi.List = nil
|
||||||
|
if err := saveFullInfo(fi); err != nil {
|
||||||
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
// return html
|
// return html
|
||||||
tmpl, err := template.ParseGlob("components/*.html")
|
tmpl, err := template.ParseGlob("components/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,6 +63,15 @@ func HandleHome(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
fi, _ := getFullInfoByCtx(r.Context())
|
fi, _ := getFullInfoByCtx(r.Context())
|
||||||
|
if fi != nil && fi.Room != nil && fi.State != nil {
|
||||||
|
if fi.State.Role == "mime" {
|
||||||
|
fi.Room.RevealAllCards()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fi != nil && fi.Room == nil {
|
||||||
|
log.Debug("loading list")
|
||||||
|
fi.List = listPublicRooms()
|
||||||
|
}
|
||||||
log.Debug("data debug", "fi", fi)
|
log.Debug("data debug", "fi", fi)
|
||||||
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
||||||
log.Error("failed to exec templ;", "error", err, "templ", "base")
|
log.Error("failed to exec templ;", "error", err, "templ", "base")
|
||||||
|
2
main.go
2
main.go
@ -27,6 +27,8 @@ func ListenToRequests(port string) error {
|
|||||||
mux.HandleFunc("GET /end-turn", handlers.HandleEndTurn)
|
mux.HandleFunc("GET /end-turn", handlers.HandleEndTurn)
|
||||||
mux.HandleFunc("POST /room-create", handlers.HandleCreateRoom)
|
mux.HandleFunc("POST /room-create", handlers.HandleCreateRoom)
|
||||||
mux.HandleFunc("GET /start-game", handlers.HandleStartGame)
|
mux.HandleFunc("GET /start-game", handlers.HandleStartGame)
|
||||||
|
mux.HandleFunc("GET /room-join", handlers.HandleJoinRoom)
|
||||||
|
// mux.HandleFunc("GET /roomlist", handlers.HandleRoomList)
|
||||||
//elements
|
//elements
|
||||||
mux.HandleFunc("GET /room/createform", handlers.HandleShowCreateForm)
|
mux.HandleFunc("GET /room/createform", handlers.HandleShowCreateForm)
|
||||||
mux.HandleFunc("GET /room/hideform", handlers.HandleHideCreateForm)
|
mux.HandleFunc("GET /room/hideform", handlers.HandleHideCreateForm)
|
||||||
|
@ -52,6 +52,7 @@ type Room struct {
|
|||||||
BlueCounter uint8
|
BlueCounter uint8
|
||||||
RedCounter uint8
|
RedCounter uint8
|
||||||
RedTurn bool // false is blue turn
|
RedTurn bool // false is blue turn
|
||||||
|
IsPublic bool
|
||||||
// GameSettings *GameSettings `json:"settings"`
|
// GameSettings *GameSettings `json:"settings"`
|
||||||
IsRunning bool `json:"is_running"`
|
IsRunning bool `json:"is_running"`
|
||||||
Language string `json:"language" example:"en" form:"language"`
|
Language string `json:"language" example:"en" form:"language"`
|
||||||
@ -102,6 +103,12 @@ func (r *Room) ChangeTurn() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Room) RevealAllCards() {
|
||||||
|
for i := range r.Cards {
|
||||||
|
r.Cards[i].Revealed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type WordCard struct {
|
type WordCard struct {
|
||||||
Word string
|
Word string
|
||||||
Color WordColor
|
Color WordColor
|
||||||
@ -142,4 +149,5 @@ func (rr *RoomReq) CreateRoom(creator string) *Room {
|
|||||||
type FullInfo struct {
|
type FullInfo struct {
|
||||||
State *UserState
|
State *UserState
|
||||||
Room *Room
|
Room *Room
|
||||||
|
List []*Room
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user