package plays import ( "fmt" "net/http" "strings" "gitea.konchin.com/ytshih/inp2025/game/models" "gitea.konchin.com/ytshih/inp2025/game/tracing" "gitea.konchin.com/ytshih/inp2025/game/types" tea "github.com/charmbracelet/bubbletea" "go.uber.org/zap" ) var ( lobbyChoices = []string{"Create Room", "Join Room", "Refresh", "Logout"} ) type Lobby struct { *Base choice string cursor int users []models.UserStatus rooms []types.Room } func NewLobby(base *Base) *Lobby { m := &Lobby{ Base: base, choice: "", cursor: 0, } return m } func (m *Lobby) fetchLobbyInfo() tea.Msg { var users []models.UserStatus resp, err := m.Base.client.R(). SetResult(&users). Get("/api/lobby/users") if err != nil || resp.StatusCode() != http.StatusOK { tracing.Logger.Error("failed to get lobby users", zap.Error(err)) return nil } m.users = users var rooms []types.Room resp, err = m.Base.client.R(). SetResult(&rooms). Get("/api/lobby/rooms") if err != nil || resp.StatusCode() != http.StatusOK { tracing.Logger.Error("failed to get lobby rooms", zap.Error(err)) return nil } m.rooms = rooms return nil } func (m *Lobby) Init() tea.Cmd { return tea.Batch(tea.ClearScreen, m.fetchLobbyInfo) } func (m *Lobby) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+l": return m, tea.ClearScreen case "ctrl+c", "q": return m, tea.Interrupt case "enter": m.choice = lobbyChoices[m.cursor] return m, tea.Quit case "tab", "shift+tab", "up", "down": s := msg.String() if s == "up" || s == "shift+tab" { m.cursor-- } else { m.cursor++ } if m.cursor >= len(lobbyChoices) { m.cursor = 0 } else if m.cursor < 0 { m.cursor = len(lobbyChoices) - 1 } } } return m, nil } func (m *Lobby) View() string { var b strings.Builder fmt.Fprintf(&b, "Game lobby\n\n") fmt.Fprintf(&b, "Online Users (%d):\n", len(m.users)) for _, user := range m.users { b.WriteString("- " + user.View() + "\n") } b.WriteString("\n") fmt.Fprintf(&b, "Game Rooms (%d):\n", len(m.rooms)) for _, room := range m.rooms { b.WriteString("- " + room.View() + "\n") } b.WriteString("\n==========\n") for i := 0; i < len(lobbyChoices); i++ { if m.cursor == i { fmt.Fprintf(&b, "(•) %s\n", lobbyChoices[i]) } else { fmt.Fprintf(&b, "( ) %s\n", lobbyChoices[i]) } } return b.String() } func (m *Lobby) Next(queue *[]*tea.Program) error { switch m.choice { case "Refresh": *queue = append(*queue, tea.NewProgram(NewLobby(m.Base))) case "Create Room": resp, err := m.Base.client.R(). SetResult(&types.Room{}). Post("/api/rooms") if err != nil || resp.StatusCode() != http.StatusOK { return fmt.Errorf("failed to create room") } room := resp.Result().(*types.Room) *queue = append(*queue, tea.NewProgram(NewRoomWaiting(m.Base, room.ID))) case "Join Room": *queue = append(*queue, tea.NewProgram(NewJoinRoom(m.Base, m.rooms))) case "Logout": *queue = append(*queue, tea.NewProgram(NewLogout(m.Base))) } return nil }