From 9945814f2d6bf3ba905096b5841dfbb51944b634 Mon Sep 17 00:00:00 2001 From: Drew Smirnoff Date: Tue, 31 Mar 2026 11:17:40 +0400 Subject: [PATCH] fix!: IMAP IDLE behavior (#433) --- fetcher/idle.go | 31 +++++++++++++++++++++++++++++-- main.go | 7 +++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/fetcher/idle.go b/fetcher/idle.go index 5cc96856daf9edf503229fd86e2540893eea431e..c98aa71642d3c47f7b63a699320c4ddedb516b32 100644 --- a/fetcher/idle.go +++ b/fetcher/idle.go @@ -47,8 +47,8 @@ func (w *IdleWatcher) Watch(account *config.Account, folder string) { // Stop existing watcher for this account if any if existing, ok := w.watchers[account.ID]; ok { close(existing.stop) - <-existing.done delete(w.watchers, account.ID) + // Let old connection tear down in the background } a := &accountIdle{ @@ -62,6 +62,18 @@ func (w *IdleWatcher) Watch(account *config.Account, folder string) { go a.run() } +// Stop stops the IDLE watcher for a specific account. +func (w *IdleWatcher) Stop(accountID string) { + w.mu.Lock() + defer w.mu.Unlock() + + if a, ok := w.watchers[accountID]; ok { + close(a.stop) + delete(w.watchers, accountID) + // Let old connection tear down in the background + } +} + // StopAll stops all IDLE watchers. func (w *IdleWatcher) StopAll() { w.mu.Lock() @@ -69,11 +81,26 @@ func (w *IdleWatcher) StopAll() { for id, a := range w.watchers { close(a.stop) - <-a.done delete(w.watchers, id) } } +// StopAllAndWait stops all IDLE watchers and waits for them to finish. +func (w *IdleWatcher) StopAllAndWait() { + w.mu.Lock() + var pending []chan struct{} + for id, a := range w.watchers { + close(a.stop) + pending = append(pending, a.done) + delete(w.watchers, id) + } + w.mu.Unlock() + + for _, done := range pending { + <-done + } +} + func (a *accountIdle) run() { defer close(a.done) diff --git a/main.go b/main.go index 04498d2337dd2e611cafc6e8f43cdc8e78ae8b24..cd8ae3889fc33a949b6a9e4499b748e5684170a8 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "path/filepath" "regexp" "runtime" + "slices" "strings" "sync" "time" @@ -416,6 +417,12 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } // Update IDLE watchers to monitor the new folder for i := range m.config.Accounts { + // Only start IDLE for accounts that actually have this folder + folders := config.GetCachedFolders(m.config.Accounts[i].ID) + if !slices.Contains(folders, msg.FolderName) { + m.idleWatcher.Stop(m.config.Accounts[i].ID) + continue + } m.idleWatcher.Watch(&m.config.Accounts[i], msg.FolderName) } if m.plugins != nil {