package plays import ( "fmt" "net/http" "strings" "time" "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{"No-op", "Logout"} ) type Lobby struct { *Base choice string cursor int updateCh chan struct{} users []models.UserStatus rooms []types.Room } func NewLobby(base *Base) *Lobby { m := Lobby{ Base: base, choice: "", cursor: 0, updateCh: make(chan struct{}, 1), } return &m } func updateLobbyInfo(m *Lobby) error { for { select { case <-m.updateCh: return nil default: var users []models.UserStatus resp, err := m.Base.client.R(). SetResult(&users). ForceContentType("application/json"). Get("/api/lobby/users") if resp.StatusCode() != http.StatusOK { tracing.Logger. Error("failed to get lobby users", zap.Error(err)) return err } m.users = users var rooms []types.Room // TODO: scan rooms _, err = m.Base.client.R(). SetResult(&rooms). ForceContentType("application/json"). Get("/api/lobby/rooms") if err != nil { tracing.Logger. Error("failed to get lobby rooms", zap.Error(err)) return err } m.rooms = rooms time.Sleep(refetchTick) } } } func (m *Lobby) Init() tea.Cmd { go updateLobbyInfo(m) return tea.Sequence(tea.ClearScreen, Tick(refreshTick)) } 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 } } case types.TickMsg: return m, Tick(refreshTick) } return m, nil } func (m *Lobby) View() string { var b strings.Builder fmt.Fprintf(&b, "Game lobby\n") fmt.Fprintf(&b, "User Status (%d):\n", len(m.users)) for _, user := range m.users { b.WriteString("- " + user.View() + "\n") } fmt.Fprintf(&b, "Room Status (%d):\n", len(m.rooms)) for _, room := range m.rooms { b.WriteString("- " + room.View() + "\n") } b.WriteString("==========\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 { m.updateCh <- struct{}{} switch m.choice { case "No-op": *queue = append(*queue, tea.NewProgram(NewLobby(m.Base))) case "Logout": *queue = append(*queue, tea.NewProgram(NewLogout(m.Base))) } return nil }