1package tui
2
3import (
4 "fmt"
5 "io"
6
7 "github.com/andrinoff/email-cli/fetcher"
8 "github.com/charmbracelet/bubbles/list"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/lipgloss"
11)
12
13var (
14 paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
15 inboxHelpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
16)
17
18// item now holds its original index from the main email slice.
19type item struct {
20 title, desc string
21 originalIndex int
22}
23
24func (i item) Title() string { return i.title }
25func (i item) Description() string { return i.desc }
26func (i item) FilterValue() string { return i.title + " " + i.desc }
27
28type itemDelegate struct{}
29
30func (d itemDelegate) Height() int { return 1 }
31func (d itemDelegate) Spacing() int { return 0 }
32func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
33func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
34 i, ok := listItem.(item)
35 if !ok {
36 return
37 }
38
39 str := fmt.Sprintf("%d. %s", index+1, i.title)
40
41 fn := itemStyle.Render
42 if index == m.Index() {
43 fn = func(s ...string) string {
44 return selectedItemStyle.Render("> " + s[0])
45 }
46 }
47
48 fmt.Fprint(w, fn(str))
49}
50
51// Inbox is now stateful to handle pagination.
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, // Store the original index here.
65 }
66 }
67
68 l := list.New(items, itemDelegate{}, 20, 14)
69 l.Title = "Inbox"
70 l.SetShowStatusBar(true)
71 l.SetFilteringEnabled(true)
72 l.Styles.Title = lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true)
73 l.Styles.PaginationStyle = paginationStyle
74 l.Styles.HelpStyle = inboxHelpStyle
75 l.SetStatusBarItemName("email", "emails")
76
77 return &Inbox{
78 list: l,
79 isFetching: false,
80 emailsCount: len(emails),
81 }
82}
83
84func (m *Inbox) Init() tea.Cmd {
85 return nil
86}
87
88func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
89 var cmds []tea.Cmd
90
91 switch msg := msg.(type) {
92 case tea.KeyMsg:
93 // When the user presses enter, we look at the selected item and send
94 // a message with its *original* index.
95 if msg.String() == "enter" {
96 selectedItem, ok := m.list.SelectedItem().(item)
97 if ok {
98 return m, func() tea.Msg {
99 // Use the stored original index, which is correct even when filtered.
100 return ViewEmailMsg{Index: selectedItem.originalIndex}
101 }
102 }
103 }
104 case tea.WindowSizeMsg:
105 m.list.SetWidth(msg.Width)
106 return m, nil
107
108 case FetchingMoreEmailsMsg:
109 m.isFetching = true
110 m.list.Title = "Fetching more emails..."
111 return m, nil
112
113 case EmailsAppendedMsg:
114 m.isFetching = false
115 m.list.Title = "Inbox"
116 newItems := make([]list.Item, len(msg.Emails))
117 for i, email := range msg.Emails {
118 newItems[i] = item{
119 title: email.Subject,
120 desc: email.From,
121 originalIndex: m.emailsCount + i, // The original index continues to grow.
122 }
123 }
124 currentItems := m.list.Items()
125 allItems := append(currentItems, newItems...)
126 cmd := m.list.SetItems(allItems)
127 m.emailsCount += len(msg.Emails)
128 cmds = append(cmds, cmd)
129 return m, tea.Batch(cmds...)
130 }
131
132 // Infinite scroll logic
133 if !m.isFetching && len(m.list.Items()) > 0 && m.list.Index() >= len(m.list.Items())-5 {
134 cmds = append(cmds, func() tea.Msg {
135 return FetchMoreEmailsMsg{Offset: uint32(m.emailsCount)}
136 })
137 }
138
139 // New logic to fetch more emails when filtering is active
140 if m.list.FilterState() == list.Filtering && !m.isFetching {
141 m.isFetching = true
142 m.list.Title = "Fetching more emails..."
143 cmds = append(cmds, func() tea.Msg {
144 return FetchMoreEmailsMsg{Offset: uint32(m.emailsCount)}
145 })
146 }
147
148 var cmd tea.Cmd
149 var newModel list.Model
150 newModel, cmd = m.list.Update(msg)
151 m.list = newModel
152 cmds = append(cmds, cmd)
153 return m, tea.Batch(cmds...)
154}
155
156func (m *Inbox) View() string {
157 return "\n" + m.list.View()
158}