aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffas <dev@jeffas.io>2020-04-24 11:42:21 +0200
committerDrew DeVault <sir@cmpwn.com>2020-04-24 12:59:21 -0400
commit7f033278eb3afc3b9ae2dca28efe8d4a3514d14a (patch)
treeee02c27cb1dfadd54be0178e91d6f2d5f0cbf4dc
parent447e662057c663f47f5c8a490543b1a52b26bc86 (diff)
downloadaerc-7f033278eb3afc3b9ae2dca28efe8d4a3514d14a.tar.gz
Add postpone command
This command uses the Postpone folder from the account config to save messages to. Messages are saved as though they were sent so have a valid 'to' recipient address and should be able to be read back in for later editing.
-rw-r--r--commands/account/compose.go2
-rw-r--r--commands/compose/postpone.go119
-rw-r--r--commands/msg/forward.go1
-rw-r--r--commands/msg/reply.go2
-rw-r--r--commands/msg/unsubscribe.go1
-rw-r--r--config/binds.conf1
-rw-r--r--config/config.go10
-rw-r--r--doc/aerc-config.5.scd5
-rw-r--r--doc/aerc.1.scd4
-rw-r--r--widgets/aerc.go4
-rw-r--r--widgets/compose.go44
11 files changed, 166 insertions, 27 deletions
diff --git a/commands/account/compose.go b/commands/account/compose.go
index 8115faa2..42d51d51 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,
+ 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
new file mode 100644
index 00000000..44aa4118
--- /dev/null
+++ b/commands/compose/postpone.go
@@ -0,0 +1,119 @@
+package compose
+
+import (
+ "io"
+ "io/ioutil"
+ "time"
+
+ "github.com/emersion/go-imap"
+ "github.com/miolini/datacounter"
+ "github.com/pkg/errors"
+
+ "git.sr.ht/~sircmpwn/aerc/widgets"
+ "git.sr.ht/~sircmpwn/aerc/worker/types"
+)
+
+type Postpone struct{}
+
+func init() {
+ register(Postpone{})
+}
+
+func (Postpone) Aliases() []string {
+ return []string{"postpone"}
+}
+
+func (Postpone) Complete(aerc *widgets.Aerc, args []string) []string {
+ return nil
+}
+
+func (Postpone) Execute(aerc *widgets.Aerc, args []string) error {
+ if len(args) != 1 {
+ return errors.New("Usage: postpone")
+ }
+ composer, _ := aerc.SelectedTab().(*widgets.Composer)
+ config := composer.Config()
+
+ if config.Postpone == "" {
+ return errors.New("No Postpone location configured")
+ }
+
+ aerc.Logger().Println("Postponing mail")
+
+ header, _, err := composer.PrepareHeader()
+ if err != nil {
+ return errors.Wrap(err, "PrepareHeader")
+ }
+ header.SetContentType("text/plain", map[string]string{"charset": "UTF-8"})
+ header.Set("Content-Transfer-Encoding", "quoted-printable")
+ worker := composer.Worker()
+ dirs := aerc.SelectedAccount().Directories().List()
+ alreadyCreated := false
+ for _, dir := range dirs {
+ if dir == config.Postpone {
+ alreadyCreated = true
+ break
+ }
+ }
+
+ errChan := make(chan string)
+
+ // run this as a goroutine so we can make other progress. The message
+ // will be saved once the directory is created.
+ go func() {
+ errStr := <-errChan
+ if errStr != "" {
+ aerc.PushError(" " + errStr)
+ return
+ }
+
+ aerc.RemoveTab(composer)
+ ctr := datacounter.NewWriterCounter(ioutil.Discard)
+ err = composer.WriteMessage(header, ctr)
+ if err != nil {
+ aerc.PushError(errors.Wrap(err, "WriteMessage").Error())
+ composer.Close()
+ return
+ }
+ nbytes := int(ctr.Count())
+ r, w := io.Pipe()
+ worker.PostAction(&types.AppendMessage{
+ Destination: config.Postpone,
+ Flags: []string{imap.SeenFlag},
+ Date: time.Now(),
+ Reader: r,
+ Length: int(nbytes),
+ }, func(msg types.WorkerMessage) {
+ switch msg := msg.(type) {
+ case *types.Done:
+ aerc.PushStatus("Message postponed.", 10*time.Second)
+ r.Close()
+ composer.Close()
+ case *types.Error:
+ aerc.PushError(" " + msg.Error.Error())
+ r.Close()
+ composer.Close()
+ }
+ })
+ composer.WriteMessage(header, w)
+ w.Close()
+ }()
+
+ if !alreadyCreated {
+ // to synchronise the creating of the directory
+ worker.PostAction(&types.CreateDirectory{
+ Directory: config.Postpone,
+ }, func(msg types.WorkerMessage) {
+ switch msg := msg.(type) {
+ case *types.Done:
+ errChan <- ""
+ case *types.Error:
+ errChan <- msg.Error.Error()
+ }
+ })
+ } else {
+ errChan <- ""
+ }
+
+ return nil
+}
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index c51949ed..833eb9fd 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -70,7 +70,6 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error {
"To": to,
"Subject": subject,
}
-
original := models.OriginalMail{}
addTab := func() (*widgets.Composer, error) {
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index c5ae1b60..291fc4b7 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -124,7 +124,7 @@ func (reply) 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(),
+ 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/unsubscribe.go b/commands/msg/unsubscribe.go
index 1bf3a836..1a2dd373 100644
--- a/commands/msg/unsubscribe.go
+++ b/commands/msg/unsubscribe.go
@@ -90,6 +90,7 @@ func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error {
}
composer, err := widgets.NewComposer(
aerc,
+ acct,
aerc.Config(),
acct.AccountConfig(),
acct.Worker(),
diff --git a/config/binds.conf b/config/binds.conf
index 5887203e..abfd77e0 100644
--- a/config/binds.conf
+++ b/config/binds.conf
@@ -90,6 +90,7 @@ $ex = <C-x>
# Keybindings used when reviewing a message to be sent
y = :send<Enter>
n = :abort<Enter>
+p = :postpone<Enter>
q = :abort<Enter>
e = :edit<Enter>
a = :attach<space>
diff --git a/config/config.go b/config/config.go
index 5794388a..e00518cb 100644
--- a/config/config.go
+++ b/config/config.go
@@ -70,6 +70,7 @@ type AccountConfig struct {
Archive string
CopyTo string
Default string
+ Postpone string
From string
Name string
Source string
@@ -171,10 +172,11 @@ func loadAccountConfig(path string) ([]AccountConfig, error) {
}
sec := file.Section(_sec)
account := AccountConfig{
- Archive: "Archive",
- Default: "INBOX",
- Name: _sec,
- Params: make(map[string]string),
+ Archive: "Archive",
+ Default: "INBOX",
+ Postpone: "Drafts",
+ Name: _sec,
+ Params: make(map[string]string),
}
if err = sec.MapTo(&account); err != nil {
return nil, err
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 36ac9c66..c309f2f6 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -402,6 +402,11 @@ Note that many of these configuration options are written for you, such as
Default: none
+*postpone*
+ Specifies the folder to save *postpone*d messages to.
+
+ Default: Drafts
+
*source*
Specifies the source for reading incoming emails on this account. This key
is required for all accounts. It should be a connection string, and the
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index d2e52a95..230361db 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -299,6 +299,10 @@ message list, the message in the message viewer, etc).
*next-field*, *prev-field*
Cycles between input fields in the compose window.
+*postpone*
+ Saves the current state of the message to the *postpone* folder for the
+ current account.
+
*save* [-p] <path>
Saves the selected message part to the specified path. If -p is selected,
aerc will create any missing directories in the specified path. If the path
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 8307bd07..779e3868 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -293,6 +293,8 @@ func (aerc *Aerc) SelectedAccount() *AccountView {
return tab
case *MessageViewer:
return tab.SelectedAccount()
+ case *Composer:
+ return tab.Account()
}
return nil
}
@@ -494,7 +496,7 @@ func (aerc *Aerc) Mailto(addr *url.URL) error {
defaults[header] = strings.Join(vals, ",")
}
}
- composer, err := NewComposer(aerc, aerc.Config(),
+ composer, err := NewComposer(aerc, acct, aerc.Config(),
acct.AccountConfig(), acct.Worker(), "", defaults, models.OriginalMail{})
if err != nil {
return nil
diff --git a/widgets/compose.go b/widgets/compose.go
index 9ceabf6c..66877cc1 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -32,9 +32,10 @@ import (
type Composer struct {
editors map[string]*headerEditor
- acct *config.AccountConfig
- config *config.AercConfig
- aerc *Aerc
+ acctConfig *config.AccountConfig
+ config *config.AercConfig
+ acct *AccountView
+ aerc *Aerc
attachments []string
date time.Time
@@ -57,7 +58,7 @@ type Composer struct {
width int
}
-func NewComposer(aerc *Aerc, conf *config.AercConfig,
+func NewComposer(aerc *Aerc, acct *AccountView, conf *config.AercConfig,
acct *config.AccountConfig, worker *types.Worker, template string,
defaults map[string]string, original models.OriginalMail) (*Composer, error) {
@@ -65,7 +66,7 @@ func NewComposer(aerc *Aerc, conf *config.AercConfig,
defaults = make(map[string]string)
}
if from := defaults["From"]; from == "" {
- defaults["From"] = acct.From
+ defaults["From"] = acctConfig.From
}
templateData := templates.ParseTemplateData(defaults, original)
@@ -82,16 +83,17 @@ func NewComposer(aerc *Aerc, conf *config.AercConfig,
}
c := &Composer{
- acct: acct,
- aerc: aerc,
- config: conf,
- date: time.Now(),
- defaults: defaults,
- editors: editors,
- email: email,
- layout: layout,
- msgId: mail.GenerateMessageID(),
- worker: worker,
+ acct: acct,
+ acctConfig: acctConfig,
+ aerc: aerc,
+ config: conf,
+ date: time.Now(),
+ defaults: defaults,
+ editors: editors,
+ email: email,
+ layout: layout,
+ msgId: mail.GenerateMessageID(),
+ worker: worker,
// You have to backtab to get to "From", since you usually don't edit it
focused: 1,
focusable: focusable,
@@ -215,7 +217,7 @@ func (c *Composer) AddTemplate(template string, data interface{}) error {
func (c *Composer) AddSignature() {
var signature []byte
- if c.acct.SignatureCmd != "" {
+ if c.acctConfig.SignatureCmd != "" {
var err error
signature, err = c.readSignatureFromCmd()
if err != nil {
@@ -228,7 +230,7 @@ func (c *Composer) AddSignature() {
}
func (c *Composer) readSignatureFromCmd() ([]byte, error) {
- sigCmd := c.acct.SignatureCmd
+ sigCmd := c.acctConfig.SignatureCmd
cmd := exec.Command("sh", "-c", sigCmd)
signature, err := cmd.Output()
if err != nil {
@@ -238,7 +240,7 @@ func (c *Composer) readSignatureFromCmd() ([]byte, error) {
}
func (c *Composer) readSignatureFromFile() []byte {
- sigFile := c.acct.SignatureFile
+ sigFile := c.acctConfig.SignatureFile
if sigFile == "" {
return nil
}
@@ -354,6 +356,10 @@ func (c *Composer) Focus(focus bool) {
}
func (c *Composer) Config() *config.AccountConfig {
+ return c.acctConfig
+}
+
+func (c *Composer) Account() *AccountView {
return c.acct
}
@@ -771,7 +777,7 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage {
} else {
// TODO: source this from actual keybindings?
grid.AddChild(ui.NewText(
- "Send this email? [y]es/[n]o/[e]dit/[a]ttach")).At(0, 0)
+ "Send this email? [y]es/[n]o/[p]ostpone/[e]dit/[a]ttach")).At(0, 0)
grid.AddChild(ui.NewText("Attachments:").
Reverse(true)).At(1, 0)
if len(composer.attachments) == 0 {