diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2022-05-30 07:34:18 -0500 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-05-31 14:32:51 +0200 |
commit | 2551dd1bfa2c68a6ba8644a0c45b24fce8874674 (patch) | |
tree | ed752720e1a08708505fd9574b49629d5df84997 /widgets | |
parent | 30d57889741cfa8284eec9b32b29144fe01002a2 (diff) | |
download | aerc-2551dd1bfa2c68a6ba8644a0c45b24fce8874674.tar.gz |
feat: add background mail polling option for all workers
Check for new mail (recent, unseen, exists counts) with an external
command, or for imap with the STATUS command, at start or on
reconnection and every X time duration
IMAP:
The selected folder is skipped, per specification. Additional config
options are included for including/excluding folders explicitly.
Maildir/Notmuch:
An external command will be run in the background to check for new mail.
An optional timeout can be used with maildir/notmuch. Default is 10s
New account options:
check-mail
check-mail-cmd (maildir/notmuch only)
check-mail-timeout (maildir/notmuch only), default 10s
check-mail-include (IMAP only)
check-mail-exclude (IMAP only)
If unset, or set less than or equal to 0, check-mail will be ignored
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Moritz Poldrack <moritz@poldrack.dev>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/account.go | 46 | ||||
-rw-r--r-- | widgets/dirlist.go | 55 |
2 files changed, 73 insertions, 28 deletions
diff --git a/widgets/account.go b/widgets/account.go index b34396bd..e913cb7e 100644 --- a/widgets/account.go +++ b/widgets/account.go @@ -33,6 +33,7 @@ type AccountView struct { msglist *MessageList worker *types.Worker state *statusline.State + newConn bool // True if this is a first run after a new connection/reconnection } func (acct *AccountView) UiConfig() config.UIConfig { @@ -100,6 +101,9 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon worker.PostAction(&types.Configure{Config: acct}, nil) worker.PostAction(&types.Connect{}, nil) view.SetStatus(statusline.ConnectionActivity("Connecting...")) + if acct.CheckMail.Minutes() > 0 { + view.CheckMailTimer(acct.CheckMail) + } return view, nil } @@ -258,13 +262,14 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { } acct.msglist.SetInitDone() acct.logger.Println("Connected.") - acct.SetStatus(statusline.Connected(true)) + acct.SetStatus(statusline.SetConnected(true)) + acct.newConn = true }) case *types.Disconnect: acct.dirlist.UpdateList(nil) acct.msglist.SetStore(nil) acct.logger.Println("Disconnected.") - acct.SetStatus(statusline.Connected(false)) + acct.SetStatus(statusline.SetConnected(false)) case *types.OpenDirectory: if store, ok := acct.dirlist.SelectedMsgStore(); ok { // If we've opened this dir before, we can re-render it from @@ -279,6 +284,11 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { acct.dirlist.UpdateList(nil) case *types.RemoveDirectory: acct.dirlist.UpdateList(nil) + case *types.FetchMessageHeaders: + if acct.newConn && acct.AccountConfig().CheckMail.Minutes() > 0 { + acct.newConn = false + acct.CheckMail() + } } case *types.DirectoryInfo: if store, ok := acct.dirlist.MsgStore(msg.Info.Name); ok { @@ -327,7 +337,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { acct.labels = msg.Labels case *types.ConnError: acct.logger.Printf("connection error: [%s] %v", acct.acct.Name, msg.Error) - acct.SetStatus(statusline.Connected(false)) + acct.SetStatus(statusline.SetConnected(false)) acct.PushError(msg.Error) acct.msglist.SetStore(nil) acct.worker.PostAction(&types.Reconnect{}, nil) @@ -349,3 +359,33 @@ func (acct *AccountView) GetSortCriteria() []*types.SortCriterion { } return criteria } + +func (acct *AccountView) CheckMail() { + // Exclude selected mailbox, per IMAP specification + exclude := append(acct.AccountConfig().CheckMailExclude, acct.dirlist.Selected()) + dirs := acct.dirlist.List() + dirs = acct.dirlist.FilterDirs(dirs, acct.AccountConfig().CheckMailInclude, false) + dirs = acct.dirlist.FilterDirs(dirs, exclude, true) + acct.logger.Printf("Checking for new mail on account %s", acct.Name()) + acct.SetStatus(statusline.ConnectionActivity("Checking for new mail...")) + msg := &types.CheckMail{ + Directories: dirs, + Command: acct.acct.CheckMailCmd, + Timeout: acct.acct.CheckMailTimeout, + } + acct.worker.PostAction(msg, func(_ types.WorkerMessage) { + acct.SetStatus(statusline.ConnectionActivity("")) + }) +} + +func (acct *AccountView) CheckMailTimer(d time.Duration) { + ticker := time.NewTicker(d) + go func() { + for range ticker.C { + if !acct.state.Connected() { + continue + } + acct.CheckMail() + } + }() +} diff --git a/widgets/dirlist.go b/widgets/dirlist.go index 412ed06c..ca0f6c19 100644 --- a/widgets/dirlist.go +++ b/widgets/dirlist.go @@ -40,6 +40,8 @@ type DirectoryLister interface { SelectedMsgStore() (*lib.MessageStore, bool) MsgStore(string) (*lib.MessageStore, bool) SetMsgStore(string, *lib.MessageStore) + + FilterDirs([]string, []string, bool) []string } type DirectoryList struct { @@ -441,38 +443,41 @@ func (dirlist *DirectoryList) sortDirsByFoldersSortConfig() { // dirstore, based on AccountConfig.Folders (inclusion) and // AccountConfig.FoldersExclude (exclusion), in that order. func (dirlist *DirectoryList) filterDirsByFoldersConfig() { - filterDirs := func(orig, filters []string, exclude bool) []string { - if len(filters) == 0 { - return orig - } - var dest []string - for _, folder := range orig { - // When excluding, include things by default, and vice-versa - include := exclude - for _, f := range filters { - if folderMatches(folder, f) { - // If matched an exclusion, don't include - // If matched an inclusion, do include - include = !exclude - break - } - } - if include { - dest = append(dest, folder) - } - } - return dest - } - dirlist.dirs = dirlist.store.List() // 'folders' (if available) is used to make the initial list and // 'folders-exclude' removes from that list. configFolders := dirlist.acctConf.Folders - dirlist.dirs = filterDirs(dirlist.dirs, configFolders, false) + dirlist.dirs = dirlist.FilterDirs(dirlist.dirs, configFolders, false) configFoldersExclude := dirlist.acctConf.FoldersExclude - dirlist.dirs = filterDirs(dirlist.dirs, configFoldersExclude, true) + dirlist.dirs = dirlist.FilterDirs(dirlist.dirs, configFoldersExclude, true) +} + +// FilterDirs filters directories by the supplied filter. If exclude is false, +// the filter will only include directories from orig which exist in filters. +// If exclude is true, the directories in filters are removed from orig +func (dirlist *DirectoryList) FilterDirs(orig, filters []string, exclude bool) []string { + if len(filters) == 0 { + return orig + } + var dest []string + for _, folder := range orig { + // When excluding, include things by default, and vice-versa + include := exclude + for _, f := range filters { + if folderMatches(folder, f) { + // If matched an exclusion, don't include + // If matched an inclusion, do include + include = !exclude + break + } + } + if include { + dest = append(dest, folder) + } + } + return dest } func (dirlist *DirectoryList) SelectedMsgStore() (*lib.MessageStore, bool) { |