1package tui
2
3import (
4 "fmt"
5 "io"
6
7 "github.com/charmbracelet/bubbles/key"
8 "github.com/charmbracelet/bubbles/list"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/lipgloss"
11 "github.com/floatpane/matcha/fetcher"
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}