package stages import ( "fmt" "net/http" "strings" "gitea.konchin.com/ytshih/inp2025/models" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) type landingOperationType int const ( landingOperationChoose landingOperationType = iota landingOperationLoginCred landingOperationRegistCred ) type focusTargetType int const ( focusTargetUsername focusTargetType = iota focusTargetPassword ) type LandingModel struct { *BaseModel op landingOperationType focus focusTargetType username textinput.Model password textinput.Model info string err error } func NewLandingModel(base *BaseModel) *LandingModel { username := textinput.New() username.Placeholder = "Username" username.CharLimit = 32 username.Width = 32 username.Blur() password := textinput.New() password.Placeholder = "Password" password.CharLimit = 32 password.Width = 32 password.Blur() password.EchoMode = textinput.EchoPassword password.EchoCharacter = '•' return &LandingModel{ BaseModel: base, op: landingOperationChoose, username: username, password: password, } } func (m *LandingModel) reset() { m.op = landingOperationChoose m.username.Blur() m.username.Reset() m.password.Blur() m.password.Reset() } type postLoginMsg struct{} func (m *LandingModel) postLogin() tea.Cmd { return func() tea.Msg { resp, err := m.BaseModel.client.R(). SetBasicAuth(m.username.Value(), m.password.Value()). Post("/auth/login") if err == nil { switch resp.StatusCode() { case http.StatusOK: m.BaseModel.client.SetBasicAuth( m.username.Value(), m.password.Value()) m.info = "login success.\n" m.err = nil case http.StatusUnauthorized: m.err = fmt.Errorf("user not exist or password incorrect, %s", string(resp.Body())) default: m.err = fmt.Errorf("unknown server error, %s", string(resp.Body())) } } else { m.err = fmt.Errorf("failed to post login, %w", err) } return postLoginMsg{} } } type postRegisterMsg struct{} func (m *LandingModel) postRegister() tea.Cmd { return func() tea.Msg { resp, err := m.BaseModel.client.R(). SetBody(models.User{ Username: m.username.Value(), Password: m.password.Value(), }). Post("/auth/register") if err == nil { switch resp.StatusCode() { case http.StatusOK: m.info = "register success.\n" m.err = nil case http.StatusBadRequest: m.err = fmt.Errorf("username already exist, %s", string(resp.Body())) default: m.err = fmt.Errorf("unknown server error, %s", string(resp.Body())) } } else { m.err = fmt.Errorf("failed to post register, %s", string(resp.Body())) } return postRegisterMsg{} } } func (m *LandingModel) Init() tea.Cmd { return tea.ClearScreen } func (m *LandingModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c": return m, tea.Quit case "L", "l": if m.op == landingOperationChoose { m.op = landingOperationLoginCred m.focus = focusTargetUsername cmds = append(cmds, m.username.Focus()) return m, tea.Batch(cmds...) } case "R", "r": if m.op == landingOperationChoose { m.op = landingOperationRegistCred m.focus = focusTargetUsername cmds = append(cmds, m.username.Focus()) return m, tea.Batch(cmds...) } case "shift+tab", "up": if m.op == landingOperationLoginCred || m.op == landingOperationRegistCred { switch m.focus { case focusTargetUsername: m.username.Blur() m.focus = focusTargetPassword cmds = append(cmds, m.password.Focus()) case focusTargetPassword: m.password.Blur() m.focus = focusTargetUsername cmds = append(cmds, m.username.Focus()) } } case "tab", "down", "enter": if m.op == landingOperationLoginCred || m.op == landingOperationRegistCred { switch m.focus { case focusTargetUsername: m.username.Blur() m.focus = focusTargetPassword cmds = append(cmds, m.password.Focus()) case focusTargetPassword: if msg.String() == "enter" { switch m.op { case landingOperationLoginCred: cmds = append(cmds, m.postLogin()) case landingOperationRegistCred: cmds = append(cmds, m.postRegister()) } } else { m.password.Blur() m.focus = focusTargetUsername cmds = append(cmds, m.username.Focus()) } } } } case postLoginMsg: if m.err == nil { *m.queue = append(*m.queue, tea.NewProgram(NewLobbyModel(m.BaseModel))) return m, tea.Quit } else { m.reset() } case postRegisterMsg: m.reset() } var cmd tea.Cmd m.username, cmd = m.username.Update(msg) cmds = append(cmds, cmd) m.password, cmd = m.password.Update(msg) cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } func (m *LandingModel) View() string { var b strings.Builder switch m.op { case landingOperationChoose: fmt.Fprintf(&b, "Choose Operation\n(L)ogin / (R)egister\n") case landingOperationLoginCred: fmt.Fprintf(&b, "Login: \n") b.WriteString(m.username.View() + "\n") b.WriteString(m.password.View() + "\n") case landingOperationRegistCred: fmt.Fprintf(&b, "Register: \n") b.WriteString(m.username.View() + "\n") b.WriteString(m.password.View() + "\n") } if m.info != "" { b.WriteString(m.info + "\n") } if m.err != nil { b.WriteString("----------\n") b.WriteString(m.err.Error() + "\n") } return b.String() }