Implement (basic form) of :reply
This commit is contained in:
parent
2b3e123cb8
commit
475b697bdf
|
@ -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
1
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue