Feat: roomlist & join room
This commit is contained in:
		| @@ -10,10 +10,13 @@ | ||||
| 	<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> | ||||
| 	</div> | ||||
| 	<div> | ||||
| 	{{template "roomlist" .List}} | ||||
| 	</div> | ||||
| 	{{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"> | ||||
| 	{{template "room" .}} | ||||
| 	</div> | ||||
| {{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" | ||||
| 	"golias/models" | ||||
| 	"golias/utils" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| 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 { | ||||
| 	key := models.CacheRoomPrefix + room.ID | ||||
| 	data, err := json.Marshal(room) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	memcache.Set(models.CacheRoomPrefix+room.ID, data) | ||||
| 	log.Debug("saved room", "room", room, "key", key) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -208,3 +211,24 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) | ||||
| 	} | ||||
| 	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) { | ||||
| 	w.WriteHeader(500) | ||||
| 	w.WriteHeader(200) // must be 200 for htmx to replace components | ||||
| 	tmpl := template.Must(template.ParseGlob("components/*.html")) | ||||
| 	tmpl.ExecuteTemplate(w, "error", msg) | ||||
| } | ||||
| @@ -111,7 +111,7 @@ func makeCookie(username string, remote string) (*http.Cookie, error) { | ||||
| 	cookie := &http.Cookie{ | ||||
| 		Name:     cookieName, | ||||
| 		Value:    cookieValue, | ||||
| 		// Secure:   true, | ||||
| 		Secure:   true, | ||||
| 		HttpOnly: true, | ||||
| 		SameSite: http.SameSiteNoneMode, | ||||
| 		Domain:   cfg.ServerConfig.Host, | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package handlers | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"golias/models" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
| @@ -45,6 +46,12 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		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) | ||||
| 	// TODO: update room score | ||||
| 	color, exists := roundWords[word] | ||||
|   | ||||
| @@ -8,6 +8,16 @@ import ( | ||||
| 	"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) { | ||||
| 	// parse payload | ||||
| 	payload := &models.RoomReq{ | ||||
| @@ -32,6 +42,7 @@ func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | ||||
| 	} | ||||
| 	fi.State.RoomID = room.ID | ||||
| 	fi.Room = room | ||||
| 	fi.Room.IsPublic = true // hardcode for local test; move to form | ||||
| 	if err := saveFullInfo(fi); err != nil { | ||||
| 		msg := "failed to set current room to session" | ||||
| 		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) { | ||||
| 	// parse payload | ||||
| 	// team := r.URL.Query().Get("team") | ||||
| 	// role := r.URL.Query().Get("role") | ||||
| 	r.ParseForm() | ||||
| 	team := r.PostFormValue("team") | ||||
| 	role := r.PostFormValue("role") | ||||
| @@ -121,11 +129,20 @@ func HandleJoinTeam(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		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) | ||||
| 	if err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	// reveal all cards | ||||
| 	if role == "mime" { | ||||
| 		fi.Room.RevealAllCards() | ||||
| 	} | ||||
| 	// return html | ||||
| 	tmpl, err := template.ParseGlob("components/*.html") | ||||
| 	if err != nil { | ||||
| @@ -177,6 +194,39 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		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 | ||||
| 	tmpl, err := template.ParseGlob("components/*.html") | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -63,6 +63,15 @@ func HandleHome(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 	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) | ||||
| 	if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { | ||||
| 		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("POST /room-create", handlers.HandleCreateRoom) | ||||
| 	mux.HandleFunc("GET /start-game", handlers.HandleStartGame) | ||||
| 	mux.HandleFunc("GET /room-join", handlers.HandleJoinRoom) | ||||
| 	// mux.HandleFunc("GET /roomlist", handlers.HandleRoomList) | ||||
| 	//elements | ||||
| 	mux.HandleFunc("GET /room/createform", handlers.HandleShowCreateForm) | ||||
| 	mux.HandleFunc("GET /room/hideform", handlers.HandleHideCreateForm) | ||||
|   | ||||
| @@ -52,6 +52,7 @@ type Room struct { | ||||
| 	BlueCounter uint8 | ||||
| 	RedCounter  uint8 | ||||
| 	RedTurn     bool // false is blue turn | ||||
| 	IsPublic    bool | ||||
| 	// GameSettings *GameSettings `json:"settings"` | ||||
| 	IsRunning bool   `json:"is_running"` | ||||
| 	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 { | ||||
| 	Word     string | ||||
| 	Color    WordColor | ||||
| @@ -142,4 +149,5 @@ func (rr *RoomReq) CreateRoom(creator string) *Room { | ||||
| type FullInfo struct { | ||||
| 	State *UserState | ||||
| 	Room  *Room | ||||
| 	List  []*Room | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder