aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-02-02 00:48:51 +0100
committerRobin Jarry <robin@jarry.cc>2023-02-20 14:40:59 +0100
commit9fa296fb762231d386db88913d1c2ea521bd813c (patch)
tree445bf6554084bb37e6aed479c9942b5afecb2149
parent28483808727fd288b95b9dd26a716f6cf7c02b5a (diff)
downloadaerc-9fa296fb762231d386db88913d1c2ea521bd813c.tar.gz
templates: unify data interface
Require that all aerc template data objects implement the same TemplateData interface. Implement that interface in two different places: 1) state.TemplateData (renamed/moved from templates.TemplateData). This structure (along with all its methods) needs to be decoupled from the templates package to break the import cycle with the config package. This allows much simpler construction of this object and ensure that values are calculated only when requested. 2) config.dummyData (extracted from templates). This is only used in the config package to validate user templates. Putting it here allows also to break an import cycle. Use state.TemplateData everywhere (including for account tabs title rendering). Signed-off-by: Robin Jarry <robin@jarry.cc> Reviewed-by: Moritz Poldrack <moritz@poldrack.dev>
-rw-r--r--config/columns.go4
-rw-r--r--config/templates.go48
-rw-r--r--config/ui_test.go31
-rw-r--r--lib/state/templates.go (renamed from lib/templates/data.go)193
-rw-r--r--lib/templates/functions.go13
-rw-r--r--lib/templates/template.go19
-rw-r--r--models/templates.go36
-rw-r--r--widgets/account.go27
-rw-r--r--widgets/compose.go60
-rw-r--r--widgets/msglist.go24
10 files changed, 223 insertions, 232 deletions
diff --git a/config/columns.go b/config/columns.go
index b1c7191b..55efa2a7 100644
--- a/config/columns.go
+++ b/config/columns.go
@@ -90,9 +90,7 @@ func parseColumnDef(col string, section *ini.Section) (*ColumnDef, error) {
return nil, err
}
- data := templates.DummyData()
- var buf bytes.Buffer
- err = t.Execute(&buf, data)
+ err = templates.Render(t, &bytes.Buffer{}, &dummyData{})
if err != nil {
return nil, err
}
diff --git a/config/templates.go b/config/templates.go
index 5580c056..f9dda0ea 100644
--- a/config/templates.go
+++ b/config/templates.go
@@ -3,9 +3,11 @@ package config
import (
"path"
"strings"
+ "time"
"git.sr.ht/~rjarry/aerc/lib/templates"
"git.sr.ht/~rjarry/aerc/log"
+ "github.com/emersion/go-message/mail"
"github.com/go-ini/ini"
)
@@ -48,13 +50,13 @@ func parseTemplates(file *ini.File) error {
// we want to fail during startup if the templates are not ok
// hence we do dummy executes here
t := Templates
- if err := templates.CheckTemplate(t.NewMessage, t.TemplateDirs); err != nil {
+ if err := checkTemplate(t.NewMessage, t.TemplateDirs); err != nil {
return err
}
- if err := templates.CheckTemplate(t.QuotedReply, t.TemplateDirs); err != nil {
+ if err := checkTemplate(t.QuotedReply, t.TemplateDirs); err != nil {
return err
}
- if err := templates.CheckTemplate(t.Forwards, t.TemplateDirs); err != nil {
+ if err := checkTemplate(t.Forwards, t.TemplateDirs); err != nil {
return err
}
@@ -62,3 +64,43 @@ func parseTemplates(file *ini.File) error {
return nil
}
+
+func checkTemplate(filename string, dirs []string) error {
+ var data dummyData
+ _, err := templates.ParseTemplateFromFile(filename, dirs, &data)
+ return err
+}
+
+// only for validation
+type dummyData struct{}
+
+var (
+ addr1 = mail.Address{Name: "John Foo", Address: "foo@bar.org"}
+ addr2 = mail.Address{Name: "John Bar", Address: "bar@foo.org"}
+)
+
+func (d *dummyData) Account() string { return "work" }
+func (d *dummyData) Folder() string { return "INBOX" }
+func (d *dummyData) To() []*mail.Address { return []*mail.Address{&addr1} }
+func (d *dummyData) Cc() []*mail.Address { return nil }
+func (d *dummyData) Bcc() []*mail.Address { return nil }
+func (d *dummyData) From() []*mail.Address { return []*mail.Address{&addr2} }
+func (d *dummyData) Peer() []*mail.Address { return d.From() }
+func (d *dummyData) ReplyTo() []*mail.Address { return nil }
+func (d *dummyData) Date() time.Time { return time.Now() }
+func (d *dummyData) DateAutoFormat(time.Time) string { return "" }
+func (d *dummyData) Header(string) string { return "" }
+func (d *dummyData) Subject() string { return "[PATCH] hey" }
+func (d *dummyData) Number() int { return 0 }
+func (d *dummyData) Labels() []string { return nil }
+func (d *dummyData) Flags() []string { return nil }
+func (d *dummyData) MessageId() string { return "123456789@foo.org" }
+func (d *dummyData) Size() int { return 420 }
+func (d *dummyData) OriginalText() string { return "Blah blah blah" }
+func (d *dummyData) OriginalDate() time.Time { return time.Now() }
+func (d *dummyData) OriginalFrom() []*mail.Address { return d.From() }
+func (d *dummyData) OriginalMIMEType() string { return "text/plain" }
+func (d *dummyData) OriginalHeader(string) string { return "" }
+func (d *dummyData) Recent() int { return 1 }
+func (d *dummyData) Unread() int { return 3 }
+func (d *dummyData) Exists() int { return 14 }
diff --git a/config/ui_test.go b/config/ui_test.go
index c5eb6031..677dcc86 100644
--- a/config/ui_test.go
+++ b/config/ui_test.go
@@ -1,13 +1,20 @@
package config
import (
- "bytes"
+ "reflect"
"testing"
+ "text/template"
- "git.sr.ht/~rjarry/aerc/lib/templates"
"github.com/stretchr/testify/assert"
)
+func templateText(t *template.Template) string {
+ // unfortunately, the original template text is stored as a private
+ // field, for test purposes, access its value via reflection
+ v := reflect.ValueOf(t).Elem()
+ return v.FieldByName("text").String()
+}
+
func TestConvertIndexFormat(t *testing.T) {
columns, err := convertIndexFormat("%-20.20D %-17.17n %Z %s")
if err != nil {
@@ -15,32 +22,26 @@ func TestConvertIndexFormat(t *testing.T) {
}
assert.Len(t, columns, 4)
- data := templates.DummyData()
- var buf bytes.Buffer
-
assert.Equal(t, "date", columns[0].Name)
assert.Equal(t, 20.0, columns[0].Width)
assert.Equal(t, ALIGN_LEFT|WIDTH_EXACT, columns[0].Flags)
- assert.Nil(t, columns[0].Template.Execute(&buf, data))
+ assert.Equal(t, `{{.DateAutoFormat .Date.Local}}`,
+ templateText(columns[0].Template))
- buf.Reset()
assert.Equal(t, "name", columns[1].Name)
assert.Equal(t, 17.0, columns[1].Width)
assert.Equal(t, ALIGN_LEFT|WIDTH_EXACT, columns[1].Flags)
- assert.Nil(t, columns[1].Template.Execute(&buf, data))
- assert.Equal(t, "John Doe", buf.String())
+ assert.Equal(t, `{{index (.From | names) 0}}`,
+ templateText(columns[1].Template))
- buf.Reset()
assert.Equal(t, "flags", columns[2].Name)
assert.Equal(t, 4.0, columns[2].Width)
assert.Equal(t, ALIGN_RIGHT|WIDTH_EXACT, columns[2].Flags)
- assert.Nil(t, columns[2].Template.Execute(&buf, data))
- assert.Equal(t, "O!*", buf.String())
+ assert.Equal(t, `{{.Flags | join ""}}`,
+ templateText(columns[2].Template))
- buf.Reset()
assert.Equal(t, "subject", columns[3].Name)
assert.Equal(t, 0.0, columns[3].Width)
assert.Equal(t, ALIGN_LEFT|WIDTH_AUTO, columns[3].Flags)
- assert.Nil(t, columns[3].Template.Execute(&buf, data))
- assert.Equal(t, "[PATCH aerc 2/3] foo: baz bar buz", buf.String())
+ assert.Equal(t, `{{.Subject}}`, templateText(columns[3].Template))
}
diff --git a/lib/templates/data.go b/lib/state/templates.go
index e34dc2f3..fdf662c5 100644
--- a/lib/templates/data.go
+++ b/lib/state/templates.go
@@ -1,10 +1,10 @@
-package templates
+package state
import (
- "fmt"
"strings"
"time"
+ "git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/models"
sortthread "github.com/emersion/go-imap-sortthread"
"github.com/emersion/go-message/mail"
@@ -24,44 +24,12 @@ type TemplateData struct {
ThreadSameSubject bool
ThreadPrefix string
- // account config
+ // selected account
+ account *config.AccountConfig
myAddresses map[string]bool
- account string
folder string // selected folder name
-
- // ui config
- timeFmt string
- thisDayTimeFmt string
- thisWeekTimeFmt string
- thisYearTimeFmt string
- iconAttachment string
-}
-
-func NewTemplateData(
- from *mail.Address,
- aliases []*mail.Address,
- account string,
- folder string,
- timeFmt string,
- thisDayTimeFmt string,
- thisWeekTimeFmt string,
- thisYearTimeFmt string,
- iconAttachment string,
-) *TemplateData {
- myAddresses := map[string]bool{from.Address: true}
- for _, addr := range aliases {
- myAddresses[addr.Address] = true
- }
- return &TemplateData{
- myAddresses: myAddresses,
- account: account,
- folder: folder,
- timeFmt: timeFmt,
- thisDayTimeFmt: thisDayTimeFmt,
- thisWeekTimeFmt: thisWeekTimeFmt,
- thisYearTimeFmt: thisYearTimeFmt,
- iconAttachment: iconAttachment,
- }
+ folders []string
+ getRUEcount func(string) (int, int, int)
}
// only used for compose/reply/forward
@@ -77,14 +45,38 @@ func (d *TemplateData) SetInfo(info *models.MessageInfo, num int, marked bool) {
d.marked = marked
}
+func (d *TemplateData) SetAccount(acct *config.AccountConfig) {
+ d.account = acct
+ d.myAddresses = map[string]bool{acct.From.Address: true}
+ for _, addr := range acct.Aliases {
+ d.myAddresses[addr.Address] = true
+ }
+}
+
+func (d *TemplateData) SetFolder(folder string) {
+ d.folder = folder
+}
+
+func (d *TemplateData) SetRUE(folders []string, cb func(string) (int, int, int)) {
+ d.folders = folders
+ d.getRUEcount = cb
+}
+
func (d *TemplateData) Account() string {
- return d.account
+ if d.account != nil {
+ return d.account.Name
+ }
+ return ""
}
func (d *TemplateData) Folder() string {
return d.folder
}
+func (d *TemplateData) ui() *config.UIConfig {
+ return config.Ui.ForAccount(d.Account()).ForFolder(d.folder)
+}
+
func (d *TemplateData) To() []*mail.Address {
var to []*mail.Address
switch {
@@ -175,20 +167,21 @@ func (d *TemplateData) DateAutoFormat(date time.Time) string {
if date.IsZero() {
return ""
}
+ ui := d.ui()
year := date.Year()
day := date.YearDay()
now := time.Now()
thisYear := now.Year()
thisDay := now.YearDay()
- fmt := d.timeFmt
+ fmt := ui.TimestampFormat
if year == thisYear {
switch {
- case day == thisDay && d.thisDayTimeFmt != "":
- fmt = d.thisDayTimeFmt
- case day > thisDay-7 && d.thisWeekTimeFmt != "":
- fmt = d.thisWeekTimeFmt
- case d.thisYearTimeFmt != "":
- fmt = d.thisYearTimeFmt
+ case day == thisDay && ui.ThisDayTimeFormat != "":
+ fmt = ui.ThisDayTimeFormat
+ case day > thisDay-7 && ui.ThisWeekTimeFormat != "":
+ fmt = ui.ThisWeekTimeFormat
+ case ui.ThisYearTimeFormat != "":
+ fmt = ui.ThisYearTimeFormat
}
}
return date.Format(fmt)
@@ -230,8 +223,8 @@ func (d *TemplateData) SubjectBase() string {
return base
}
-func (d *TemplateData) Number() string {
- return fmt.Sprintf("%d", d.msgNum)
+func (d *TemplateData) Number() int {
+ return d.msgNum
}
func (d *TemplateData) Labels() []string {
@@ -263,7 +256,7 @@ func (d *TemplateData) Flags() []string {
if d.info.BodyStructure != nil {
for _, bS := range d.info.BodyStructure.Parts {
if strings.ToLower(bS.Disposition) == "attachment" {
- flags = append(flags, d.iconAttachment)
+ flags = append(flags, d.ui().IconAttachment)
break
}
}
@@ -284,11 +277,11 @@ func (d *TemplateData) MessageId() string {
return d.info.Envelope.MessageId
}
-func (d *TemplateData) Size() uint32 {
+func (d *TemplateData) Size() int {
if d.info == nil || d.info.Envelope == nil {
return 0
}
- return d.info.Size
+ return int(d.info.Size)
}
func (d *TemplateData) OriginalText() string {
@@ -331,76 +324,30 @@ func (d *TemplateData) OriginalHeader(name string) string {
return text
}
-// DummyData provides dummy data to test template validity
-func DummyData() *TemplateData {
- from := &mail.Address{
- Name: "John Doe",
- Address: "john@example.com",
- }
- to := &mail.Address{
- Name: "Alice Doe",
- Address: "alice@example.com",
- }
- h := &mail.Header{}
- h.SetAddressList("from", []*mail.Address{from})
- h.SetAddressList("to", []*mail.Address{to})
-
- oh := &mail.Header{}
- oh.SetAddressList("from", []*mail.Address{to})
- oh.SetAddressList("to", []*mail.Address{from})
-
- original := models.OriginalMail{
- Date: time.Now(),
- From: from.String(),
- Text: "This is only a test text",
- MIMEType: "text/plain",
- RFC822Headers: oh,
- }
- data := NewTemplateData(
- to,
- nil,
- "account",
- "folder",
- "2006 Jan 02, 15:04 GMT-0700",
- "15:04",
- "Monday 15:04",
- "Jan 02",
- "a",
- )
- data.SetHeaders(h, &original)
-
- info := &models.MessageInfo{
- BodyStructure: &models.BodyStructure{
- MIMEType: "text",
- MIMESubType: "plain",
- Params: make(map[string]string),
- Description: "",
- Encoding: "",
- Parts: []*models.BodyStructure{},
- Disposition: "",
- DispositionParams: make(map[string]string),
- },
- Envelope: &models.Envelope{
- Date: time.Date(1981, 6, 23, 16, 52, 0, 0, time.UTC),
- Subject: "[PATCH aerc 2/3] foo: baz bar buz",
- From: []*mail.Address{from},
- ReplyTo: []*mail.Address{},
- To: []*mail.Address{to},
- Cc: []*mail.Address{},
- Bcc: []*mail.Address{},
- MessageId: "",
- InReplyTo: "",
- },
- Flags: models.FlaggedFlag,
- Labels: []string{"inbox", "patch"},
- InternalDate: time.Now(),
- RFC822Headers: nil,
- Refs: []string{},
- Size: 65512,
- Uid: 12345,
- Error: nil,
- }
- data.SetInfo(info, 42, true)
-
- return data
+func (d *TemplateData) rue() (int, int, int) {
+ var recent, unread, exists int
+ if d.getRUEcount != nil {
+ for _, dir := range d.folders {
+ r, u, e := d.getRUEcount(dir)
+ recent += r
+ unread += u
+ exists += e
+ }
+ }
+ return recent, unread, exists
+}
+
+func (d *TemplateData) Recent() int {
+ r, _, _ := d.rue()
+ return r
+}
+
+func (d *TemplateData) Unread() int {
+ _, u, _ := d.rue()
+ return u
+}
+
+func (d *TemplateData) Exists() int {
+ _, _, e := d.rue()
+ return e
}
diff --git a/lib/templates/functions.go b/lib/templates/functions.go
index 630db264..37b014f5 100644
--- a/lib/templates/functions.go
+++ b/lib/templates/functions.go
@@ -161,9 +161,14 @@ func persons(addresses []*mail.Address) []string {
var units = []string{"K", "M", "G", "T"}
-func humanReadable(value uint32) string {
+func humanReadable(value int) string {
+ sign := ""
+ if value < 0 {
+ sign = "-"
+ value = -value
+ }
if value < 1000 {
- return fmt.Sprintf("%d", value)
+ return fmt.Sprintf("%s%d", sign, value)
}
val := float64(value)
unit := ""
@@ -172,9 +177,9 @@ func humanReadable(value uint32) string {
val /= 1000.0
}
if val < 100.0 {
- return fmt.Sprintf("%.1f%s", val, unit)
+ return fmt.Sprintf("%s%.1f%s", sign, val, unit)
}
- return fmt.Sprintf("%.0f%s", val, unit)
+ return fmt.Sprintf("%s%.0f%s", sign, val, unit)
}
func cwd() string {
diff --git a/lib/templates/template.go b/lib/templates/template.go
index 28785356..bf257669 100644
--- a/lib/templates/template.go
+++ b/lib/templates/template.go
@@ -8,6 +8,7 @@ import (
"path"
"text/template"
+ "git.sr.ht/~rjarry/aerc/models"
"github.com/mitchellh/go-homedir"
)
@@ -28,19 +29,21 @@ func findTemplate(templateName string, templateDirs []string) (string, error) {
"Can't find template %q in any of %v ", templateName, templateDirs)
}
-func ParseTemplateFromFile(templateName string, templateDirs []string, data interface{}) (io.Reader, error) {
- templateFile, err := findTemplate(templateName, templateDirs)
+func ParseTemplateFromFile(
+ name string, dirs []string, data models.TemplateData,
+) (io.Reader, error) {
+ templateFile, err := findTemplate(name, dirs)
if err != nil {
return nil, err
}
- emailTemplate, err := template.New(templateName).
+ emailTemplate, err := template.New(name).
Funcs(templateFuncs).ParseFiles(templateFile)
if err != nil {
return nil, err
}
var body bytes.Buffer
- if err := emailTemplate.Execute(&body, data); err != nil {
+ if err := Render(emailTemplate, &body, data); err != nil {
return nil, err
}
return &body, nil
@@ -50,10 +53,6 @@ func ParseTemplate(name, content string) (*template.Template, error) {
return template.New(name).Funcs(templateFuncs).Parse(content)
}
-func CheckTemplate(templateName string, templateDirs []string) error {
- if templateName != "" {
- _, err := ParseTemplateFromFile(templateName, templateDirs, DummyData())
- return err
- }
- return nil
+func Render(t *template.Template, w io.Writer, data models.TemplateData) error {
+ return t.Execute(w, data)
}
diff --git a/models/templates.go b/models/templates.go
new file mode 100644
index 00000000..84f68c21
--- /dev/null
+++ b/models/templates.go
@@ -0,0 +1,36 @@
+package models
+
+import (
+ "time"
+
+ "github.com/emersion/go-message/mail"
+)
+
+// This interface needs to be implemented for compliance with aerc-templates(7)
+type TemplateData interface {
+ Account() string
+ Folder() string
+ To() []*mail.Address
+ Cc() []*mail.Address
+ Bcc() []*mail.Address
+ From() []*mail.Address
+ Peer() []*mail.Address
+ ReplyTo() []*mail.Address
+ Date() time.Time
+ DateAutoFormat(date time.Time) string
+ Header(name string) string
+ Subject() string
+ Number() int
+ Labels() []string
+ Flags() []string
+ MessageId() string
+ Size() int
+ OriginalText() string
+ OriginalDate() time.Time
+ OriginalFrom() []*mail.Address
+ OriginalMIMEType() string
+ OriginalHeader(name string) string
+ Recent() int
+ Unread() int
+ Exists() int
+}
diff --git a/widgets/account.go b/widgets/account.go
index 85043d1f..3176a2bf 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -13,7 +13,9 @@ import (
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/marker"
"git.sr.ht/~rjarry/aerc/lib/sort"
+ "git.sr.ht/~rjarry/aerc/lib/state"
"git.sr.ht/~rjarry/aerc/lib/statusline"
+ "git.sr.ht/~rjarry/aerc/lib/templates"
"git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
@@ -597,23 +599,14 @@ func (acct *AccountView) Vsplit(n int) error {
// setTitle executes the title template and sets the tab title
func (acct *AccountView) setTitle() {
- data := struct {
- Account string
- Recent int
- Unread int
- Exists int
- Folder string
- }{}
- data.Account = acct.Name()
- data.Folder = acct.SelectedDirectory()
- for _, name := range acct.dirlist.List() {
- r, u, e := acct.dirlist.GetRUECount(name)
- data.Recent += r
- data.Unread += u
- data.Exists += e
- }
- buf := bytes.NewBuffer(nil)
- err := acct.uiConf.TabTitleAccount.Execute(buf, data)
+ var data state.TemplateData
+
+ data.SetAccount(acct.acct)
+ data.SetFolder(acct.SelectedDirectory())
+ data.SetRUE(acct.dirlist.List(), acct.dirlist.GetRUECount)
+
+ var buf bytes.Buffer
+ err := templates.Render(acct.uiConf.TabTitleAccount, &buf, &data)
if err != nil {
acct.PushError(err)
return
diff --git a/widgets/compose.go b/widgets/compose.go
index 7311de7a..b74e1537 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -23,6 +23,7 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/format"
+ "git.sr.ht/~rjarry/aerc/lib/state"
"git.sr.ht/~rjarry/aerc/lib/templates"
"git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
@@ -95,21 +96,11 @@ func NewComposer(
completer: nil,
}
- uiConfig := acct.UiConfig()
-
- templateData := templates.NewTemplateData(
- acct.acct.From,
- acct.acct.Aliases,
- acct.Name(),
- acct.Directories().Selected(),
- uiConfig.MessageViewTimestampFormat,
- uiConfig.MessageViewThisDayTimeFormat,
- uiConfig.MessageViewThisWeekTimeFormat,
- uiConfig.MessageViewThisYearTimeFormat,
- uiConfig.IconAttachment,
- )
- templateData.SetHeaders(h, orig)
- if err := c.AddTemplate(template, templateData); err != nil {
+ var data state.TemplateData
+ data.SetAccount(acct.acct)
+ data.SetFolder(acct.Directories().Selected())
+ data.SetHeaders(h, orig)
+ if err := c.AddTemplate(template, &data); err != nil {
return nil, err
}
c.AddSignature()
@@ -509,7 +500,7 @@ func (c *Composer) RemovePart(mimetype string) error {
return fmt.Errorf("%s part not found", mimetype)
}
-func (c *Composer) AddTemplate(template string, data interface{}) error {
+func (c *Composer) AddTemplate(template string, data models.TemplateData) error {
if template == "" {
return nil
}
@@ -1551,37 +1542,24 @@ func (c *Composer) setTitle() {
if c.Tab == nil {
return
}
- data := struct {
- Account string
- Subject string
- To []*mail.Address
- From []*mail.Address
- Cc []*mail.Address
- Bcc []*mail.Address
- OriginalFrom []*mail.Address
- }{}
- data.Account = c.acct.Name()
+
+ var data state.TemplateData
+
+ header := c.header.Copy()
// Get subject direct from the textinput
subject, ok := c.editors["subject"]
if ok {
- data.Subject = subject.input.String()
- }
- if data.Subject == "" {
- data.Subject = "New Email"
+ header.SetSubject(subject.input.String())
}
- // Get address fields from header, which gets updated on focus lost of
- // any headerEditor field
- data.From, _ = c.header.AddressList("from")
- data.To, _ = c.header.AddressList("to")
- data.Cc, _ = c.header.AddressList("cc")
- data.Bcc, _ = c.header.AddressList("bcc")
-
- if c.parent != nil && c.parent.RFC822Headers != nil {
- data.OriginalFrom, _ = c.parent.RFC822Headers.AddressList("from")
+ if header.Get("subject") == "" {
+ header.SetSubject("New Email")
}
+ data.SetAccount(c.acctConfig)
+ data.SetFolder(c.acct.SelectedDirectory())
+ data.SetHeaders(&header, c.parent)
- buf := bytes.NewBuffer(nil)
- err := c.acct.UiConfig().TabTitleComposer.Execute(buf, data)
+ var buf bytes.Buffer
+ err := templates.Render(c.acct.UiConfig().TabTitleComposer, &buf, &data)
if err != nil {
c.acct.PushError(err)
return
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 85072d1e..4b2bf721 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -11,7 +11,7 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/iterator"
- "git.sr.ht/~rjarry/aerc/lib/templates"
+ "git.sr.ht/~rjarry/aerc/lib/state"
"git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
@@ -87,19 +87,11 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
return
}
- data := templates.NewTemplateData(
- acct.acct.From,
- acct.acct.Aliases,
- acct.Name(),
- acct.Directories().Selected(),
- uiConfig.TimestampFormat,
- uiConfig.ThisDayTimeFormat,
- uiConfig.ThisWeekTimeFormat,
- uiConfig.ThisYearTimeFormat,
- uiConfig.IconAttachment,
- )
-
var needsHeaders []uint32
+ var data state.TemplateData
+
+ data.SetAccount(acct.acct)
+ data.SetFolder(acct.Directories().Selected())
customDraw := func(t *ui.Table, r int, c *ui.Context) bool {
row := &t.Rows[r]
@@ -175,7 +167,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
lastSubject = baseSubject
prevThread = thread
- if addMessage(store, thread.Uid, &table, data, uiConfig) {
+ if addMessage(store, thread.Uid, &table, &data, uiConfig) {
break threadLoop
}
}
@@ -187,7 +179,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
continue
}
uid := iter.Value().(uint32)
- if addMessage(store, uid, &table, data, uiConfig) {
+ if addMessage(store, uid, &table, &data, uiConfig) {
break
}
}
@@ -220,7 +212,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
func addMessage(
store *lib.MessageStore, uid uint32,
- table *ui.Table, data *templates.TemplateData,
+ table *ui.Table, data *state.TemplateData,
uiConfig *config.UIConfig,
) bool {
msg := store.Messages[uid]