diff options
-rw-r--r-- | app/account.go | 9 | ||||
-rw-r--r-- | commands/account/import-mbox.go | 44 | ||||
-rw-r--r-- | commands/compose/postpone.go | 37 | ||||
-rw-r--r-- | commands/compose/send.go | 48 | ||||
-rw-r--r-- | config/aerc.conf | 4 | ||||
-rw-r--r-- | config/hooks.go | 1 | ||||
-rw-r--r-- | doc/aerc-config.5.scd | 15 | ||||
-rw-r--r-- | lib/hooks/mail-added.go | 23 | ||||
-rw-r--r-- | lib/msgstore.go | 30 |
9 files changed, 151 insertions, 60 deletions
diff --git a/app/account.go b/app/account.go index b5bed86e..734ba37a 100644 --- a/app/account.go +++ b/app/account.go @@ -265,6 +265,15 @@ func (acct *AccountView) newStore(name string) *lib.MessageStore { msg := fmt.Sprintf("mail-deleted hook: %s", err) PushError(msg) } + }, func(dest string) { + err := hooks.RunHook(&hooks.MailAdded{ + Account: acct.Name(), + Folder: dest, + }) + if err != nil { + msg := fmt.Sprintf("mail-added hook: %s", err) + PushError(msg) + } }, acct.updateSplitView, acct.dirlist.UiConfig(name).ThreadContext, diff --git a/commands/account/import-mbox.go b/commands/account/import-mbox.go index 774dfa8f..7aa393b6 100644 --- a/commands/account/import-mbox.go +++ b/commands/account/import-mbox.go @@ -65,7 +65,6 @@ func (i ImportMbox) Execute(args []string) error { app.PushError(err.Error()) return } - worker := acct.Worker() var appended uint32 for i, m := range messages { @@ -79,27 +78,28 @@ func (i ImportMbox) Execute(args []string) error { break } nbytes, _ := io.Copy(&buf, r) - worker.PostAction(&types.AppendMessage{ - Destination: folder, - Flags: models.SeenFlag, - Date: time.Now(), - Reader: &buf, - Length: int(nbytes), - }, func(msg types.WorkerMessage) { - switch msg := msg.(type) { - case *types.Unsupported: - errMsg := fmt.Sprintf("%s: AppendMessage is unsupported", args[0]) - log.Errorf(errMsg) - app.PushError(errMsg) - return - case *types.Error: - log.Errorf("AppendMessage failed: %v", msg.Error) - done <- false - case *types.Done: - atomic.AddUint32(&appended, 1) - done <- true - } - }) + store.Append( + folder, + models.SeenFlag, + time.Now(), + &buf, + int(nbytes), + func(msg types.WorkerMessage) { + switch msg := msg.(type) { + case *types.Unsupported: + errMsg := fmt.Sprintf("%s: AppendMessage is unsupported", args[0]) + log.Errorf(errMsg) + app.PushError(errMsg) + return + case *types.Error: + log.Errorf("AppendMessage failed: %v", msg.Error) + done <- false + case *types.Done: + atomic.AddUint32(&appended, 1) + done <- true + } + }, + ) select { case ok := <-done: diff --git a/commands/compose/postpone.go b/commands/compose/postpone.go index 3ce9cc84..098b7039 100644 --- a/commands/compose/postpone.go +++ b/commands/compose/postpone.go @@ -34,6 +34,10 @@ func (p Postpone) Execute(args []string) error { if acct == nil { return errors.New("No account selected") } + store := acct.Store() + if store == nil { + return errors.New("No message store selected") + } tab := app.SelectedTab() if tab == nil { return errors.New("No tab selected") @@ -98,22 +102,23 @@ func (p Postpone) Execute(args []string) error { handleErr(errors.Wrap(err, "WriteMessage")) return } - worker.PostAction(&types.AppendMessage{ - Destination: targetFolder, - Flags: models.SeenFlag, - Date: time.Now(), - Reader: buf, - Length: buf.Len(), - }, func(msg types.WorkerMessage) { - switch msg := msg.(type) { - case *types.Done: - app.PushStatus("Message postponed.", 10*time.Second) - composer.SetPostponed() - composer.Close() - case *types.Error: - handleErr(msg.Error) - } - }) + store.Append( + targetFolder, + models.SeenFlag, + time.Now(), + buf, + buf.Len(), + func(msg types.WorkerMessage) { + switch msg := msg.(type) { + case *types.Done: + app.PushStatus("Message postponed.", 10*time.Second) + composer.SetPostponed() + composer.Close() + case *types.Error: + handleErr(msg.Error) + } + }, + ) }() if !alreadyCreated { diff --git a/commands/compose/send.go b/commands/compose/send.go index 1047ebad..91fd42bd 100644 --- a/commands/compose/send.go +++ b/commands/compose/send.go @@ -228,8 +228,7 @@ func send(composer *app.Composer, ctx sendCtx, } if ctx.copyto != "" && ctx.scheme != "jmap" { app.PushStatus("Copying to "+ctx.copyto, 10*time.Second) - errch := copyToSent(composer.Worker(), ctx.copyto, - copyBuf.Len(), ©Buf) + errch := copyToSent(ctx.copyto, copyBuf.Len(), ©Buf) err = <-errch if err != nil { errmsg := fmt.Sprintf( @@ -558,23 +557,32 @@ func newJmapSender( return writer, err } -func copyToSent(worker *types.Worker, dest string, - n int, msg io.Reader, -) <-chan error { - errCh := make(chan error) - worker.PostAction(&types.AppendMessage{ - Destination: dest, - Flags: models.SeenFlag, - Date: time.Now(), - Reader: msg, - Length: n, - }, func(msg types.WorkerMessage) { - switch msg := msg.(type) { - case *types.Done: - errCh <- nil - case *types.Error: - errCh <- msg.Error - } - }) +func copyToSent(dest string, n int, msg io.Reader) <-chan error { + errCh := make(chan error, 1) + acct := app.SelectedAccount() + if acct == nil { + errCh <- errors.New("No account selected") + return errCh + } + store := acct.Store() + if store == nil { + errCh <- errors.New("No message store selected") + return errCh + } + store.Append( + dest, + models.SeenFlag, + time.Now(), + msg, + n, + func(msg types.WorkerMessage) { + switch msg := msg.(type) { + case *types.Done: + errCh <- nil + case *types.Error: + errCh <- msg.Error + } + }, + ) return errCh } diff --git a/config/aerc.conf b/config/aerc.conf index cf9aafa2..d8142197 100644 --- a/config/aerc.conf +++ b/config/aerc.conf @@ -585,6 +585,10 @@ message/rfc822=colorize #mail-deleted=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" & # +# Executed when aerc adds mail to a folder +#mail-added=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" & + +# # Executed when aerc starts #aerc-startup=aerc :terminal calcurse && aerc :next-tab diff --git a/config/hooks.go b/config/hooks.go index 7a6047c1..b1b71a25 100644 --- a/config/hooks.go +++ b/config/hooks.go @@ -12,6 +12,7 @@ type HooksConfig struct { AercShutdown string `ini:"aerc-shutdown"` MailReceived string `ini:"mail-received"` MailDeleted string `ini:"mail-deleted"` + MailAdded string `ini:"mail-added"` } var Hooks HooksConfig diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd index 200c0609..34bfb882 100644 --- a/doc/aerc-config.5.scd +++ b/doc/aerc-config.5.scd @@ -971,6 +971,21 @@ They are configured in the *[hooks]* section of aerc.conf. *mail-deleted* = _mbsync "$AERC_ACCOUNT:$AERC_FOLDER"_ +*mail-added* = _<command>_ + Executed when a message is added to a folder. Note that this hook is not + triggered when a new message is received (use *mail-received* for that) but + rather is only triggered when aerc itself adds a message to a folder, e.g. + when moving or copying a message. + + Variables: + + - *AERC_ACCOUNT* + - *AERC_FOLDER* + + Example: + + *mail-added* = _mbsync "$AERC_ACCOUNT:$AERC_FOLDER"_ + *aerc-shutdown* = _<command>_ Executed when aerc shuts down. Aerc will wait for the command to finish before exiting. diff --git a/lib/hooks/mail-added.go b/lib/hooks/mail-added.go new file mode 100644 index 00000000..f146d010 --- /dev/null +++ b/lib/hooks/mail-added.go @@ -0,0 +1,23 @@ +package hooks + +import ( + "fmt" + + "git.sr.ht/~rjarry/aerc/config" +) + +type MailAdded struct { + Account string + Folder string +} + +func (m *MailAdded) Cmd() string { + return config.Hooks.MailAdded +} + +func (m *MailAdded) Env() []string { + return []string{ + fmt.Sprintf("AERC_ACCOUNT=%s", m.Account), + fmt.Sprintf("AERC_FOLDER=%s", m.Folder), + } +} diff --git a/lib/msgstore.go b/lib/msgstore.go index 9c6b750d..cb81abef 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -69,6 +69,7 @@ type MessageStore struct { triggerNewEmail func(*models.MessageInfo) triggerDirectoryChange func() triggerMailDeleted func() + triggerMailAdded func(string) threadBuilderDebounce *time.Timer threadBuilderDelay time.Duration @@ -89,7 +90,8 @@ func NewMessageStore(worker *types.Worker, reverseOrder bool, reverseThreadOrder bool, sortThreadSiblings bool, triggerNewEmail func(*models.MessageInfo), triggerDirectoryChange func(), triggerMailDeleted func(), - onSelect func(*models.MessageInfo), threadContext bool, + triggerMailAdded func(string), onSelect func(*models.MessageInfo), + threadContext bool, ) *MessageStore { if !worker.Backend.Capabilities().Thread { clientThreads = true @@ -124,6 +126,7 @@ func NewMessageStore(worker *types.Worker, triggerNewEmail: triggerNewEmail, triggerDirectoryChange: triggerDirectoryChange, triggerMailDeleted: triggerMailDeleted, + triggerMailAdded: triggerMailAdded, threadBuilderDelay: clientThreadsDelay, @@ -608,7 +611,12 @@ func (store *MessageStore) Copy(uids []uint32, dest string, createDest bool, store.worker.PostAction(&types.CopyMessages{ Destination: dest, Uids: uids, - }, cb) + }, func(msg types.WorkerMessage) { + if _, ok := msg.(*types.Done); ok { + store.triggerMailAdded(dest) + } + cb(msg) + }) } func (store *MessageStore) Move(uids []uint32, dest string, createDest bool, @@ -635,11 +643,29 @@ func (store *MessageStore) Move(uids []uint32, dest string, createDest bool, cb(msg) case *types.Done: store.triggerMailDeleted() + store.triggerMailAdded(dest) cb(msg) } }) } +func (store *MessageStore) Append(dest string, flags models.Flags, date time.Time, + reader io.Reader, length int, cb func(msg types.WorkerMessage), +) { + store.worker.PostAction(&types.AppendMessage{ + Destination: dest, + Flags: flags, + Date: date, + Reader: reader, + Length: length, + }, func(msg types.WorkerMessage) { + if _, ok := msg.(*types.Done); ok { + store.triggerMailAdded(dest) + } + cb(msg) + }) +} + func (store *MessageStore) Flag(uids []uint32, flags models.Flags, enable bool, cb func(msg types.WorkerMessage), ) { |