diff --git a/internal/ui/messages/messages.go b/internal/ui/messages/messages.go index c099f34..79aedfb 100644 --- a/internal/ui/messages/messages.go +++ b/internal/ui/messages/messages.go @@ -7,6 +7,7 @@ type Page int const ( PageLogin Page = iota PageMenu + PageListDomains ) type SwitchPageMsg struct { @@ -17,4 +18,7 @@ type SessionReadyMsg struct { Client *porkbun.Client } +type ListDomainsMsg struct { +} + type ErrorMsg error diff --git a/internal/ui/model.go b/internal/ui/model.go index 73bb767..e8eb461 100644 --- a/internal/ui/model.go +++ b/internal/ui/model.go @@ -6,6 +6,7 @@ import ( "git.juancwu.dev/juancwu/porkbacon/internal/config" "git.juancwu.dev/juancwu/porkbacon/internal/ui/messages" + "git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/listdomains" "git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/login" "git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/menu" tea "github.com/charmbracelet/bubbletea" @@ -15,6 +16,7 @@ type MainModel struct { currentPage messages.Page login *login.Model menu *menu.Model + listDomains *listdomains.Model isMenuInit bool width int height int @@ -42,6 +44,10 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case tea.KeyMsg: + if msg.String() == "ctrl+c" { + return m, tea.Quit + } case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height @@ -50,13 +56,16 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case messages.SessionReadyMsg: m.menu = menu.New(msg.Client) + m.listDomains = listdomains.New(msg.Client) if m.width > 0 && m.height > 0 { newMenu, _ := m.menu.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height}) m.menu = newMenu.(*menu.Model) } m.currentPage = messages.PageMenu m.isMenuInit = true - return m, m.menu.Init() + return m, tea.Batch(m.menu.Init(), m.listDomains.Init()) + case messages.ListDomainsMsg: + m.currentPage = messages.PageListDomains } switch m.currentPage { @@ -68,6 +77,10 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var newMenu tea.Model newMenu, cmd = m.menu.Update(msg) m.menu = newMenu.(*menu.Model) + case messages.PageListDomains: + var newModel tea.Model + newModel, cmd = m.listDomains.Update(msg) + m.listDomains = newModel.(*listdomains.Model) } cmds = append(cmds, cmd) @@ -80,6 +93,8 @@ func (m MainModel) View() string { return m.login.View() case messages.PageMenu: return m.menu.View() + case messages.PageListDomains: + return m.listDomains.View() default: return "Unknown Page" } diff --git a/internal/ui/pages/listdomains/model.go b/internal/ui/pages/listdomains/model.go new file mode 100644 index 0000000..a4e2784 --- /dev/null +++ b/internal/ui/pages/listdomains/model.go @@ -0,0 +1,140 @@ +package listdomains + +import ( + "fmt" + "strings" + + "git.juancwu.dev/juancwu/porkbacon/internal/porkbun" + "git.juancwu.dev/juancwu/porkbacon/internal/ui/messages" + "github.com/charmbracelet/bubbles/paginator" + "github.com/charmbracelet/bubbles/spinner" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +type Model struct { + loading bool + client *porkbun.Client + domains []string + paginator paginator.Model + spinner spinner.Model + stderr string +} + +func New(client *porkbun.Client) *Model { + p := paginator.New() + p.Type = paginator.Dots + p.PerPage = 1 + p.ActiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}).Render("•") + p.InactiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "250", Dark: "238"}).Render("•") + + s := spinner.New() + s.Spinner = spinner.Dot + s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) + + return &Model{ + loading: false, + client: client, + domains: nil, + paginator: p, + spinner: s, + } +} + +func (m *Model) Init() tea.Cmd { + return m.spinner.Tick +} + +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + + switch msg := msg.(type) { + case tea.KeyMsg: + if !m.loading && msg.String() == "esc" { + return m, func() tea.Msg { + m.loading = false + m.domains = nil + m.client = nil + return messages.SwitchPageMsg{Page: messages.PageMenu} + } + } + + if len(m.domains) > 0 { + m.paginator, cmd = m.paginator.Update(msg) + return m, cmd + } + + case messages.ListDomainsMsg: + m.loading = true + m.domains = nil + return m, tea.Batch(listDomains(m.client), m.spinner.Tick) + + case *porkbun.DomainListAllResponse: + m.loading = false + for _, domain := range msg.Domains { + m.domains = append(m.domains, renderDomain(&domain)) + } + m.paginator.SetTotalPages(len(m.domains)) + m.paginator.Page = 0 + + case messages.ErrorMsg: + m.stderr = fmt.Sprintf("Error: %v", msg) + return m, nil + } + + if m.loading { + m.spinner, cmd = m.spinner.Update(msg) + return m, tea.Batch(cmd, m.spinner.Tick) + } + + return m, nil +} + +func (m *Model) View() string { + if m.stderr != "" { + return fmt.Sprintf("%s\n\n(Press ctrl+c to quit)", m.stderr) + } + + if m.loading { + return fmt.Sprintf("\n\n %s Loading... press ctl+c to quit\n\n", m.spinner.View()) + } + + if len(m.domains) > 0 { + return fmt.Sprintf("%s\n\n%s\n\n(Press Esc to go back, arrows to navigate)", m.domains[m.paginator.Page], m.paginator.View()) + } + + return "Uhh.. This is awkward... Press Esc to go back." +} + +func listDomains(client *porkbun.Client) tea.Cmd { + return func() tea.Msg { + resp, err := client.DomainListAll(0, true) + if err != nil { + return messages.ErrorMsg(err) + } + + return resp + } +} + +func renderDomain(item *porkbun.Domain) string { + var b strings.Builder + b.WriteString("Domain: " + item.Domain + "\n") + b.WriteString("Status: " + item.Status + "\n") + b.WriteString("Create Date: " + item.CreateDate + "\n") + b.WriteString("Expire Date: " + item.ExpireDate + "\n") + b.WriteString("Security Lock: " + item.SecurityLock + "\n") + b.WriteString("Whois Privacy: " + item.WhoIsPrivacy + "\n") + b.WriteString(fmt.Sprintln("Auto Renew:", item.AutoRenew)) + b.WriteString(fmt.Sprintln("Not Local:", item.NotLocal)) + if len(item.Labels) > 0 { + b.WriteString("Labels:\n") + } + for i, label := range item.Labels { + b.WriteString("=> " + label.Title) + if i < len(item.Labels)-1 { + b.WriteString("\n") + } + } + return b.String() +} diff --git a/internal/ui/pages/menu/model.go b/internal/ui/pages/menu/model.go index 89272a1..c1b2112 100644 --- a/internal/ui/pages/menu/model.go +++ b/internal/ui/pages/menu/model.go @@ -17,11 +17,6 @@ type pingResultMsg struct { IP string } -type domainListMsg struct { - Status string - Domains []string -} - type dnsRecordListMsg struct { Status string Records []string @@ -58,7 +53,6 @@ type Model struct { textInput textinput.Model loading bool state uint8 - domains []string records []string client *porkbun.Client err error @@ -159,16 +153,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } - if len(m.domains) > 0 { - var cmd tea.Cmd - m.paginator, cmd = m.paginator.Update(msg) - if msg.String() == "esc" { - m.domains = nil - return m, nil - } - return m, cmd - } - if len(m.records) > 0 { var cmd tea.Cmd m.paginator, cmd = m.paginator.Update(msg) @@ -191,15 +175,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.paginator.PerPage = 1 m.paginator.SetTotalPages(len(msg.Records)) m.paginator.Page = 0 - m.domains = msg.Records - return m, nil - - case domainListMsg: - m.loading = false - m.paginator.PerPage = 1 - m.paginator.SetTotalPages(len(msg.Domains)) - m.paginator.Page = 0 - m.domains = msg.Domains return m, nil case pingResultMsg: @@ -238,10 +213,6 @@ func (m *Model) View() string { ) } - if len(m.domains) > 0 { - return fmt.Sprintf("%s\n\n%s\n\n(Press Esc to go back, arrows to navigate)", m.domains[m.paginator.Page], m.paginator.View()) - } - if len(m.records) > 0 { return fmt.Sprintf("%s\n\n%s\n\n(Press Esc to go back, arrows to navigate)", m.records[m.paginator.Page], m.paginator.View()) } @@ -256,23 +227,9 @@ func (m *Model) handleSelection(i menuItem) (tea.Model, tea.Cmd) { m.textInput.Focus() return m, textinput.Blink case domainListAll: - m.loading = true - cmd := func() tea.Msg { - resp, err := m.client.DomainListAll(0, true) - if err != nil { - return messages.ErrorMsg(err) - } - var domains []string - for _, domain := range resp.Domains { - view := renderDomainItem(&domain) - domains = append(domains, view) - } - return domainListMsg{ - Status: resp.Status, - Domains: domains, - } + return m, func() tea.Msg { + return messages.ListDomainsMsg{} } - return m, tea.Batch(cmd, m.spinner.Tick) case utilPing: m.loading = true cmd := func() tea.Msg {