Feat: roomlist & join room
This commit is contained in:
		| @@ -10,8 +10,11 @@ | |||||||
| 	<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> | ||||||
|   | |||||||
							
								
								
									
										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
	 Grail Finder
					Grail Finder