inbox.go

  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}