Implement (basic form) of :reply

This commit is contained in:
Drew DeVault 2019-05-16 12:15:34 -04:00
parent 2b3e123cb8
commit 475b697bdf
6 changed files with 139 additions and 7 deletions

83
commands/account/reply.go Normal file
View file

@ -0,0 +1,83 @@
package account
import (
"errors"
"fmt"
"strings"
"github.com/emersion/go-imap"
"git.sr.ht/~sircmpwn/aerc2/widgets"
)
func init() {
register("reply", Reply)
}
func Reply(aerc *widgets.Aerc, args []string) error {
if len(args) != 1 {
return errors.New("Usage: reply [-aq]")
}
// TODO: Reply all (w/ getopt)
acct := aerc.SelectedAccount()
msg := acct.Messages().Selected()
acct.Logger().Println("Replying to email " + msg.Envelope.MessageId)
var (
to []string
cc []string
toList []*imap.Address
)
if len(msg.Envelope.ReplyTo) != 0 {
toList = msg.Envelope.ReplyTo
} else {
toList = msg.Envelope.From
}
for _, addr := range toList {
if addr.PersonalName != "" {
to = append(to, fmt.Sprintf("%s <%s@%s>",
addr.PersonalName, addr.MailboxName, addr.HostName))
} else {
to = append(to, fmt.Sprintf("<%s@%s>",
addr.MailboxName, addr.HostName))
}
}
// TODO: Only if reply all
for _, addr := range msg.Envelope.Cc {
if addr.PersonalName != "" {
cc = append(cc, fmt.Sprintf("%s <%s@%s>",
addr.PersonalName, addr.MailboxName, addr.HostName))
} else {
cc = append(cc, fmt.Sprintf("<%s@%s>",
addr.MailboxName, addr.HostName))
}
}
subject := "Re: " + msg.Envelope.Subject
composer := widgets.NewComposer(
aerc.Config(), acct.AccountConfig(), acct.Worker()).
Defaults(map[string]string{
"To": strings.Join(to, ","),
"Cc": strings.Join(cc, ","),
"Subject": subject,
"In-Reply-To": msg.Envelope.MessageId,
}).
FocusTerminal()
tab := aerc.NewTab(composer, subject)
composer.OnSubjectChange(func(subject string) {
if subject == "" {
tab.Name = "New email"
} else {
tab.Name = subject
}
tab.Content.Invalidate()
})
return nil
}

1
go.mod
View file

@ -15,6 +15,7 @@ require (
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c // indirect github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c // indirect
github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41
github.com/mattn/go-isatty v0.0.3 github.com/mattn/go-isatty v0.0.3
github.com/mattn/go-runewidth v0.0.2 github.com/mattn/go-runewidth v0.0.2
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0 github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0

2
go.sum
View file

@ -40,6 +40,8 @@ github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a h1:vLFQnHOnCnmlySdpHAKF
github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a/go.mod h1:Z5mDqe0fxyxn3W2yTxsBAOQqIrXADQIh02wrTnaRM38= github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a/go.mod h1:Z5mDqe0fxyxn3W2yTxsBAOQqIrXADQIh02wrTnaRM38=
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c h1:b11Y3yxg40v2/9KUz76a4mSC1DMlgnPGAt+4pJSgmyU= github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c h1:b11Y3yxg40v2/9KUz76a4mSC1DMlgnPGAt+4pJSgmyU=
github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41 h1:CVsnY46BCLkX9XOhALJ/S7yb9ayc4eqjXSXO3tyB66A=
github.com/martinlindhe/base36 v0.0.0-20190418230009-7c6542dfbb41/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791 h1:PfHMsLQJwoc0ccjK0sam6J0wQo4s8mOuAo2yQGw+T2U= github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791 h1:PfHMsLQJwoc0ccjK0sam6J0wQo4s8mOuAo2yQGw+T2U=

34
lib/msgid.go Normal file
View file

@ -0,0 +1,34 @@
package lib
// TODO: Remove this pending merge into github.com/emersion/go-message
import (
"bytes"
"encoding/binary"
"fmt"
"math/rand"
"os"
"time"
"github.com/martinlindhe/base36"
)
// Generates an RFC 2822-complaint Message-Id based on the informational draft
// "Recommendations for generating Message IDs", for lack of a better
// authoritative source.
func GenerateMessageId() string {
var (
now bytes.Buffer
nonce bytes.Buffer
)
binary.Write(&now, binary.BigEndian, time.Now().UnixNano())
binary.Write(&nonce, binary.BigEndian, rand.Uint64())
hostname, err := os.Hostname()
if err != nil {
hostname = "localhost"
}
return fmt.Sprintf("<%s.%s@%s>",
base36.EncodeBytes(now.Bytes()),
base36.EncodeBytes(nonce.Bytes()),
hostname)
}

View file

@ -84,6 +84,14 @@ func (acct *AccountView) AccountConfig() *config.AccountConfig {
return acct.acct return acct.acct
} }
func (acct *AccountView) Worker() *types.Worker {
return acct.worker
}
func (acct *AccountView) Logger() *log.Logger {
return acct.logger
}
func (acct *AccountView) Name() string { func (acct *AccountView) Name() string {
return acct.acct.Name return acct.acct.Name
} }
@ -110,10 +118,6 @@ func (acct *AccountView) Focus(focus bool) {
// TODO: Unfocus children I guess // TODO: Unfocus children I guess
} }
func (acct *AccountView) Worker() *types.Worker {
return acct.worker
}
func (acct *AccountView) connected(msg types.WorkerMessage) { func (acct *AccountView) connected(msg types.WorkerMessage) {
switch msg := msg.(type) { switch msg := msg.(type) {
case *types.Done: case *types.Done:

View file

@ -14,6 +14,7 @@ import (
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
"git.sr.ht/~sircmpwn/aerc2/config" "git.sr.ht/~sircmpwn/aerc2/config"
"git.sr.ht/~sircmpwn/aerc2/lib"
"git.sr.ht/~sircmpwn/aerc2/lib/ui" "git.sr.ht/~sircmpwn/aerc2/lib/ui"
"git.sr.ht/~sircmpwn/aerc2/worker/types" "git.sr.ht/~sircmpwn/aerc2/worker/types"
) )
@ -123,6 +124,13 @@ func (c *Composer) Defaults(defaults map[string]string) *Composer {
return c return c
} }
func (c *Composer) FocusTerminal() *Composer {
c.focusable[c.focused].Focus(false)
c.focused = 3
c.focusable[c.focused].Focus(true)
return c
}
func (c *Composer) OnSubjectChange(fn func(subject string)) { func (c *Composer) OnSubjectChange(fn func(subject string)) {
c.headers.subject.OnChange(func() { c.headers.subject.OnChange(func() {
fn(c.headers.subject.input.String()) fn(c.headers.subject.input.String())
@ -197,9 +205,9 @@ func (c *Composer) PrepareHeader() (*mail.Header, []string, error) {
c.email.Seek(0, os.SEEK_SET) c.email.Seek(0, os.SEEK_SET)
} }
// Update headers // Update headers
// TODO: Custom header fields
mhdr := (*message.Header)(&header.Header) mhdr := (*message.Header)(&header.Header)
mhdr.SetContentType("text/plain", map[string]string{"charset": "UTF-8"}) mhdr.SetContentType("text/plain", map[string]string{"charset": "UTF-8"})
mhdr.SetText("Message-Id", lib.GenerateMessageId())
if subject, _ := header.Subject(); subject == "" { if subject, _ := header.Subject(); subject == "" {
header.SetSubject(c.headers.subject.input.String()) header.SetSubject(c.headers.subject.input.String())
} }
@ -228,14 +236,14 @@ func (c *Composer) PrepareHeader() (*mail.Header, []string, error) {
rcpts = append(rcpts, addr.Address) rcpts = append(rcpts, addr.Address)
} }
} }
// TODO: Add cc, bcc to rcpts
// Merge in additional headers // Merge in additional headers
txthdr := mhdr.Header txthdr := mhdr.Header
for key, value := range c.defaults { for key, value := range c.defaults {
if !txthdr.Has(key) { if !txthdr.Has(key) && value != "" {
mhdr.SetText(key, value) mhdr.SetText(key, value)
} }
} }
// TODO: Add cc, bcc to rcpts
return &header, rcpts, nil return &header, rcpts, nil
} }