@@ -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(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 {
@@ -1765,14 +1776,28 @@ 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)
+ }
+
+ 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(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 {
@@ -1784,9 +1809,25 @@ 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)
}
- 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(msg.UID, msg.AccountID, folderName, msg.Mailbox)
case tui.EmailMarkedReadMsg:
if msg.Err != nil {
@@ -1814,24 +1855,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 +1871,28 @@ 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),
- )
+ 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 {
+ 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(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 +1905,25 @@ 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),
- )
+ 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 {
+ 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(msg.UIDs, msg.AccountID, folderName, msg.Mailbox, len(msg.UIDs))
case tui.BatchMoveEmailsMsg:
if m.config == nil {
@@ -1884,14 +1934,28 @@ 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),
- )
+ 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 {
+ 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(msg.UIDs, msg.AccountID, msg.SourceFolder, msg.DestFolder, len(msg.UIDs))
case tui.BatchEmailActionDoneMsg:
if msg.Err != nil {
@@ -1902,18 +1966,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 +2984,54 @@ 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(uid uint32, accountID string, folderName string, mailbox tui.MailboxKind) tea.Cmd {
return func() tea.Msg {
- err := fetcher.DeleteFolderEmail(account, folderName, uid)
+ 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}
}
}
-func 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 := fetcher.ArchiveFolderEmail(account, folderName, uid)
+ 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}
}
}
-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 {
- ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout)
- defer cancel()
-
- p := m.getProvider(account)
- if p == nil {
+ if m.service == nil {
return tui.BatchEmailActionDoneMsg{
- Count: count,
- Action: "delete",
- Err: fmt.Errorf("provider not found"),
+ Count: count,
+ SuccessCount: 0,
+ FailureCount: count,
+ Action: "delete",
+ Mailbox: mailbox,
+ Err: fmt.Errorf("service not initialized"),
}
}
- err := p.DeleteEmails(ctx, folderName, uids)
+ err := m.service.DeleteEmails(accountID, folderName, uids)
- // Remove emails from local state on success
- 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{
@@ -2984,31 +3045,24 @@ 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 {
- ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout)
- defer cancel()
-
- p := m.getProvider(account)
- if p == nil {
+ if m.service == nil {
return tui.BatchEmailActionDoneMsg{
- Count: count,
- Action: "archive",
- Err: fmt.Errorf("provider not found"),
+ Count: count,
+ SuccessCount: 0,
+ FailureCount: count,
+ Action: "archive",
+ Mailbox: mailbox,
+ Err: fmt.Errorf("service not initialized"),
}
}
- err := p.ArchiveEmails(ctx, folderName, uids)
-
- if err == nil && m.folderInbox != nil {
- m.folderInbox.GetInbox().RemoveEmails(uids, accountID)
- }
+ err := m.service.ArchiveEmails(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{
@@ -3022,31 +3076,23 @@ 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 {
- ctx, cancel := context.WithTimeout(context.Background(), httpclient.IMAPBatchActionTimeout)
- defer cancel()
-
- p := m.getProvider(account)
- if p == nil {
+ if m.service == nil {
return tui.BatchEmailActionDoneMsg{
- Count: count,
- Action: "move",
- Err: fmt.Errorf("provider not found"),
+ Count: count,
+ SuccessCount: 0,
+ FailureCount: count,
+ Action: "move",
+ Err: fmt.Errorf("service not initialized"),
}
}
- err := p.MoveEmails(ctx, uids, sourceFolder, destFolder)
+ err := m.service.MoveEmails(accountID, uids, sourceFolder, destFolder)
- 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{
@@ -3059,9 +3105,19 @@ 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(uid uint32, accountID string, sourceFolder, destFolder string) tea.Cmd {
return func() tea.Msg {
- err := fetcher.MoveEmailToFolder(account, uid, sourceFolder, destFolder)
+ 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,
AccountID: accountID,