@@ -159,6 +159,8 @@ func (m *Inbox) updateList() {
showAccountLabel = false
}
+ m.emailsCount = len(displayEmails)
+
items := make([]list.Item, len(displayEmails))
for i, email := range displayEmails {
accountEmail := ""
@@ -211,21 +213,20 @@ func (m *Inbox) updateList() {
}
m.list = l
- m.emailsCount = len(displayEmails)
}
func (m *Inbox) getTitle() string {
var title string
if m.currentAccountID == "" {
- title = m.getBaseTitle() + " - All Accounts"
+ title = m.getBaseTitleWithCount() + " - All Accounts"
} else {
- title = m.getBaseTitle()
+ title = m.getBaseTitleWithCount()
for _, acc := range m.accounts {
if acc.ID == m.currentAccountID {
if acc.Name != "" {
- title = fmt.Sprintf("%s - %s", m.getBaseTitle(), acc.Name)
+ title = fmt.Sprintf("%s - %s", m.getBaseTitleWithCount(), acc.Name)
} else {
- title = fmt.Sprintf("%s - %s", m.getBaseTitle(), acc.Email)
+ title = fmt.Sprintf("%s - %s", m.getBaseTitleWithCount(), acc.Email)
}
break
}
@@ -234,6 +235,9 @@ func (m *Inbox) getTitle() string {
if m.isRefreshing {
title += " (refreshing...)"
}
+ if m.isFetching {
+ title += " (loading more...)"
+ }
return title
}
@@ -246,6 +250,10 @@ func (m *Inbox) getBaseTitle() string {
}
}
+func (m *Inbox) getBaseTitleWithCount() string {
+ return fmt.Sprintf("%s (%d)", m.getBaseTitle(), m.emailsCount)
+}
+
func (m *Inbox) Init() tea.Cmd {
return nil
}
@@ -312,7 +320,7 @@ func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case FetchingMoreEmailsMsg:
m.isFetching = true
- m.list.Title = "Fetching more emails..."
+ m.list.Title = m.getTitle()
return m, nil
case EmailsAppendedMsg:
@@ -376,25 +384,56 @@ func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
- if !m.isFetching && len(m.list.Items()) > 0 && m.list.Index() >= len(m.list.Items())-5 {
- accountID := m.currentAccountID
- offset := uint32(m.emailsCount)
- if accountID == "" && len(m.accounts) > 0 {
- // For "ALL" view, we'd need to fetch from all accounts
- // For simplicity, don't auto-fetch more in ALL view
- } else if accountID != "" {
- cmds = append(cmds, func() tea.Msg {
- return FetchMoreEmailsMsg{Offset: offset, AccountID: accountID, Mailbox: m.mailbox}
- })
- }
- }
-
var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
cmds = append(cmds, cmd)
+
+ if m.shouldFetchMore() {
+ cmds = append(cmds, m.fetchMoreCmds()...)
+ }
return m, tea.Batch(cmds...)
}
+func (m *Inbox) shouldFetchMore() bool {
+ if m.isFetching {
+ return false
+ }
+ if len(m.list.Items()) == 0 {
+ return false
+ }
+ if m.list.FilterState() == list.Filtering {
+ return false
+ }
+ return m.list.Index() >= len(m.list.Items())-1
+}
+
+func (m *Inbox) fetchMoreCmds() []tea.Cmd {
+ var cmds []tea.Cmd
+ if m.currentAccountID == "" {
+ if len(m.accounts) == 0 {
+ return nil
+ }
+ for _, acc := range m.accounts {
+ accountID := acc.ID
+ offset := uint32(len(m.emailsByAccount[accountID]))
+ cmds = append(cmds, func(id string, off uint32) tea.Cmd {
+ return func() tea.Msg {
+ return FetchMoreEmailsMsg{Offset: off, AccountID: id, Mailbox: m.mailbox}
+ }
+ }(accountID, offset))
+ }
+ return cmds
+ }
+
+ offset := uint32(len(m.emailsByAccount[m.currentAccountID]))
+ cmds = append(cmds, func(id string, off uint32) tea.Cmd {
+ return func() tea.Msg {
+ return FetchMoreEmailsMsg{Offset: off, AccountID: id, Mailbox: m.mailbox}
+ }
+ }(m.currentAccountID, offset))
+ return cmds
+}
+
func (m *Inbox) View() string {
var b strings.Builder
@@ -1,6 +1,7 @@
package tui
import (
+ "strings"
"testing"
"time"
@@ -9,6 +10,28 @@ import (
"github.com/floatpane/matcha/fetcher"
)
+func collectMsgs(cmd tea.Cmd) []tea.Msg {
+ if cmd == nil {
+ return nil
+ }
+ msg := cmd()
+ switch batch := msg.(type) {
+ case tea.BatchMsg:
+ var msgs []tea.Msg
+ for _, m := range batch {
+ if m != nil {
+ msgs = append(msgs, m)
+ }
+ }
+ return msgs
+ default:
+ if msg != nil {
+ return []tea.Msg{msg}
+ }
+ }
+ return nil
+}
+
// TestInboxUpdate verifies the state transitions in the inbox view.
func TestInboxUpdate(t *testing.T) {
// Create sample accounts
@@ -248,3 +271,76 @@ func TestInboxGetEmailAtIndex(t *testing.T) {
t.Error("Expected nil for negative index, got non-nil")
}
}
+
+func TestInboxTitleShowsCount(t *testing.T) {
+ accounts := []config.Account{
+ {ID: "account-1", Email: "test@example.com"},
+ }
+
+ emails := []fetcher.Email{
+ {UID: 1, From: "sender@example.com", Subject: "Email 1", AccountID: "account-1", Date: time.Now()},
+ {UID: 2, From: "sender@example.com", Subject: "Email 2", AccountID: "account-1", Date: time.Now().Add(-time.Minute)},
+ {UID: 3, From: "sender@example.com", Subject: "Email 3", AccountID: "account-1", Date: time.Now().Add(-2 * time.Minute)},
+ }
+
+ inbox := NewInbox(emails, accounts)
+
+ if !strings.Contains(inbox.list.Title, "Inbox (3)") {
+ t.Fatalf("expected inbox title to contain count, got %q", inbox.list.Title)
+ }
+}
+
+func TestSentInboxTitleShowsCount(t *testing.T) {
+ accounts := []config.Account{
+ {ID: "account-1", Email: "test@example.com"},
+ }
+
+ emails := []fetcher.Email{
+ {UID: 1, From: "sender@example.com", Subject: "Email 1", AccountID: "account-1", Date: time.Now()},
+ {UID: 2, From: "sender@example.com", Subject: "Email 2", AccountID: "account-1", Date: time.Now().Add(-time.Minute)},
+ }
+
+ inbox := NewSentInbox(emails, accounts)
+
+ if !strings.Contains(inbox.list.Title, "Sent (2)") {
+ t.Fatalf("expected sent title to contain count, got %q", inbox.list.Title)
+ }
+}
+
+func TestFetchMoreTriggeredAtListEnd(t *testing.T) {
+ accounts := []config.Account{
+ {ID: "account-1", Email: "test@example.com"},
+ }
+
+ emails := []fetcher.Email{
+ {UID: 1, From: "a@example.com", Subject: "Email 1", AccountID: "account-1", Date: time.Now()},
+ {UID: 2, From: "b@example.com", Subject: "Email 2", AccountID: "account-1", Date: time.Now().Add(-time.Minute)},
+ }
+
+ inbox := NewInbox(emails, accounts)
+
+ _, cmd := inbox.Update(tea.KeyMsg{Type: tea.KeyDown})
+ msgs := collectMsgs(cmd)
+
+ var fetchMsg FetchMoreEmailsMsg
+ for _, m := range msgs {
+ if msg, ok := m.(FetchMoreEmailsMsg); ok {
+ fetchMsg = msg
+ break
+ }
+ }
+
+ if fetchMsg.AccountID == "" {
+ t.Fatal("expected a FetchMoreEmailsMsg when reaching end of the list")
+ }
+
+ if fetchMsg.Offset != uint32(len(emails)) {
+ t.Fatalf("expected offset %d, got %d", len(emails), fetchMsg.Offset)
+ }
+ if fetchMsg.AccountID != "account-1" {
+ t.Fatalf("expected account ID 'account-1', got %q", fetchMsg.AccountID)
+ }
+ if fetchMsg.Mailbox != MailboxInbox {
+ t.Fatalf("expected MailboxInbox, got %s", fetchMsg.Mailbox)
+ }
+}