From b1eb7ad18d2e0bbeecaf61a58825bbc794ceb40c Mon Sep 17 00:00:00 2001 From: Srivathsan Murali Date: Mon, 25 May 2020 16:59:48 +0200 Subject: [PATCH] Set AnsweredFlag on successful reply --- commands/compose/send.go | 2 ++ commands/msg/reply.go | 4 ++++ lib/msgstore.go | 9 +++++++++ widgets/compose.go | 9 +++++++++ worker/imap/flags.go | 32 ++++++++++++++++++++++++++++++++ worker/imap/worker.go | 2 ++ worker/maildir/message.go | 20 ++++++++++++++++++++ worker/maildir/worker.go | 35 +++++++++++++++++++++++++++++++++++ worker/notmuch/message.go | 33 +++++++++++++++++++++++++++++++++ worker/notmuch/worker.go | 27 +++++++++++++++++++++++++++ worker/types/messages.go | 6 ++++++ 11 files changed, 179 insertions(+) diff --git a/commands/compose/send.go b/commands/compose/send.go index 36b9fd8..59ae5d0 100644 --- a/commands/compose/send.go +++ b/commands/compose/send.go @@ -244,6 +244,7 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error { case *types.Done: aerc.PushStatus("Message sent.", 10*time.Second) r.Close() + composer.SetSent() composer.Close() case *types.Error: aerc.PushError(" " + msg.Error.Error()) @@ -256,6 +257,7 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error { w.Close() } else { aerc.PushStatus("Message sent.", 10*time.Second) + composer.SetSent() composer.Close() } }() diff --git a/commands/msg/reply.go b/commands/msg/reply.go index 8e0feef..28ce245 100644 --- a/commands/msg/reply.go +++ b/commands/msg/reply.go @@ -157,6 +157,10 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error { tab.Content.Invalidate() }) + composer.OnClose(func(c *widgets.Composer) { + store.Answered([]uint32{msg.Uid}, c.Sent(), nil) + }) + return nil } diff --git a/lib/msgstore.go b/lib/msgstore.go index 7dc2689..86215a7 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -342,6 +342,15 @@ func (store *MessageStore) Read(uids []uint32, read bool, }, cb) } +func (store *MessageStore) Answered(uids []uint32, answered bool, + cb func(msg types.WorkerMessage)) { + + store.worker.PostAction(&types.AnsweredMessages{ + Answered: answered, + Uids: uids, + }, cb) +} + func (store *MessageStore) Uids() []uint32 { if store.filter { return store.results diff --git a/widgets/compose.go b/widgets/compose.go index 4281941..01b8dd8 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -52,6 +52,7 @@ type Composer struct { layout HeaderLayout focusable []ui.MouseableDrawableInteractive focused int + sent bool onClose []func(ti *Composer) @@ -163,6 +164,14 @@ func buildComposeHeader(conf *config.AercConfig, cmpl *completer.Completer, return layout, editors, focusable } +func (c *Composer) SetSent() { + c.sent = true +} + +func (c *Composer) Sent() bool { + return c.sent +} + // Note: this does not reload the editor. You must call this before the first // Draw() call. func (c *Composer) SetContents(reader io.Reader) *Composer { diff --git a/worker/imap/flags.go b/worker/imap/flags.go index 17c9393..2625086 100644 --- a/worker/imap/flags.go +++ b/worker/imap/flags.go @@ -44,6 +44,38 @@ func (imapw *IMAPWorker) handleDeleteMessages(msg *types.DeleteMessages) { } } +func (imapw *IMAPWorker) handleAnsweredMessages(msg *types.AnsweredMessages) { + item := imap.FormatFlagsOp(imap.AddFlags, true) + flags := []interface{}{imap.AnsweredFlag} + if !msg.Answered { + item = imap.FormatFlagsOp(imap.RemoveFlags, true) + flags = []interface{}{imap.AnsweredFlag} + } + uids := toSeqSet(msg.Uids) + emitErr := func(err error) { + imapw.worker.PostMessage(&types.Error{ + Message: types.RespondTo(msg), + Error: err, + }, nil) + } + if err := imapw.client.UidStore(uids, item, flags, nil); err != nil { + emitErr(err) + return + } + imapw.worker.PostAction(&types.FetchMessageHeaders{ + Uids: msg.Uids, + }, func(_msg types.WorkerMessage) { + switch m := _msg.(type) { + case *types.Error: + err := fmt.Errorf("handleAnsweredMessages: %v", m.Error) + imapw.worker.Logger.Printf("could not fetch headers: %s", err) + emitErr(err) + case *types.Done: + imapw.worker.PostMessage(&types.Done{types.RespondTo(msg)}, nil) + } + }) +} + func (imapw *IMAPWorker) handleReadMessages(msg *types.ReadMessages) { item := imap.FormatFlagsOp(imap.AddFlags, true) flags := []interface{}{imap.SeenFlag} diff --git a/worker/imap/worker.go b/worker/imap/worker.go index 45a2269..a43ac49 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -175,6 +175,8 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { w.handleDeleteMessages(msg) case *types.ReadMessages: w.handleReadMessages(msg) + case *types.AnsweredMessages: + w.handleAnsweredMessages(msg) case *types.CopyMessages: w.handleCopyMessages(msg) case *types.AppendMessage: diff --git a/worker/maildir/message.go b/worker/maildir/message.go index 9d622b4..5c6c930 100644 --- a/worker/maildir/message.go +++ b/worker/maildir/message.go @@ -53,6 +53,26 @@ func (m Message) SetFlags(flags []maildir.Flag) error { return m.dir.SetFlags(m.key, flags) } +// MarkReplied either adds or removes the maildir.FlagReplied flag from the +// message. +func (m Message) MarkReplied(answered bool) error { + flags, err := m.Flags() + if err != nil { + return fmt.Errorf("could not read previous flags: %v", err) + } + if answered { + flags = append(flags, maildir.FlagReplied) + return m.SetFlags(flags) + } + var newFlags []maildir.Flag + for _, flag := range flags { + if flag != maildir.FlagReplied { + newFlags = append(newFlags, flag) + } + } + return m.SetFlags(newFlags) +} + // MarkRead either adds or removes the maildir.FlagSeen flag from the message. func (m Message) MarkRead(seen bool) error { flags, err := m.Flags() diff --git a/worker/maildir/worker.go b/worker/maildir/worker.go index dbdb925..f14672e 100644 --- a/worker/maildir/worker.go +++ b/worker/maildir/worker.go @@ -195,6 +195,8 @@ func (w *Worker) handleMessage(msg types.WorkerMessage) error { return w.handleDeleteMessages(msg) case *types.ReadMessages: return w.handleReadMessages(msg) + case *types.AnsweredMessages: + return w.handleAnsweredMessages(msg) case *types.CopyMessages: return w.handleCopyMessages(msg) case *types.AppendMessage: @@ -438,6 +440,39 @@ func (w *Worker) handleDeleteMessages(msg *types.DeleteMessages) error { return nil } +func (w *Worker) handleAnsweredMessages(msg *types.AnsweredMessages) error { + for _, uid := range msg.Uids { + m, err := w.c.Message(*w.selected, uid) + if err != nil { + w.worker.Logger.Printf("could not get message: %v", err) + w.err(msg, err) + continue + } + if err := m.MarkReplied(msg.Answered); err != nil { + w.worker.Logger.Printf( + "could not mark message as answered: %v", err) + w.err(msg, err) + continue + } + info, err := m.MessageInfo() + if err != nil { + w.worker.Logger.Printf("could not get message info: %v", err) + w.err(msg, err) + continue + } + + w.worker.PostMessage(&types.MessageInfo{ + Message: types.RespondTo(msg), + Info: info, + }, nil) + + w.worker.PostMessage(&types.DirectoryInfo{ + Info: w.getDirectoryInfo(w.selectedName), + }, nil) + } + return nil +} + func (w *Worker) handleReadMessages(msg *types.ReadMessages) error { for _, uid := range msg.Uids { m, err := w.c.Message(*w.selected, uid) diff --git a/worker/notmuch/message.go b/worker/notmuch/message.go index ec1adb9..3fd2fb6 100644 --- a/worker/notmuch/message.go +++ b/worker/notmuch/message.go @@ -64,6 +64,39 @@ func (m *Message) NewBodyPartReader(requestedParts []int) (io.Reader, error) { return lib.FetchEntityPartReader(msg, requestedParts) } +// MarkAnswered either adds or removes the "replied" tag from the message. +func (m *Message) MarkAnswered(answered bool) error { + haveReplied := false + tags, err := m.Tags() + if err != nil { + return err + } + for _, t := range tags { + if t == "replied" { + haveReplied = true + break + } + } + if haveReplied == answered { + // we already have the desired state + return nil + } + + if haveAnswered { + err := m.RemoveTag("replied") + if err != nil { + return err + } + return nil + } + + err = m.AddTag("replied") + if err != nil { + return err + } + return nil +} + // MarkRead either adds or removes the maildir.FlagSeen flag from the message. func (m *Message) MarkRead(seen bool) error { haveUnread := false diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go index 9929648..b226269 100644 --- a/worker/notmuch/worker.go +++ b/worker/notmuch/worker.go @@ -363,6 +363,33 @@ func (w *worker) handleFetchFullMessages(msg *types.FetchFullMessages) error { return nil } +func (w *worker) handleAnsweredMessages(msg *types.AnsweredMessages) error { + for _, uid := range msg.Uids { + m, err := w.msgFromUid(uid) + if err != nil { + w.w.Logger.Printf("could not get message: %v", err) + w.err(msg, err) + continue + } + if err := m.MarkAnswered(msg.Answered); err != nil { + w.w.Logger.Printf("could not mark message as answered: %v", err) + w.err(msg, err) + continue + } + err = w.emitMessageInfo(m, msg) + if err != nil { + w.w.Logger.Printf(err.Error()) + w.err(msg, err) + continue + } + } + if err := w.emitDirectoryInfo(w.currentQueryName); err != nil { + w.w.Logger.Printf(err.Error()) + } + w.done(msg) + return nil +} + func (w *worker) handleReadMessages(msg *types.ReadMessages) error { for _, uid := range msg.Uids { m, err := w.msgFromUid(uid) diff --git a/worker/types/messages.go b/worker/types/messages.go index f1ef36e..475a7aa 100644 --- a/worker/types/messages.go +++ b/worker/types/messages.go @@ -120,6 +120,12 @@ type ReadMessages struct { Uids []uint32 } +type AnsweredMessages struct { + Message + Answered bool + Uids []uint32 +} + type CopyMessages struct { Message Destination string