diff --git a/tui/auth.go b/tui/auth.go index 15eb534..b649383 100644 --- a/tui/auth.go +++ b/tui/auth.go @@ -1,8 +1,44 @@ package main -import huh "github.com/charmbracelet/huh" +import ( + huh "github.com/charmbracelet/huh" +) type Auth struct { - form *huh.Form - jwt *string + loginForm *huh.Form + registerForm *huh.Form + jwt *string +} + +func getLoginForm() *huh.Form { + return huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Email"). + Key("email"), + huh.NewInput(). + Title("Password"). + Key("password"). + Password(true), + )).WithWidth(40) +} + +func getRegisterForm() *huh.Form { + return huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Email"). + Key("email"), + huh.NewInput(). + Title("Username"). + Key("username"), + huh.NewInput(). + Title("Password"). + Key("password"). + Password(true), + huh.NewInput(). + Title("Repeat Password"). + Key("password_repeat"). + Password(true), + )).WithWidth(40) } diff --git a/tui/http.go b/tui/http.go index 6e75000..0833da6 100644 --- a/tui/http.go +++ b/tui/http.go @@ -1,9 +1,11 @@ package main import ( + "bytes" "encoding/json" "fmt" "io" + "log" "net/http" tea "github.com/charmbracelet/bubbletea" @@ -17,17 +19,91 @@ type missingJwtMsg struct{} func (e errMsg) Error() string { return e.error.Error() } type getEntriesSuccessMsg []Entry -type getEntriesErrMsg error +type httpErrorMsg error -const serverUrl = "localhost:3000" +const serverUrl = "http://localhost:1597" type loginSuccessMsg struct{ string } +type registerSuccessMsg struct{ string } + +func getData(req *http.Request) ([]byte, error) { + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Print("err", err) + return nil, httpErrorMsg(err) + } + defer resp.Body.Close() // nolint: errcheck + + data, err := io.ReadAll(resp.Body) + log.Print("body", resp.Body) + if err != nil { + return nil, httpErrorMsg(err) + } + return data, nil + +} func login(username string, password string) tea.Cmd { return func() tea.Msg { - _ = username - _ = password - return loginSuccessMsg{"dawdaw"} + url := fmt.Sprintf("%s/login", serverUrl) + body := struct { + Name string `json:"email"` + Password string `json:"password"` + }{ + Name: username, Password: password, + } + out, err := json.Marshal(body) + if err != nil { + return err + } + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(out)) + req.Header.Add("Content-type", "application/json") + data, err := getData(req) + if err != nil { + return err + } + var loginResp AuthRes + + err = json.Unmarshal(data, &loginResp) + if err != nil { + return httpErrorMsg(err) + } + + return loginSuccessMsg{loginResp.Token} + } +} + +type AuthRes struct { + Token string `json:"token"` +} + +func register(username string, password string, email string) tea.Cmd { + return func() tea.Msg { + + url := fmt.Sprintf("%s/register", serverUrl) + body := struct { + Name string `json:"name"` + Password string `json:"password"` + Email string `json:"email"` + }{ + Name: username, Password: password, Email: email, + } + out, err := json.Marshal(body) + if err != nil { + log.Fatal(err) + } + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(out)) + req.Header.Add("Content-type", "application/json") + data, err := getData(req) + if err != nil { + return err + } + var registerResp AuthRes + err = json.Unmarshal(data, ®isterResp) + if err != nil { + return httpErrorMsg(err) + } + return registerSuccessMsg{registerResp.Token} } } @@ -42,25 +118,25 @@ func getEntries(jwt *string) tea.Cmd { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", *jwt)) if err != nil { - return getEntriesErrMsg(err) + return httpErrorMsg(err) } resp, err := http.DefaultClient.Do(req) if err != nil { - return getEntriesErrMsg(err) + return httpErrorMsg(err) } defer resp.Body.Close() // nolint: errcheck data, err := io.ReadAll(resp.Body) if err != nil { - return getEntriesErrMsg(err) + return httpErrorMsg(err) } var entries []Entry err = json.Unmarshal(data, &entries) if err != nil { - return getEntriesErrMsg(err) + return httpErrorMsg(err) } return getEntriesSuccessMsg(entries) diff --git a/tui/main.go b/tui/main.go index b690ced..dc11dfe 100644 --- a/tui/main.go +++ b/tui/main.go @@ -39,31 +39,12 @@ type Model struct { } func New() *Model { - loginForm := huh.NewForm( - huh.NewGroup( - huh.NewInput(). - Title("Username"). - Key("username"), - // Validating fields is easy. The form will mark erroneous fields - // and display error messages accordingly. - // TODO: - // Validate(func(str string) error { - // if str == "octopus773" { - // return errors.New("Not you") - // } - // return nil - // }))) - huh.NewInput(). - Title("Password"). - Key("password"). - Password(true), - )) ti := textinput.New() ti.Placeholder = "Pikachu" ti.Focus() ti.CharLimit = 156 ti.Width = 56 - return &Model{textInput: ti, auth: Auth{form: loginForm, jwt: new(string)}, page: "LOGIN"} + return &Model{textInput: ti, auth: Auth{loginForm: getLoginForm(), registerForm: getRegisterForm(), jwt: new(string)}, page: LOGIN} } func (m Model) getEverything() tea.Cmd { @@ -85,7 +66,7 @@ func (m *Model) initList(width int, height int) { } func (m Model) Init() tea.Cmd { - return tea.Batch(checkServer, m.auth.form.Init()) + return tea.Batch(checkServer, m.auth.loginForm.Init(), m.auth.registerForm.Init()) } @@ -194,37 +175,70 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case loginSuccessMsg: *m.auth.jwt = msg.string m.page = FEEDS + return m, nil + + case registerSuccessMsg: + *m.auth.jwt = msg.string + m.page = FEEDS + return m, nil case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC, tea.KeyEsc: return m, tea.Quit + case tea.KeyCtrlT: + if m.page == LOGIN { + m.page = REGISTER + } else if m.page == REGISTER { + m.page = LOGIN + } + } switch { - case key.Matches(msg, m.textInput.KeyMap.DeleteCharacterBackward): + case key.Matches(msg, m.textInput.KeyMap.DeleteCharacterBackward): //TODO: add only when query words := strings.Split(m.textInput.Value(), " ") if len(words) > 0 && (strings.HasPrefix(words[len(words)-1], "tag:") || strings.HasPrefix(words[len(words)-1], "feed:")) { m.deleteWordBackward() } } - // if writing } var cmds []tea.Cmd // Process the form - form, cmd := m.auth.form.Update(msg) - if f, ok := form.(*huh.Form); ok { - m.auth.form = f - cmds = append(cmds, cmd) + // LOGIN + if m.page == LOGIN { + form, cmd := m.auth.loginForm.Update(msg) + if f, ok := form.(*huh.Form); ok { + m.auth.loginForm = f + cmds = append(cmds, cmd) + } + + if m.auth.loginForm.State == huh.StateCompleted { + username := m.auth.loginForm.GetString("email") + password := m.auth.loginForm.GetString("password") + cmds = append(cmds, login(username, password)) + } } - if m.auth.form.State == huh.StateCompleted { - // Quit when the form is done. - username := m.auth.form.GetString("username") - password := m.auth.form.GetString("password") - cmds = append(cmds, login(username, password)) - } + if m.page == REGISTER { + // Process the form + // LOGIN + registerForm, cmd := m.auth.registerForm.Update(msg) + if f, ok := registerForm.(*huh.Form); ok { + m.auth.registerForm = f + cmds = append(cmds, cmd) + } + + if m.auth.registerForm.State == huh.StateCompleted { + username := m.auth.registerForm.GetString("username") + password := m.auth.registerForm.GetString("password") + email := m.auth.registerForm.GetString("email") + cmds = append(cmds, register(username, password, email)) + } + + } + var cmd tea.Cmd m.list, cmd = m.list.Update(msg) cmds = append(cmds, cmd) m.textInput, cmd = m.textInput.Update(msg) diff --git a/tui/pages.go b/tui/pages.go index ee62991..ea11a64 100644 --- a/tui/pages.go +++ b/tui/pages.go @@ -1,22 +1,33 @@ package main +import ( + "fmt" + + "github.com/charmbracelet/lipgloss" +) + const ( - LOGIN = "LOGIN" - ENTRIES = "ENTRIES" - FEEDS = "FEEDS" - TAGS = "TAGS" + LOGIN = "LOGIN" + REGISTER = "REGISTER" + ENTRIES = "ENTRIES" + FEEDS = "FEEDS" + TAGS = "TAGS" ) type VexPage string func (m Model) LoginView() string { - return m.auth.form.View() + return lipgloss.JoinHorizontal( + lipgloss.Left, + m.auth.loginForm.View(), + m.auth.registerForm.View(), + ) } func (m Model) EntriesView() string { return "" } func (m Model) FeedsView() string { - return "feeds" + return fmt.Sprintf("%s ", *m.auth.jwt) } func (m Model) TagsView() string { return "" @@ -25,6 +36,8 @@ func (m Model) View() string { switch m.page { case LOGIN: return m.LoginView() + case REGISTER: + return m.LoginView() case ENTRIES: return m.EntriesView() case FEEDS: