aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Cox <me@jasoncarloscox.com>2024-01-25 08:25:51 -0500
committerRobin Jarry <robin@jarry.cc>2024-01-26 21:36:15 +0100
commit11b035f120969758d1b08dddd761f9375504de55 (patch)
treebf028d7155ceb45deba3461cb73ed74413f3d965
parent0aab8ac318f6dee479afba13d09a5d1d1c0baa91 (diff)
downloadaerc-11b035f120969758d1b08dddd761f9375504de55.tar.gz
flags: add support for draft flag
Support the draft flag wherever flags are used. Automatically set it when postponing a message, and allow recalling a message without the -f flag if it has the draft flag set, regardless of what folder it's in. Notmuch doesn't seem to pick up on the draft flag when indexing even though the flag is set on the maildir file. Explicitly set all tags corresponding to set flags when appending a message in notmuch. Changelog-added: Support the `draft` flag. Signed-off-by: Jason Cox <me@jasoncarloscox.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--commands/account/search.go1
-rw-r--r--commands/completion_helpers.go2
-rw-r--r--commands/compose/postpone.go2
-rw-r--r--commands/msg/read.go5
-rw-r--r--commands/msg/recall.go12
-rw-r--r--config/templates.go1
-rw-r--r--config/ui.go3
-rw-r--r--doc/aerc-config.5.scd7
-rw-r--r--doc/aerc-search.1.scd2
-rw-r--r--doc/aerc-templates.7.scd3
-rw-r--r--doc/aerc.1.scd2
-rw-r--r--lib/state/templates.go10
-rw-r--r--models/models.go3
-rw-r--r--models/templates.go1
-rw-r--r--worker/imap/imap.go2
-rw-r--r--worker/jmap/jmap.go5
-rw-r--r--worker/lib/maildir.go4
-rw-r--r--worker/notmuch/notmuch.go3
-rw-r--r--worker/notmuch/worker.go27
19 files changed, 82 insertions, 13 deletions
diff --git a/commands/account/search.go b/commands/account/search.go
index be3b125f..0246a9eb 100644
--- a/commands/account/search.go
+++ b/commands/account/search.go
@@ -73,6 +73,7 @@ var flagValues = map[string]models.Flags{
"seen": models.SeenFlag,
"answered": models.AnsweredFlag,
"flagged": models.FlaggedFlag,
+ "draft": models.DraftFlag,
}
func (s *SearchFilter) ParseFlag(arg string) error {
diff --git a/commands/completion_helpers.go b/commands/completion_helpers.go
index e4556594..b7ba6780 100644
--- a/commands/completion_helpers.go
+++ b/commands/completion_helpers.go
@@ -45,7 +45,7 @@ func GetAddress(search string) []string {
// GetFlagList returns a list of available flags for completion
func GetFlagList() []string {
- return []string{"Seen", "Answered", "Flagged"}
+ return []string{"Seen", "Answered", "Flagged", "Draft"}
}
// GetDateList returns a list of date terms for completion
diff --git a/commands/compose/postpone.go b/commands/compose/postpone.go
index a869417b..3c23eda3 100644
--- a/commands/compose/postpone.go
+++ b/commands/compose/postpone.go
@@ -108,7 +108,7 @@ func (p Postpone) Execute(args []string) error {
}
store.Append(
targetFolder,
- models.SeenFlag,
+ models.SeenFlag|models.DraftFlag,
time.Now(),
buf,
buf.Len(),
diff --git a/commands/msg/read.go b/commands/msg/read.go
index b487d8a8..5b4bd073 100644
--- a/commands/msg/read.go
+++ b/commands/msg/read.go
@@ -41,13 +41,16 @@ func (f *FlagMsg) ParseFlag(arg string) error {
case "flagged":
f.Flag = models.FlaggedFlag
f.FlagName = "flagged"
+ case "draft":
+ f.Flag = models.DraftFlag
+ f.FlagName = "draft"
default:
return fmt.Errorf("Unknown flag %q", arg)
}
return nil
}
-var validFlags = []string{"seen", "answered", "flagged"}
+var validFlags = []string{"seen", "answered", "flagged", "draft"}
func (*FlagMsg) CompleteFlag(arg string) []string {
return commands.FilterList(validFlags, arg, nil)
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 059fe558..e5cd2ed1 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -15,6 +15,7 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/log"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -44,10 +45,6 @@ func (r Recall) Execute(args []string) error {
if acct == nil {
return errors.New("No account selected")
}
- if acct.SelectedDirectory() != acct.AccountConfig().Postpone && !r.Force {
- return errors.New("Use -f to recall from outside the " +
- acct.AccountConfig().Postpone + " directory.")
- }
store := widget.Store()
if store == nil {
return errors.New("Cannot perform action. Messages still loading")
@@ -57,6 +54,13 @@ func (r Recall) Execute(args []string) error {
if err != nil {
return errors.Wrap(err, "Recall failed")
}
+
+ if acct.SelectedDirectory() != acct.AccountConfig().Postpone &&
+ !msgInfo.Flags.Has(models.DraftFlag) && !r.Force {
+ return errors.New("Use -f to recall non-draft messages from outside the " +
+ acct.AccountConfig().Postpone + " directory.")
+ }
+
log.Debugf("Recalling message <%s>", msgInfo.Envelope.MessageId)
addTab := func(composer *app.Composer) {
diff --git a/config/templates.go b/config/templates.go
index c5d11478..61c411fb 100644
--- a/config/templates.go
+++ b/config/templates.go
@@ -94,6 +94,7 @@ func (d *dummyData) HasAttachment() bool { return true }
func (d *dummyData) IsRecent() bool { return false }
func (d *dummyData) IsUnread() bool { return false }
func (d *dummyData) IsFlagged() bool { return false }
+func (d *dummyData) IsDraft() bool { return false }
func (d *dummyData) IsMarked() bool { return false }
func (d *dummyData) MessageId() string { return "123456789@foo.org" }
func (d *dummyData) Size() int { return 420 }
diff --git a/config/ui.go b/config/ui.go
index ed0b4a33..83b45230 100644
--- a/config/ui.go
+++ b/config/ui.go
@@ -55,9 +55,10 @@ type UIConfig struct {
IconReplied string `ini:"icon-replied" default:"r"`
IconNew string `ini:"icon-new" default:"N"`
IconOld string `ini:"icon-old" default:"O"`
+ IconDraft string `ini:"icon-draft" default:"d"`
IconFlagged string `ini:"icon-flagged" default:"!"`
IconMarked string `ini:"icon-marked" default:"*"`
- IconDeleted string `ini:"icon-deleted" default:"D"`
+ IconDeleted string `ini:"icon-deleted" default:"X"`
DirListDelay time.Duration `ini:"dirlist-delay" default:"200ms"`
DirListTree bool `ini:"dirlist-tree"`
DirListCollapse int `ini:"dirlist-collapse"`
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 043b738a..1aa9da5f 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -405,10 +405,15 @@ These options are configured in the *[ui]* section of _aerc.conf_.
Default: _\*_
+*icon-draft* = _<string>_
+ The icon to display in *column-flags* when the message is a draft.
+
+ Default: _d_
+
*icon-deleted* = _<string>_
The icon to display in *column-flags* when the message has been deleted.
- Default: _D_
+ Default: _X_
*fuzzy-complete* = _true_|_false_
When typing a command or option, the popover will now show not only the
diff --git a/doc/aerc-search.1.scd b/doc/aerc-search.1.scd
index 161a632f..fa630c1d 100644
--- a/doc/aerc-search.1.scd
+++ b/doc/aerc-search.1.scd
@@ -34,6 +34,8 @@ This syntax is common to all backends.
Replied messages
_Flagged_
Flagged messages
+ _Draft_
+ Draft messages
*-H*: Search in the headers of the messages, for a specific header
Syntax: _Header: Value_
diff --git a/doc/aerc-templates.7.scd b/doc/aerc-templates.7.scd
index 160fb77f..6e87c616 100644
--- a/doc/aerc-templates.7.scd
+++ b/doc/aerc-templates.7.scd
@@ -116,7 +116,8 @@ available always.
{{.Flags | join ""}}
```
-*IsReplied*, *HasAttachment*, *IsFlagged*, *IsRecent*, *IsUnread*, *IsMarked*
+*IsReplied*, *HasAttachment*, *IsFlagged*, *IsRecent*, *IsUnread*, *IsMarked*,
+*IsDraft*
Individual boolean flags. not available when composing, replying nor
forwarding.
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index d77bbf03..1ee61060 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -418,6 +418,8 @@ message list, the message in the message viewer, etc).
Message has been answered
_Flagged_
Message is flagged for urgent/special attention
+ _Draft_
+ Message is a draft
*:unflag* [*-t*] _<flag>_
Operates exactly like *:flag*, defaulting to unsetting (disabling) flags.
diff --git a/lib/state/templates.go b/lib/state/templates.go
index ac701e9f..d039f1b6 100644
--- a/lib/state/templates.go
+++ b/lib/state/templates.go
@@ -393,6 +393,9 @@ func (d *templateData) Flags() []string {
default:
flags = append(flags, d.ui().IconOld) // message is unread and old
}
+ if d.info.Flags.Has(models.DraftFlag) {
+ flags = append(flags, d.ui().IconDraft)
+ }
if d.info.Flags.Has(models.DeletedFlag) {
flags = append(flags, d.ui().IconDeleted)
}
@@ -452,6 +455,13 @@ func (d *templateData) IsFlagged() bool {
return false
}
+func (d *templateData) IsDraft() bool {
+ if d.info != nil && d.info.Flags.Has(models.DraftFlag) {
+ return true
+ }
+ return false
+}
+
func (d *templateData) IsMarked() bool {
return d.marked
}
diff --git a/models/models.go b/models/models.go
index 55dd2347..80bc0db1 100644
--- a/models/models.go
+++ b/models/models.go
@@ -30,6 +30,9 @@ const (
// FlaggedFlag marks a message with a user flag
FlaggedFlag
+
+ // DraftFlag marks a message as a draft
+ DraftFlag
)
func (f Flags) Has(flags Flags) bool {
diff --git a/models/templates.go b/models/templates.go
index a6a85371..5caed158 100644
--- a/models/templates.go
+++ b/models/templates.go
@@ -38,6 +38,7 @@ type TemplateData interface {
IsRecent() bool
IsUnread() bool
IsMarked() bool
+ IsDraft() bool
MessageId() string
Role() string
Size() int
diff --git a/worker/imap/imap.go b/worker/imap/imap.go
index 82ee2e2e..fbdcb2b9 100644
--- a/worker/imap/imap.go
+++ b/worker/imap/imap.go
@@ -85,6 +85,7 @@ var imapToFlag = map[string]models.Flags{
imap.AnsweredFlag: models.AnsweredFlag,
imap.DeletedFlag: models.DeletedFlag,
imap.FlaggedFlag: models.FlaggedFlag,
+ imap.DraftFlag: models.DraftFlag,
}
var flagToImap = map[models.Flags]string{
@@ -93,6 +94,7 @@ var flagToImap = map[models.Flags]string{
models.AnsweredFlag: imap.AnsweredFlag,
models.DeletedFlag: imap.DeletedFlag,
models.FlaggedFlag: imap.FlaggedFlag,
+ models.DraftFlag: imap.DraftFlag,
}
func translateImapFlags(imapFlags []string) models.Flags {
diff --git a/worker/jmap/jmap.go b/worker/jmap/jmap.go
index 8012b6e4..bb0f75f9 100644
--- a/worker/jmap/jmap.go
+++ b/worker/jmap/jmap.go
@@ -67,6 +67,9 @@ func flagsToKeywords(flags models.Flags) map[string]bool {
if flags.Has(models.FlaggedFlag) {
kw["$flagged"] = true
}
+ if flags.Has(models.DraftFlag) {
+ kw["$draft"] = true
+ }
return kw
}
@@ -81,6 +84,8 @@ func keywordsToFlags(kw map[string]bool) models.Flags {
f |= models.AnsweredFlag
case "$flagged":
f |= models.FlaggedFlag
+ case "$draft":
+ f |= models.DraftFlag
}
}
}
diff --git a/worker/lib/maildir.go b/worker/lib/maildir.go
index 43d3b820..a4a0ecd5 100644
--- a/worker/lib/maildir.go
+++ b/worker/lib/maildir.go
@@ -118,7 +118,7 @@ var MaildirToFlag = map[maildir.Flag]models.Flags{
maildir.FlagSeen: models.SeenFlag,
maildir.FlagTrashed: models.DeletedFlag,
maildir.FlagFlagged: models.FlaggedFlag,
- // maildir.FlagDraft Flag = 'D'
+ maildir.FlagDraft: models.DraftFlag,
// maildir.FlagPassed Flag = 'P'
}
@@ -127,7 +127,7 @@ var FlagToMaildir = map[models.Flags]maildir.Flag{
models.SeenFlag: maildir.FlagSeen,
models.DeletedFlag: maildir.FlagTrashed,
models.FlaggedFlag: maildir.FlagFlagged,
- // maildir.FlagDraft Flag = 'D'
+ models.DraftFlag: maildir.FlagDraft,
// maildir.FlagPassed Flag = 'P'
}
diff --git a/worker/notmuch/notmuch.go b/worker/notmuch/notmuch.go
index 5610cac0..1d064fd2 100644
--- a/worker/notmuch/notmuch.go
+++ b/worker/notmuch/notmuch.go
@@ -8,17 +8,20 @@ import "git.sr.ht/~rjarry/aerc/models"
var tagToFlag = map[string]models.Flags{
"unread": models.SeenFlag,
"replied": models.AnsweredFlag,
+ "draft": models.DraftFlag,
"flagged": models.FlaggedFlag,
}
var flagToTag = map[models.Flags]string{
models.SeenFlag: "unread",
models.AnsweredFlag: "replied",
+ models.DraftFlag: "draft",
models.FlaggedFlag: "flagged",
}
var flagToInvert = map[models.Flags]bool{
models.SeenFlag: true,
models.AnsweredFlag: false,
+ models.DraftFlag: false,
models.FlaggedFlag: false,
}
diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go
index 374a20b7..80a8d720 100644
--- a/worker/notmuch/worker.go
+++ b/worker/notmuch/worker.go
@@ -894,9 +894,16 @@ func (w *worker) handleAppendMessage(msg *types.AppendMessage) error {
return err
}
writer.Close()
- if _, err := w.db.IndexFile(filename); err != nil {
+ id, err := w.db.IndexFile(filename)
+ if err != nil {
return err
}
+
+ err = w.addFlags(id, msg.Flags)
+ if err != nil {
+ return err
+ }
+
w.w.PostMessage(&types.DirectoryInfo{
Info: w.getDirectoryInfo(w.currentQueryName, w.query),
}, nil)
@@ -973,3 +980,21 @@ func (w *worker) processNewMaildirFiles(dir string) error {
return nil
}
+
+func (w *worker) addFlags(id string, flags models.Flags) error {
+ addTags := []string{}
+ removeTags := []string{}
+ for flag, tag := range flagToTag {
+ if !flags.Has(flag) {
+ continue
+ }
+
+ if flagToInvert[flag] {
+ removeTags = append(removeTags, tag)
+ } else {
+ addTags = append(addTags, tag)
+ }
+ }
+
+ return w.db.MsgModifyTags(id, addTags, removeTags)
+}