inbox.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"io"
  6
  7	"github.com/andrinoff/email-cli/fetcher"
  8	"github.com/charmbracelet/bubbles/key"
  9	"github.com/charmbracelet/bubbles/list"
 10	tea "github.com/charmbracelet/bubbletea"
 11	"github.com/charmbracelet/lipgloss"
 12)
 13
 14var (
 15	paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
 16	inboxHelpStyle  = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
 17)
 18
 19type item struct {
 20	title, desc   string
 21	originalIndex int
 22	uid           uint32 // Added UID to item
 23}
 24
 25func (i item) Title() string       { return i.title }
 26func (i item) Description() string { return i.desc }
 27func (i item) FilterValue() string { return i.title + " " + i.desc }
 28
 29type itemDelegate struct{}
 30
 31func (d itemDelegate) Height() int                             { return 1 }
 32func (d itemDelegate) Spacing() int                            { return 0 }
 33func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
 34func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
 35	i, ok := listItem.(item)
 36	if !ok {
 37		return
 38	}
 39
 40	str := fmt.Sprintf("%d. %s", index+1, i.title)
 41
 42	fn := itemStyle.Render
 43	if index == m.Index() {
 44		fn = func(s ...string) string {
 45			return selectedItemStyle.Render("> " + s[0])
 46		}
 47	}
 48
 49	fmt.Fprint(w, fn(str))
 50}
 51
 52type Inbox struct {
 53	list        list.Model
 54	isFetching  bool
 55	emailsCount int
 56}
 57
 58func NewInbox(emails []fetcher.Email) *Inbox {
 59	items := make([]list.Item, len(emails))
 60	for i, email := range emails {
 61		items[i] = item{
 62			title:         email.Subject,
 63			desc:          email.From,
 64			originalIndex: i,
 65			uid:           email.UID, // Store UID
 66		}
 67	}
 68
 69	l := list.New(items, itemDelegate{}, 20, 14)
 70	l.Title = "Inbox"
 71	l.SetShowStatusBar(true)
 72	l.SetFilteringEnabled(true)
 73	l.Styles.Title = lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true)
 74	l.Styles.PaginationStyle = paginationStyle
 75	l.Styles.HelpStyle = inboxHelpStyle
 76	l.SetStatusBarItemName("email", "emails")
 77	l.AdditionalShortHelpKeys = func() []key.Binding {
 78		return []key.Binding{
 79			key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "delete")),
 80			key.NewBinding(key.WithKeys("a"), key.WithHelp("a", "archive")),
 81		}
 82	}
 83
 84	return &Inbox{
 85		list:        l,
 86		isFetching:  false,
 87		emailsCount: len(emails),
 88	}
 89}
 90
 91func (m *Inbox) Init() tea.Cmd {
 92	return nil
 93}
 94
 95func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 96	var cmds []tea.Cmd
 97
 98	switch msg := msg.(type) {
 99	case tea.KeyMsg:
100		if m.list.FilterState() == list.Filtering {
101			break
102		}
103		switch keypress := msg.String(); keypress {
104		case "d":
105			selectedItem, ok := m.list.SelectedItem().(item)
106			if ok {
107				return m, func() tea.Msg {
108					return DeleteEmailMsg{UID: selectedItem.uid}
109				}
110			}
111		case "a":
112			selectedItem, ok := m.list.SelectedItem().(item)
113			if ok {
114				return m, func() tea.Msg {
115					return ArchiveEmailMsg{UID: selectedItem.uid}
116				}
117			}
118		case "enter":
119			selectedItem, ok := m.list.SelectedItem().(item)
120			if ok {
121				return m, func() tea.Msg {
122					return ViewEmailMsg{Index: selectedItem.originalIndex}
123				}
124			}
125		}
126	case tea.WindowSizeMsg:
127		m.list.SetWidth(msg.Width)
128		return m, nil
129
130	case FetchingMoreEmailsMsg:
131		m.isFetching = true
132		m.list.Title = "Fetching more emails..."
133		return m, nil
134
135	case EmailsAppendedMsg:
136		m.isFetching = false
137		m.list.Title = "Inbox"
138		newItems := make([]list.Item, len(msg.Emails))
139		for i, email := range msg.Emails {
140			newItems[i] = item{
141				title:         email.Subject,
142				desc:          email.From,
143				originalIndex: m.emailsCount + i,
144				uid:           email.UID,
145			}
146		}
147		currentItems := m.list.Items()
148		allItems := append(currentItems, newItems...)
149		cmd := m.list.SetItems(allItems)
150		m.emailsCount += len(msg.Emails)
151		cmds = append(cmds, cmd)
152		return m, tea.Batch(cmds...)
153	}
154
155	if !m.isFetching && len(m.list.Items()) > 0 && m.list.Index() >= len(m.list.Items())-5 {
156		cmds = append(cmds, func() tea.Msg {
157			return FetchMoreEmailsMsg{Offset: uint32(m.emailsCount)}
158		})
159	}
160
161	var cmd tea.Cmd
162	m.list, cmd = m.list.Update(msg)
163	cmds = append(cmds, cmd)
164	return m, tea.Batch(cmds...)
165}
166
167func (m *Inbox) View() string {
168	return "\n" + m.list.View()
169}