1package tui
2
3import (
4 "fmt"
5
6 "github.com/andrinoff/email-cli/config"
7 "github.com/andrinoff/email-cli/fetcher"
8 "github.com/charmbracelet/bubbles/list"
9 "github.com/charmbracelet/bubbles/spinner"
10 tea "github.com/charmbracelet/bubbletea"
11 "github.com/charmbracelet/lipgloss"
12)
13
14// inboxItem wraps fetcher.Email to satisfy the list.Item interface.
15type inboxItem struct {
16 fetcher.Email
17}
18
19func (i inboxItem) Title() string { return i.Subject }
20func (i inboxItem) Description() string { return fmt.Sprintf("From: %s", i.From) }
21func (i inboxItem) FilterValue() string { return i.Subject }
22
23type Inbox struct {
24 list list.Model
25 spinner spinner.Model
26 loading bool
27 err error
28}
29
30func NewInbox() *Inbox {
31 s := spinner.New()
32 s.Spinner = spinner.Dot
33 s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
34
35 // Create an empty list for now. It will be populated later.
36 l := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0)
37 l.Title = "Inbox"
38
39 return &Inbox{
40 spinner: s,
41 list: l,
42 loading: true,
43 }
44}
45
46func (m *Inbox) Init() tea.Cmd {
47 return tea.Batch(m.spinner.Tick, fetchEmails)
48}
49
50func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
51 var cmd tea.Cmd
52 switch msg := msg.(type) {
53 case spinner.TickMsg:
54 if m.loading {
55 m.spinner, cmd = m.spinner.Update(msg)
56 }
57 return m, cmd
58
59 case emailsFetchedMsg:
60 m.loading = false
61 items := make([]list.Item, len(msg))
62 for i, email := range msg {
63 items[i] = inboxItem{email}
64 }
65 m.list.SetItems(items)
66 return m, nil
67
68 case errMsg:
69 m.err = msg.err
70 return m, tea.Quit
71
72 case tea.KeyMsg:
73 if msg.String() == "enter" && !m.loading {
74 selected := m.list.SelectedItem().(inboxItem)
75 return m, func() tea.Msg {
76 return ViewEmailMsg{Email: selected.Email}
77 }
78 }
79
80 case tea.WindowSizeMsg:
81 // When the window size changes, update the list's dimensions.
82 m.list.SetSize(msg.Width, msg.Height)
83 }
84
85 if !m.loading {
86 m.list, cmd = m.list.Update(msg)
87 }
88 return m, cmd
89}
90
91func (m *Inbox) View() string {
92 if m.err != nil {
93 return fmt.Sprintf("Error: %v", m.err)
94 }
95 if m.loading {
96 return fmt.Sprintf("\n\n %s Fetching emails...\n\n", m.spinner.View())
97 }
98 return DocStyle.Render(m.list.View())
99}
100
101// --- Bubble Tea Commands and Messages ---
102
103type emailsFetchedMsg []fetcher.Email
104type errMsg struct{ err error }
105
106func fetchEmails() tea.Msg {
107 cfg, err := config.LoadConfig()
108 if err != nil {
109 return errMsg{err}
110 }
111 emails, err := fetcher.FetchEmails(cfg)
112 if err != nil {
113 return errMsg{err}
114 }
115 return emailsFetchedMsg(emails)
116}