aboutsummaryrefslogtreecommitdiffstats
path: root/commands/msg
diff options
context:
space:
mode:
authorARaspiK <araspik@protonmail.com>2020-07-05 14:29:52 +0000
committerReto Brunner <reto@labrat.space>2020-07-08 09:13:03 +0200
commit0535f6333f2f5d13469fc315a65c53ff8a5e83f3 (patch)
treefa3adf96b16d4a0296c04b6a17f5433c03fbc8de /commands/msg
parentfda3f43e7c5e5a175a01dd3e5b8637b1ecb30c51 (diff)
downloadaerc-0535f6333f2f5d13469fc315a65c53ff8a5e83f3.tar.gz
Add additional flagging functionality
More mail flags can now be set, unset, and toggled, not just the read/seen flag. This functionality is implemented with a new `:flag` and `:unflag` command, which are extensions to the matching `:read` and `:unread` commands, adding support for different flags. In fact, the `read`/`unread` commands are now recognized aliases to `flag`/`unflag`. The new commands are also well documented in aerc(1). The change mostly extends the previous read/unread setting functionality by adding a selection for the flag to change.
Diffstat (limited to 'commands/msg')
-rw-r--r--commands/msg/read.go221
1 files changed, 135 insertions, 86 deletions
diff --git a/commands/msg/read.go b/commands/msg/read.go
index 1e264c27..08881894 100644
--- a/commands/msg/read.go
+++ b/commands/msg/read.go
@@ -1,9 +1,9 @@
package msg
import (
- "errors"
"sync"
"time"
+ "fmt"
"git.sr.ht/~sircmpwn/getopt"
@@ -13,36 +13,106 @@ import (
"git.sr.ht/~sircmpwn/aerc/worker/types"
)
-type Read struct{}
+type FlagMsg struct{}
func init() {
- register(Read{})
+ register(FlagMsg{})
}
-func (Read) Aliases() []string {
- return []string{"read", "unread"}
+func (FlagMsg) Aliases() []string {
+ return []string{"flag", "unflag", "read", "unread"}
}
-func (Read) Complete(aerc *widgets.Aerc, args []string) []string {
+func (FlagMsg) Complete(aerc *widgets.Aerc, args []string) []string {
return nil
}
-func (Read) Execute(aerc *widgets.Aerc, args []string) error {
- opts, optind, err := getopt.Getopts(args, "t")
+// If this was called as 'flag' or 'unflag', without the toggle (-t)
+// option, then it will flag the corresponding messages with the given
+// flag. If the toggle option was given, it will individually toggle
+// the given flag for the corresponding messages.
+//
+// If this was called as 'read' or 'unread', it has the same effect as
+// 'flag' or 'unflag', respectively, but the 'Seen' flag is affected.
+func (FlagMsg) Execute(aerc *widgets.Aerc, args []string) error {
+
+ // The flag to change
+ var flag models.Flag
+ // User-readable name of the flag to change
+ var flagName string
+ // Whether to toggle the flag (true) or to enable/disable it (false)
+ var toggle bool
+ // Whether to enable (true) or disable (false) the flag
+ enable := (args[0] == "read" || args[0] == "flag")
+ // User-readable name for the action being performed
+ var actionName string
+ // Getopt option string, varies by command name
+ var getoptString string
+ // Help message to provide on parsing failure
+ var helpMessage string
+ // Used during parsing to prevent choosing a flag muliple times
+ // A default flag will be used if this is false
+ flagChosen := false
+
+ if args[0] == "read" || args[0] == "unread" {
+ flag = models.SeenFlag
+ flagName = "read"
+ getoptString = "t"
+ helpMessage = "Usage: " + args[0] + " [-t]"
+ } else { // 'flag' / 'unflag'
+ flag = models.FlaggedFlag
+ flagName = "flagged"
+ getoptString = "tax:"
+ helpMessage = "Usage: " + args[0] + " [-t] [-a | -x <flag>]"
+ }
+
+ opts, optind, err := getopt.Getopts(args, getoptString)
if err != nil {
return err
}
- if optind != len(args) {
- return errors.New("Usage: " + args[0] + " [-t]")
- }
- var toggle bool
-
for _, opt := range opts {
switch opt.Option {
case 't':
toggle = true
+ case 'a':
+ if flagChosen {
+ return fmt.Errorf("Cannot choose a flag multiple times! " + helpMessage)
+ }
+ flag = models.AnsweredFlag
+ flagName = "answered"
+ flagChosen = true
+ case 'x':
+ if flagChosen {
+ return fmt.Errorf("Cannot choose a flag multiple times! " + helpMessage)
+ }
+ // TODO: Support all flags?
+ switch opt.Value {
+ case "Seen":
+ flag = models.SeenFlag
+ flagName = "seen"
+ case "Answered":
+ flag = models.AnsweredFlag
+ flagName = "answered"
+ case "Flagged":
+ flag = models.FlaggedFlag
+ flagName = "flagged"
+ default:
+ return fmt.Errorf("Unknown / Prohibited flag \"%v\"", opt.Value)
+ }
+ flagChosen = true
}
}
+ if toggle {
+ actionName = "Toggling"
+ } else if enable {
+ actionName = "Setting"
+ } else {
+ actionName = "Unsetting"
+ }
+ if optind != len(args) {
+ // Any non-option arguments: Error
+ return fmt.Errorf(helpMessage)
+ }
h := newHelper(aerc)
store, err := h.store()
@@ -50,57 +120,68 @@ func (Read) Execute(aerc *widgets.Aerc, args []string) error {
return err
}
- if toggle {
- // ignore command given, simply toggle all the read states
- return submitToggle(aerc, store, h)
- }
- msgUids, err := h.markedOrSelectedUids()
- if err != nil {
- return err
- }
- switch args[0] {
- case "read":
- submitReadChange(aerc, store, msgUids, true)
- case "unread":
- submitReadChange(aerc, store, msgUids, false)
-
- }
- return nil
-}
+ // UIDs of messages to enable or disable the flag for.
+ var toEnable []uint32
+ var toDisable []uint32
-func splitMessages(msgs []*models.MessageInfo) (read []uint32, unread []uint32) {
- for _, m := range msgs {
- var seen bool
- for _, flag := range m.Flags {
- if flag == models.SeenFlag {
- seen = true
- break
+ if toggle {
+ // If toggling, split messages into those that need to
+ // be enabled / disabled.
+ msgs, err := h.messages()
+ if err != nil {
+ return err
+ }
+ for _, m := range msgs {
+ var enabled bool
+ for _, mFlag := range m.Flags {
+ if mFlag == flag {
+ enabled = true
+ break
+ }
}
+ if enabled {
+ toDisable = append(toDisable, m.Uid)
+ } else {
+ toEnable = append(toEnable, m.Uid)
+ }
+ }
+ } else {
+ msgUids, err := h.markedOrSelectedUids()
+ if err != nil {
+ return err
}
- if seen {
- read = append(read, m.Uid)
+ if enable {
+ toEnable = msgUids
} else {
- unread = append(unread, m.Uid)
+ toDisable = msgUids
}
}
- return read, unread
-}
-func submitReadChange(aerc *widgets.Aerc, store *lib.MessageStore,
- uids []uint32, newState bool) {
- store.Read(uids, newState, func(msg types.WorkerMessage) {
- switch msg := msg.(type) {
- case *types.Done:
- aerc.PushStatus(msg_success, 10*time.Second)
- case *types.Error:
- aerc.PushError(" " + msg.Error.Error())
+ var wg sync.WaitGroup
+ success := true
+
+ if len(toEnable) != 0 {
+ submitFlagChange(aerc, store, toEnable, flag, true, &wg, &success)
+ }
+ if len(toDisable) != 0 {
+ submitFlagChange(aerc, store, toDisable, flag, false, &wg, &success)
+ }
+
+ // We need to do flagging in the background, else we block the main thread
+ go func() {
+ wg.Wait()
+ if success {
+ aerc.PushStatus(actionName + " flag '" + flagName + "' successful", 10*time.Second)
}
- })
+ }()
+
+ return nil
}
-func submitReadChangeWg(aerc *widgets.Aerc, store *lib.MessageStore,
- uids []uint32, newState bool, wg *sync.WaitGroup, success *bool) {
- store.Read(uids, newState, func(msg types.WorkerMessage) {
+func submitFlagChange(aerc *widgets.Aerc, store *lib.MessageStore,
+ uids []uint32, flag models.Flag, newState bool,
+ wg *sync.WaitGroup, success *bool) {
+ store.Flag(uids, flag, newState, func(msg types.WorkerMessage) {
wg.Add(1)
switch msg := msg.(type) {
case *types.Done:
@@ -112,35 +193,3 @@ func submitReadChangeWg(aerc *widgets.Aerc, store *lib.MessageStore,
}
})
}
-
-func submitToggle(aerc *widgets.Aerc, store *lib.MessageStore, h *helper) error {
- msgs, err := h.messages()
- if err != nil {
- return err
- }
- read, unread := splitMessages(msgs)
-
- var wg sync.WaitGroup
- success := true
-
- if len(read) != 0 {
- newState := false
- submitReadChangeWg(aerc, store, read, newState, &wg, &success)
- }
-
- if len(unread) != 0 {
- newState := true
- submitReadChangeWg(aerc, store, unread, newState, &wg, &success)
- }
- // we need to do that in the background, else we block the main thread
- go func() {
- wg.Wait()
- if success {
- aerc.PushStatus(msg_success, 10*time.Second)
- }
- }()
- return nil
-
-}
-
-const msg_success = "read state set successfully"