From 3102ac3680ba5fcfb126894a7b7b950b07b6c735 Mon Sep 17 00:00:00 2001 From: Jeffas Date: Fri, 24 Apr 2020 11:42:22 +0200 Subject: Add recall command This command allows recalling the selected postponed email to edit in the composer. The command only allows recalling from the postpone directory. --- commands/account/compose.go | 2 +- commands/compose/postpone.go | 4 +- commands/msg/forward.go | 2 +- commands/msg/recall.go | 141 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 commands/msg/recall.go (limited to 'commands') diff --git a/commands/account/compose.go b/commands/account/compose.go index 42d51d51..b33acf5a 100644 --- a/commands/account/compose.go +++ b/commands/account/compose.go @@ -31,7 +31,7 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error { } acct := aerc.SelectedAccount() - composer, err := widgets.NewComposer(aerc, acct + composer, err := widgets.NewComposer(aerc, acct, aerc.Config(), acct.AccountConfig(), acct.Worker(), template, nil, models.OriginalMail{}) if err != nil { diff --git a/commands/compose/postpone.go b/commands/compose/postpone.go index 44aa4118..60c9df1a 100644 --- a/commands/compose/postpone.go +++ b/commands/compose/postpone.go @@ -5,10 +5,10 @@ import ( "io/ioutil" "time" - "github.com/emersion/go-imap" "github.com/miolini/datacounter" "github.com/pkg/errors" + "git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/widgets" "git.sr.ht/~sircmpwn/aerc/worker/types" ) @@ -79,7 +79,7 @@ func (Postpone) Execute(aerc *widgets.Aerc, args []string) error { r, w := io.Pipe() worker.PostAction(&types.AppendMessage{ Destination: config.Postpone, - Flags: []string{imap.SeenFlag}, + Flags: []models.Flag{models.SeenFlag}, Date: time.Now(), Reader: r, Length: int(nbytes), diff --git a/commands/msg/forward.go b/commands/msg/forward.go index 833eb9fd..35a65d8e 100644 --- a/commands/msg/forward.go +++ b/commands/msg/forward.go @@ -78,7 +78,7 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error { original.Date = msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM") } - composer, err := widgets.NewComposer(aerc, aerc.Config(), acct.AccountConfig(), + composer, err := widgets.NewComposer(aerc, acct, aerc.Config(), acct.AccountConfig(), acct.Worker(), template, defaults, original) if err != nil { aerc.PushError("Error: " + err.Error()) diff --git a/commands/msg/recall.go b/commands/msg/recall.go new file mode 100644 index 00000000..c2f887a1 --- /dev/null +++ b/commands/msg/recall.go @@ -0,0 +1,141 @@ +package msg + +import ( + "io" + + "github.com/emersion/go-message" + _ "github.com/emersion/go-message/charset" + "github.com/emersion/go-message/mail" + "github.com/pkg/errors" + + "git.sr.ht/~sircmpwn/aerc/models" + "git.sr.ht/~sircmpwn/aerc/widgets" + "git.sr.ht/~sircmpwn/aerc/worker/types" +) + +type Recall struct{} + +func init() { + register(Recall{}) +} + +func (Recall) Aliases() []string { + return []string{"recall"} +} + +func (Recall) Complete(aerc *widgets.Aerc, args []string) []string { + return nil +} + +func (Recall) Execute(aerc *widgets.Aerc, args []string) error { + if len(args) != 1 { + return errors.New("Usage: recall") + } + + widget := aerc.SelectedTab().(widgets.ProvidesMessage) + acct := widget.SelectedAccount() + if acct == nil { + return errors.New("No account selected") + } + if acct.SelectedDirectory() != acct.AccountConfig().Postpone { + return errors.New("Can only recall from the postpone directory: " + + acct.AccountConfig().Postpone) + } + store := widget.Store() + if store == nil { + return errors.New("Cannot perform action. Messages still loading") + } + + msgInfo, err := widget.SelectedMessage() + if err != nil { + return errors.Wrap(err, "Recall failed") + } + acct.Logger().Println("Recalling message " + msgInfo.Envelope.MessageId) + + // copy the headers to the defaults map for addition to the composition + defaults := make(map[string]string) + headerFields := msgInfo.RFC822Headers.Fields() + for headerFields.Next() { + defaults[headerFields.Key()] = headerFields.Value() + } + + composer, err := widgets.NewComposer(aerc, acct, aerc.Config(), + acct.AccountConfig(), acct.Worker(), "", defaults, models.OriginalMail{}) + if err != nil { + return errors.Wrap(err, "Cannot open a new composer") + } + + // focus the terminal since the header fields are likely already done + composer.FocusTerminal() + + addTab := func() { + subject := msgInfo.Envelope.Subject + if subject == "" { + subject = "Recalled email" + } + tab := aerc.NewTab(composer, subject) + composer.OnHeaderChange("Subject", func(subject string) { + if subject == "" { + tab.Name = "New email" + } else { + tab.Name = subject + } + tab.Content.Invalidate() + }) + composer.OnClose(func(composer *widgets.Composer) { + worker := composer.Worker() + uids := []uint32{msgInfo.Uid} + + worker.PostAction(&types.DeleteMessages{ + Uids: uids, + }, func(msg types.WorkerMessage) { + switch msg := msg.(type) { + case *types.Error: + aerc.PushError(" " + msg.Error.Error()) + composer.Close() + } + }) + + return + }) + } + + // find the main body part and add it to the editor + // TODO: copy all parts of the message over? + var ( + path []int + part *models.BodyStructure + ) + if len(msgInfo.BodyStructure.Parts) != 0 { + part, path = findPlaintext(msgInfo.BodyStructure, path) + } + if part == nil { + part = msgInfo.BodyStructure + path = []int{1} + } + + store.FetchBodyPart(msgInfo.Uid, part, path, func(reader io.Reader) { + header := message.Header{} + header.SetText( + "Content-Transfer-Encoding", part.Encoding) + header.SetContentType(part.MIMEType, part.Params) + header.SetText("Content-Description", part.Description) + entity, err := message.New(header, reader) + if err != nil { + // TODO: Do something with the error + addTab() + return + } + mreader := mail.NewReader(entity) + part, err := mreader.NextPart() + if err != nil { + // TODO: Do something with the error + addTab() + return + } + composer.SetContents(part.Body) + addTab() + }) + + return nil +} -- cgit