From 203d0f07a080805dbf0df5aa2580f2ed6610b68f Mon Sep 17 00:00:00 2001 From: mavonx Date: Sat, 30 May 2026 14:14:41 +0300 Subject: [PATCH 1/3] fix: delegate email operations to daemon --- main.go | 242 ++++++++++++++++++++++++++------------------------------ 1 file changed, 113 insertions(+), 129 deletions(-) diff --git a/main.go b/main.go index 3ecafcec..b7ec8af3 100644 --- a/main.go +++ b/main.go @@ -832,9 +832,27 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo if account == nil { return m, nil } - m.previousModel = m.current - m.current = tui.NewStatus("Moving email...") - return m, tea.Batch(m.current.Init(), moveEmailToFolderCmd(account, msg.UID, msg.AccountID, msg.SourceFolder, msg.DestFolder)) + + folderName := folderInbox + if m.folderInbox != nil { + folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmail(msg.UID, msg.AccountID) + } + + m.removeEmailFromStores(msg.UID, msg.AccountID) + + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.UID != msg.UID || e.AccountID != msg.AccountID { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) + } + + return m, m.moveEmailToFolderCmd(account, msg.UID, msg.AccountID, msg.SourceFolder, msg.DestFolder) case tui.UpdatePreviewMsg: // Trigger preview body fetch @@ -921,11 +939,6 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo return tui.RestoreViewMsg{} }) } - // Remove email from current view - if m.folderInbox != nil { - m.folderInbox.RemoveEmail(msg.UID, msg.AccountID) - m.current = m.folderInbox - } return m, nil case tui.CachedEmailsLoadedMsg: @@ -1752,8 +1765,6 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo case tui.DeleteEmailMsg: tui.ClearKittyGraphics() - m.previousModel = m.current - m.current = tui.NewStatus("Deleting email...") account := m.config.GetAccountByID(msg.AccountID) if account == nil { @@ -1766,13 +1777,26 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmail(msg.UID, msg.AccountID) + } + + m.removeEmailFromStores(msg.UID, msg.AccountID) + + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.UID != msg.UID || e.AccountID != msg.AccountID { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) } - return m, tea.Batch(m.current.Init(), deleteFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox)) + + return m, m.deleteFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox) case tui.ArchiveEmailMsg: tui.ClearKittyGraphics() - m.previousModel = m.current - m.current = tui.NewStatus("Archiving email...") account := m.config.GetAccountByID(msg.AccountID) if account == nil { @@ -1785,8 +1809,23 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmail(msg.UID, msg.AccountID) } - return m, tea.Batch(m.current.Init(), archiveFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox)) + + m.removeEmailFromStores(msg.UID, msg.AccountID) + + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.UID != msg.UID || e.AccountID != msg.AccountID { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) + } + + return m, m.archiveFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox) case tui.EmailMarkedReadMsg: if msg.Err != nil { @@ -1814,24 +1853,10 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo }) } - // Remove email from stores - m.removeEmailFromStores(msg.UID, msg.AccountID) - - if m.folderInbox != nil { - m.folderInbox.RemoveEmail(msg.UID, msg.AccountID) - m.current = m.folderInbox - m.current, _ = m.current.Update(m.currentWindowSize()) - return m, m.current.Init() - } - m.current = tui.NewChoice() - m.current, _ = m.current.Update(m.currentWindowSize()) - return m, m.current.Init() + return m, nil case tui.BatchDeleteEmailsMsg: tui.ClearKittyGraphics() - m.previousModel = m.current - count := len(msg.UIDs) - m.current = tui.NewStatus(fmt.Sprintf("Deleting %d emails...", count)) account := m.config.GetAccountByID(msg.AccountID) if account == nil { @@ -1844,18 +1869,24 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) } - return m, tea.Batch( - m.current.Init(), - m.batchDeleteEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, count), - ) + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.AccountID != msg.AccountID || !slices.Contains(msg.UIDs, e.UID) { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) + } + + return m, m.batchDeleteEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) case tui.BatchArchiveEmailsMsg: tui.ClearKittyGraphics() - m.previousModel = m.current - count := len(msg.UIDs) - m.current = tui.NewStatus(fmt.Sprintf("Archiving %d emails...", count)) account := m.config.GetAccountByID(msg.AccountID) if account == nil { @@ -1868,12 +1899,21 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) } - return m, tea.Batch( - m.current.Init(), - m.batchArchiveEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, count), - ) + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.AccountID != msg.AccountID || !slices.Contains(msg.UIDs, e.UID) { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) + } + + return m, m.batchArchiveEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) case tui.BatchMoveEmailsMsg: if m.config == nil { @@ -1884,14 +1924,24 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo return m, nil } - count := len(msg.UIDs) - m.previousModel = m.current - m.current = tui.NewStatus(fmt.Sprintf("Moving %d emails...", count)) + folderName := folderInbox + if m.folderInbox != nil { + folderName = m.folderInbox.GetCurrentFolder() + m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) + } - return m, tea.Batch( - m.current.Init(), - m.batchMoveEmailsCmd(account, msg.UIDs, msg.AccountID, msg.SourceFolder, msg.DestFolder, count), - ) + if emails, ok := m.folderEmails[folderName]; ok { + var filtered []fetcher.Email + for _, e := range emails { + if e.AccountID != msg.AccountID || !slices.Contains(msg.UIDs, e.UID) { + filtered = append(filtered, e) + } + } + m.folderEmails[folderName] = filtered + go saveFolderEmailsToCache(folderName, filtered) + } + + return m, m.batchMoveEmailsCmd(account, msg.UIDs, msg.AccountID, msg.SourceFolder, msg.DestFolder, len(msg.UIDs)) case tui.BatchEmailActionDoneMsg: if msg.Err != nil { @@ -1902,18 +1952,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo }) } - // Success - show brief confirmation - successMsg := fmt.Sprintf("%d emails %sd successfully", msg.SuccessCount, msg.Action) - if msg.FailureCount > 0 { - successMsg = fmt.Sprintf("%d of %d emails %sd (%d failed)", - msg.SuccessCount, msg.Count, msg.Action, msg.FailureCount) - } - - m.current = tui.NewStatus(successMsg) - - return m, tea.Tick(1500*time.Millisecond, func(t time.Time) tea.Msg { - return tui.RestoreViewMsg{} - }) + return m, nil case tui.DownloadAttachmentMsg: m.previousModel = m.current @@ -2931,46 +2970,27 @@ func markEmailAsUnreadCmd(account *config.Account, uid uint32, accountID string, } } -func deleteFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { +func (m *mainModel) deleteFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { - err := fetcher.DeleteFolderEmail(account, folderName, uid) + err := m.service.DeleteEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } } -func archiveFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { +func (m *mainModel) archiveFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { - err := fetcher.ArchiveFolderEmail(account, folderName, uid) + err := m.service.ArchiveEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } } func (m *mainModel) batchDeleteEmailsCmd(account *config.Account, uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout) - defer cancel() - - p := m.getProvider(account) - if p == nil { - return tui.BatchEmailActionDoneMsg{ - Count: count, - Action: "delete", - Err: fmt.Errorf("provider not found"), - } - } - - err := p.DeleteEmails(ctx, folderName, uids) - - // Remove emails from local state on success - if err == nil && m.folderInbox != nil { - m.folderInbox.GetInbox().RemoveEmails(uids, accountID) - } + err := m.service.DeleteEmails(accountID, folderName, uids) - successCount := count - failureCount := 0 + successCount, failureCount := count, 0 if err != nil { - failureCount = count - successCount = 0 + successCount, failureCount = 0, count } return tui.BatchEmailActionDoneMsg{ @@ -2986,29 +3006,11 @@ func (m *mainModel) batchDeleteEmailsCmd(account *config.Account, uids []uint32, func (m *mainModel) batchArchiveEmailsCmd(account *config.Account, uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout) - defer cancel() - - p := m.getProvider(account) - if p == nil { - return tui.BatchEmailActionDoneMsg{ - Count: count, - Action: "archive", - Err: fmt.Errorf("provider not found"), - } - } - - err := p.ArchiveEmails(ctx, folderName, uids) + err := m.service.ArchiveEmails(accountID, folderName, uids) - if err == nil && m.folderInbox != nil { - m.folderInbox.GetInbox().RemoveEmails(uids, accountID) - } - - successCount := count - failureCount := 0 + successCount, failureCount := count, 0 if err != nil { - failureCount = count - successCount = 0 + successCount, failureCount = 0, count } return tui.BatchEmailActionDoneMsg{ @@ -3024,29 +3026,11 @@ func (m *mainModel) batchArchiveEmailsCmd(account *config.Account, uids []uint32 func (m *mainModel) batchMoveEmailsCmd(account *config.Account, uids []uint32, accountID, sourceFolder, destFolder string, count int) tea.Cmd { return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout) - defer cancel() - - p := m.getProvider(account) - if p == nil { - return tui.BatchEmailActionDoneMsg{ - Count: count, - Action: "move", - Err: fmt.Errorf("provider not found"), - } - } - - err := p.MoveEmails(ctx, uids, sourceFolder, destFolder) - - if err == nil && m.folderInbox != nil { - m.folderInbox.GetInbox().RemoveEmails(uids, accountID) - } + err := m.service.MoveEmails(accountID, uids, sourceFolder, destFolder) - successCount := count - failureCount := 0 + successCount, failureCount := count, 0 if err != nil { - failureCount = count - successCount = 0 + successCount, failureCount = 0, count } return tui.BatchEmailActionDoneMsg{ @@ -3059,9 +3043,9 @@ func (m *mainModel) batchMoveEmailsCmd(account *config.Account, uids []uint32, a } } -func moveEmailToFolderCmd(account *config.Account, uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd { +func (m *mainModel) moveEmailToFolderCmd(account *config.Account, uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd { return func() tea.Msg { - err := fetcher.MoveEmailToFolder(account, uid, sourceFolder, destFolder) + err := m.service.MoveEmails(accountID, []uint32{uid}, sourceFolder, destFolder) return tui.EmailMovedMsg{ UID: uid, AccountID: accountID, From 0aa9d2a1d3f69dfe4c04cf0a864d8f9fa85ca9e8 Mon Sep 17 00:00:00 2001 From: mavonx Date: Sat, 30 May 2026 14:27:33 +0300 Subject: [PATCH 2/3] fix: remove unused parameters --- main.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index b7ec8af3..d320409a 100644 --- a/main.go +++ b/main.go @@ -852,7 +852,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.moveEmailToFolderCmd(account, msg.UID, msg.AccountID, msg.SourceFolder, msg.DestFolder) + return m, m.moveEmailToFolderCmd(msg.UID, msg.AccountID, msg.SourceFolder, msg.DestFolder) case tui.UpdatePreviewMsg: // Trigger preview body fetch @@ -1793,7 +1793,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.deleteFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox) + return m, m.deleteFolderEmailCmd(msg.UID, msg.AccountID, folderName, msg.Mailbox) case tui.ArchiveEmailMsg: tui.ClearKittyGraphics() @@ -1825,7 +1825,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.archiveFolderEmailCmd(account, msg.UID, msg.AccountID, folderName, msg.Mailbox) + return m, m.archiveFolderEmailCmd(msg.UID, msg.AccountID, folderName, msg.Mailbox) case tui.EmailMarkedReadMsg: if msg.Err != nil { @@ -1883,7 +1883,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.batchDeleteEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) + return m, m.batchDeleteEmailsCmd(msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) case tui.BatchArchiveEmailsMsg: tui.ClearKittyGraphics() @@ -1913,7 +1913,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.batchArchiveEmailsCmd(account, msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) + return m, m.batchArchiveEmailsCmd(msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs)) case tui.BatchMoveEmailsMsg: if m.config == nil { @@ -1941,7 +1941,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo go saveFolderEmailsToCache(folderName, filtered) } - return m, m.batchMoveEmailsCmd(account, msg.UIDs, msg.AccountID, msg.SourceFolder, msg.DestFolder, len(msg.UIDs)) + return m, m.batchMoveEmailsCmd(msg.UIDs, msg.AccountID, msg.SourceFolder, msg.DestFolder, len(msg.UIDs)) case tui.BatchEmailActionDoneMsg: if msg.Err != nil { @@ -2970,21 +2970,21 @@ func markEmailAsUnreadCmd(account *config.Account, uid uint32, accountID string, } } -func (m *mainModel) deleteFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { +func (m *mainModel) deleteFolderEmailCmd(uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { err := m.service.DeleteEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } } -func (m *mainModel) archiveFolderEmailCmd(account *config.Account, uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { +func (m *mainModel) archiveFolderEmailCmd(uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { err := m.service.ArchiveEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } } -func (m *mainModel) batchDeleteEmailsCmd(account *config.Account, uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { +func (m *mainModel) batchDeleteEmailsCmd(uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { err := m.service.DeleteEmails(accountID, folderName, uids) @@ -3004,7 +3004,7 @@ func (m *mainModel) batchDeleteEmailsCmd(account *config.Account, uids []uint32, } } -func (m *mainModel) batchArchiveEmailsCmd(account *config.Account, uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { +func (m *mainModel) batchArchiveEmailsCmd(uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { err := m.service.ArchiveEmails(accountID, folderName, uids) @@ -3024,7 +3024,7 @@ func (m *mainModel) batchArchiveEmailsCmd(account *config.Account, uids []uint32 } } -func (m *mainModel) batchMoveEmailsCmd(account *config.Account, uids []uint32, accountID, sourceFolder, destFolder string, count int) tea.Cmd { +func (m *mainModel) batchMoveEmailsCmd(uids []uint32, accountID, sourceFolder, destFolder string, count int) tea.Cmd { return func() tea.Msg { err := m.service.MoveEmails(accountID, uids, sourceFolder, destFolder) @@ -3043,7 +3043,7 @@ func (m *mainModel) batchMoveEmailsCmd(account *config.Account, uids []uint32, a } } -func (m *mainModel) moveEmailToFolderCmd(account *config.Account, uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd { +func (m *mainModel) moveEmailToFolderCmd(uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd { return func() tea.Msg { err := m.service.MoveEmails(accountID, []uint32{uid}, sourceFolder, destFolder) return tui.EmailMovedMsg{ From 50d79144b720649ffd148af44aae6647356fa40c Mon Sep 17 00:00:00 2001 From: mavonx Date: Sun, 31 May 2026 12:56:51 +0300 Subject: [PATCH 3/3] fix: nil guard, reader navigation, and sync lookup --- main.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/main.go b/main.go index d320409a..b9494921 100644 --- a/main.go +++ b/main.go @@ -1776,6 +1776,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { + m.current = m.folderInbox folderName = m.folderInbox.GetCurrentFolder() m.folderInbox.GetInbox().RemoveEmail(msg.UID, msg.AccountID) } @@ -1808,6 +1809,7 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo folderName := folderInbox if m.folderInbox != nil { + m.current = m.folderInbox folderName = m.folderInbox.GetCurrentFolder() m.folderInbox.GetInbox().RemoveEmail(msg.UID, msg.AccountID) } @@ -1872,6 +1874,10 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) } + for _, uid := range msg.UIDs { + m.removeEmailFromStores(uid, msg.AccountID) + } + if emails, ok := m.folderEmails[folderName]; ok { var filtered []fetcher.Email for _, e := range emails { @@ -1902,6 +1908,10 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) } + for _, uid := range msg.UIDs { + m.removeEmailFromStores(uid, msg.AccountID) + } + if emails, ok := m.folderEmails[folderName]; ok { var filtered []fetcher.Email for _, e := range emails { @@ -1930,6 +1940,10 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo m.folderInbox.GetInbox().RemoveEmails(msg.UIDs, msg.AccountID) } + for _, uid := range msg.UIDs { + m.removeEmailFromStores(uid, msg.AccountID) + } + if emails, ok := m.folderEmails[folderName]; ok { var filtered []fetcher.Email for _, e := range emails { @@ -2972,6 +2986,14 @@ func markEmailAsUnreadCmd(account *config.Account, uid uint32, accountID string, func (m *mainModel) deleteFolderEmailCmd(uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.EmailActionDoneMsg{ + UID: uid, + AccountID: accountID, + Mailbox: mailbox, + Err: fmt.Errorf("service not initialized"), + } + } err := m.service.DeleteEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } @@ -2979,6 +3001,14 @@ func (m *mainModel) deleteFolderEmailCmd(uid uint32, accountID string, folderNam func (m *mainModel) archiveFolderEmailCmd(uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.EmailActionDoneMsg{ + UID: uid, + AccountID: accountID, + Mailbox: mailbox, + Err: fmt.Errorf("service not initialized"), + } + } err := m.service.ArchiveEmails(accountID, folderName, []uint32{uid}) return tui.EmailActionDoneMsg{UID: uid, AccountID: accountID, Mailbox: mailbox, Err: err} } @@ -2986,6 +3016,17 @@ func (m *mainModel) archiveFolderEmailCmd(uid uint32, accountID string, folderNa func (m *mainModel) batchDeleteEmailsCmd(uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.BatchEmailActionDoneMsg{ + Count: count, + SuccessCount: 0, + FailureCount: count, + Action: "delete", + Mailbox: mailbox, + Err: fmt.Errorf("service not initialized"), + } + } + err := m.service.DeleteEmails(accountID, folderName, uids) successCount, failureCount := count, 0 @@ -3006,6 +3047,17 @@ func (m *mainModel) batchDeleteEmailsCmd(uids []uint32, accountID, folderName st func (m *mainModel) batchArchiveEmailsCmd(uids []uint32, accountID, folderName string, mailbox tui.MailboxKind, count int) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.BatchEmailActionDoneMsg{ + Count: count, + SuccessCount: 0, + FailureCount: count, + Action: "archive", + Mailbox: mailbox, + Err: fmt.Errorf("service not initialized"), + } + } + err := m.service.ArchiveEmails(accountID, folderName, uids) successCount, failureCount := count, 0 @@ -3026,6 +3078,16 @@ func (m *mainModel) batchArchiveEmailsCmd(uids []uint32, accountID, folderName s func (m *mainModel) batchMoveEmailsCmd(uids []uint32, accountID, sourceFolder, destFolder string, count int) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.BatchEmailActionDoneMsg{ + Count: count, + SuccessCount: 0, + FailureCount: count, + Action: "move", + Err: fmt.Errorf("service not initialized"), + } + } + err := m.service.MoveEmails(accountID, uids, sourceFolder, destFolder) successCount, failureCount := count, 0 @@ -3045,6 +3107,16 @@ func (m *mainModel) batchMoveEmailsCmd(uids []uint32, accountID, sourceFolder, d func (m *mainModel) moveEmailToFolderCmd(uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd { return func() tea.Msg { + if m.service == nil { + return tui.EmailMovedMsg{ + UID: uid, + AccountID: accountID, + SourceFolder: sourceFolder, + DestFolder: destFolder, + Err: fmt.Errorf("service not initialized"), + } + } + err := m.service.MoveEmails(accountID, []uint32{uid}, sourceFolder, destFolder) return tui.EmailMovedMsg{ UID: uid,