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			key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl + c", "quit")),
 82		}
 83	}
 84
 85	l.KeyMap.Quit.SetEnabled(false)
 86
 87	return &Inbox{
 88		list:        l,
 89		isFetching:  false,
 90		emailsCount: len(emails),
 91	}
 92}
 93
 94func (m *Inbox) Init() tea.Cmd {
 95	return nil
 96}
 97
 98func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 99	var cmds []tea.Cmd
100
101	switch msg := msg.(type) {
102	case tea.KeyMsg:
103		if m.list.FilterState() == list.Filtering {
104			break
105		}
106		switch keypress := msg.String(); keypress {
107		case "d":
108			selectedItem, ok := m.list.SelectedItem().(item)
109			if ok {
110				return m, func() tea.Msg {
111					return DeleteEmailMsg{UID: selectedItem.uid}
112				}
113			}
114		case "a":
115			selectedItem, ok := m.list.SelectedItem().(item)
116			if ok {
117				return m, func() tea.Msg {
118					return ArchiveEmailMsg{UID: selectedItem.uid}
119				}
120			}
121		case "enter":
122			selectedItem, ok := m.list.SelectedItem().(item)
123			if ok {
124				return m, func() tea.Msg {
125					return ViewEmailMsg{Index: selectedItem.originalIndex}
126				}
127			}
128		}
129	case tea.WindowSizeMsg:
130		m.list.SetWidth(msg.Width)
131		return m, nil
132
133	case FetchingMoreEmailsMsg:
134		m.isFetching = true
135		m.list.Title = "Fetching more emails..."
136		return m, nil
137
138	case EmailsAppendedMsg:
139		m.isFetching = false
140		m.list.Title = "Inbox"
141		newItems := make([]list.Item, len(msg.Emails))
142		for i, email := range msg.Emails {
143			newItems[i] = item{
144				title:         email.Subject,
145				desc:          email.From,
146				originalIndex: m.emailsCount + i,
147				uid:           email.UID,
148			}
149		}
150		currentItems := m.list.Items()
151		allItems := append(currentItems, newItems...)
152		cmd := m.list.SetItems(allItems)
153		m.emailsCount += len(msg.Emails)
154		cmds = append(cmds, cmd)
155		return m, tea.Batch(cmds...)
156	}
157
158	if !m.isFetching && len(m.list.Items()) > 0 && m.list.Index() >= len(m.list.Items())-5 {
159		cmds = append(cmds, func() tea.Msg {
160			return FetchMoreEmailsMsg{Offset: uint32(m.emailsCount)}
161		})
162	}
163
164	var cmd tea.Cmd
165	m.list, cmd = m.list.Update(msg)
166	cmds = append(cmds, cmd)
167	return m, tea.Batch(cmds...)
168}
169
170func (m *Inbox) View() string {
171	return "\n" + m.list.View()
172}