From 65b01341502f0fd5530d84ed713d7071971178e0 Mon Sep 17 00:00:00 2001 From: drew Date: Thu, 31 Jul 2025 15:51:05 +0400 Subject: [PATCH] fix: load uid and name separately (#45) --- fetcher/fetcher.go | 59 +++++++++++++++++++++++++++++----------------- main.go | 32 +++++++++++++++++++++++++ tui/messages.go | 2 +- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 449e30c19306b38ac16287843252003d3df090fd..ac4db994deb7f59b740c06491693027eef5a649c 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "mime" "strings" "time" @@ -146,13 +147,28 @@ func FetchEmails(cfg *config.Config, limit, offset uint32) ([]Email, error) { done <- c.Fetch(seqset, fetchItems, messages) }() - var emails []Email + // Collect messages from the channel into a slice + var msgs []*imap.Message for msg := range messages { - if msg == nil { + msgs = append(msgs, msg) + } + + // Wait for the fetch to complete and check for errors + if err := <-done; err != nil { + return nil, err + } + + var emails []Email + for _, msg := range msgs { + if msg == nil || msg.Envelope == nil { continue } - fromAddrs := msg.Envelope.From[0].Address() + var fromAddr string + if len(msg.Envelope.From) > 0 { + fromAddr = msg.Envelope.From[0].Address() + } + var toAddrList []string for _, addr := range msg.Envelope.To { toAddrList = append(toAddrList, addr.Address()) @@ -160,17 +176,13 @@ func FetchEmails(cfg *config.Config, limit, offset uint32) ([]Email, error) { emails = append(emails, Email{ UID: msg.Uid, - From: fromAddrs, + From: fromAddr, To: toAddrList, Subject: decodeHeader(msg.Envelope.Subject), Date: msg.Envelope.Date, }) } - if err := <-done; err != nil { - return nil, err - } - // Reverse the order to show newest first for i, j := 0, len(emails)-1; i < j; i, j = i+1, j-1 { emails[i], emails[j] = emails[j], emails[i] @@ -179,8 +191,6 @@ func FetchEmails(cfg *config.Config, limit, offset uint32) ([]Email, error) { return emails, nil } - -// New function to fetch the body for a single email func FetchEmailBody(cfg *config.Config, uid uint32) (string, []Attachment, error) { c, err := connect(cfg) if err != nil { @@ -199,12 +209,23 @@ func FetchEmailBody(cfg *config.Config, uid uint32) (string, []Attachment, error done := make(chan error, 1) fetchItems := []imap.FetchItem{imap.FetchItem("BODY[]")} go func() { - done <- c.Fetch(seqset, fetchItems, messages) + done <- c.UidFetch(seqset, fetchItems, messages) }() - msg := <-messages - if msg == nil { - return "", nil, fmt.Errorf("could not fetch email body") + // Wait for the fetch operation to complete first. + if err := <-done; err != nil { + return "", nil, err + } + + // Now that the fetch is complete, check for a message. + var msg *imap.Message + select { + case msg = <-messages: + if msg == nil { + return "", nil, fmt.Errorf("fetched a nil message") + } + default: + return "", nil, fmt.Errorf("no message found with UID %d", uid) } bodyLiteral := msg.GetBody(&imap.BodySectionName{}) @@ -224,7 +245,8 @@ func FetchEmailBody(cfg *config.Config, uid uint32) (string, []Attachment, error if err == io.EOF { break } else if err != nil { - return "", nil, fmt.Errorf("error getting next part: %v", err) + log.Printf("Error getting next part: %v", err) + break } cdHeader := p.Header.Get("Content-Disposition") @@ -256,14 +278,9 @@ func FetchEmailBody(cfg *config.Config, uid uint32) (string, []Attachment, error } } - if err := <-done; err != nil { - return "", nil, err - } - return body, attachments, nil } - func moveEmail(cfg *config.Config, uid uint32, destMailbox string) error { c, err := connect(cfg) if err != nil { @@ -314,4 +331,4 @@ func ArchiveEmail(cfg *config.Config, uid uint32) error { archiveMailbox = "Archive" } return moveEmail(cfg, uid, archiveMailbox) -} +} \ No newline at end of file diff --git a/main.go b/main.go index 48bd81d061375d7aa031ea8c9eef2a8ae2b8f302..5581998663b4fb75ebada9c7f53e45ce4d6b9324 100644 --- a/main.go +++ b/main.go @@ -152,6 +152,21 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, m.current.Init() case tui.ViewEmailMsg: + // Show a status message while fetching the email body + m.current = tui.NewStatus("Fetching email content...") + // Pass the index directly to the command + return m, tea.Batch(m.current.Init(), fetchEmailBodyCmd(m.config, m.emails[msg.Index], msg.Index)) + + case tui.EmailBodyFetchedMsg: + if msg.Err != nil { + log.Printf("could not fetch email body: %v", msg.Err) + m.current = m.inbox + return m, nil + } + // Use the index from the message to update the correct email + m.emails[msg.Index].Body = msg.Body + m.emails[msg.Index].Attachments = msg.Attachments + emailView := tui.NewEmailView(m.emails[msg.Index], m.width, m.height) m.current = emailView return m, m.current.Init() @@ -248,6 +263,23 @@ func (m *mainModel) View() string { return m.current.View() } +func fetchEmailBodyCmd(cfg *config.Config, email fetcher.Email, index int) tea.Cmd { + return func() tea.Msg { + body, attachments, err := fetcher.FetchEmailBody(cfg, email.UID) + if err != nil { + return tui.EmailBodyFetchedMsg{Index: index, Err: err} + } + + // Return the fetched data along with the original index + return tui.EmailBodyFetchedMsg{ + Index: index, + Body: body, + Attachments: attachments, + } + } +} + + func markdownToHTML(md []byte) []byte { var buf bytes.Buffer p := goldmark.New(goldmark.WithRendererOptions(html.WithUnsafe())) diff --git a/tui/messages.go b/tui/messages.go index 4f14b69848bc0a4734c0f7b0f96742ac5d2a0623..b6fb3014c6f326e13eb15c43f25f7c38bb4f03b6 100644 --- a/tui/messages.go +++ b/tui/messages.go @@ -116,4 +116,4 @@ type EmailBodyFetchedMsg struct { Body string Attachments []fetcher.Attachment Err error -} \ No newline at end of file +}