Display user specified headers in viewer if present
This commit is contained in:
parent
d7975132b6
commit
dfc048fe28
5 changed files with 116 additions and 41 deletions
|
@ -61,6 +61,14 @@ alternatives=text/plain,text/html
|
||||||
# Default: false
|
# Default: false
|
||||||
show-headers=false
|
show-headers=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Layout of headers when viewing a message. To display multiple headers in the
|
||||||
|
# same row, separate them with a pipe, e.g. "From|To". Rows will be hidden if
|
||||||
|
# none of their specified headers are present in the message.
|
||||||
|
#
|
||||||
|
# Default: From|To,Cc|Bcc,Date,Subject
|
||||||
|
header-layout=From|To,Cc|Bcc,Date,Subject
|
||||||
|
|
||||||
[compose]
|
[compose]
|
||||||
#
|
#
|
||||||
# Specifies the command to run the editor with. It will be shown in an embedded
|
# Specifies the command to run the editor with. It will be shown in an embedded
|
||||||
|
|
|
@ -80,6 +80,7 @@ type ViewerConfig struct {
|
||||||
Pager string
|
Pager string
|
||||||
Alternatives []string
|
Alternatives []string
|
||||||
ShowHeaders bool `ini:"show-headers"`
|
ShowHeaders bool `ini:"show-headers"`
|
||||||
|
HeaderLayout [][]string `ini:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AercConfig struct {
|
type AercConfig struct {
|
||||||
|
@ -261,6 +262,8 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
|
||||||
switch key {
|
switch key {
|
||||||
case "alternatives":
|
case "alternatives":
|
||||||
config.Viewer.Alternatives = strings.Split(val, ",")
|
config.Viewer.Alternatives = strings.Split(val, ",")
|
||||||
|
case "header-layout":
|
||||||
|
config.Viewer.HeaderLayout = parseLayout(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +326,18 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
|
||||||
EmptyDirlist: "(no folders)",
|
EmptyDirlist: "(no folders)",
|
||||||
MouseEnabled: false,
|
MouseEnabled: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Viewer: ViewerConfig{
|
||||||
|
Pager: "less -R",
|
||||||
|
Alternatives: []string{"text/plain", "text/html"},
|
||||||
|
ShowHeaders: false,
|
||||||
|
HeaderLayout: [][]string{
|
||||||
|
{"From", "To"},
|
||||||
|
{"Cc", "Bcc"},
|
||||||
|
{"Date"},
|
||||||
|
{"Subject"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
// These bindings are not configurable
|
// These bindings are not configurable
|
||||||
config.Bindings.AccountWizard.ExKey = KeyStroke{
|
config.Bindings.AccountWizard.ExKey = KeyStroke{
|
||||||
|
@ -431,3 +446,12 @@ func checkConfigPerms(filename string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLayout(layout string) [][]string {
|
||||||
|
rows := strings.Split(layout, ",")
|
||||||
|
l := make([][]string, len(rows))
|
||||||
|
for i, r := range rows {
|
||||||
|
l[i] = strings.Split(r, "|")
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,14 @@ These options are configured in the *[viewer]* section of aerc.conf.
|
||||||
|
|
||||||
Default: text/plain,text/html
|
Default: text/plain,text/html
|
||||||
|
|
||||||
|
*header-layout*
|
||||||
|
Defines the default headers to display when viewing a message. To display
|
||||||
|
multiple headers in the same row, separate them with a pipe, e.g. "From|To".
|
||||||
|
Rows will be hidden if none of their specified headers are present in the
|
||||||
|
message.
|
||||||
|
|
||||||
|
Default: From|To,Cc|Bcc,Date,Subject
|
||||||
|
|
||||||
*show-headers*
|
*show-headers*
|
||||||
Default setting to determine whether to show full headers or only parsed
|
Default setting to determine whether to show full headers or only parsed
|
||||||
ones in message viewer.
|
ones in message viewer.
|
||||||
|
|
|
@ -54,6 +54,20 @@ func NewGrid() *Grid {
|
||||||
return &Grid{invalid: true}
|
return &Grid{invalid: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeGrid creates a grid with the specified number of columns and rows. Each
|
||||||
|
// cell has a size of 1.
|
||||||
|
func MakeGrid(numRows, numCols, rowStrategy, colStrategy int) *Grid {
|
||||||
|
rows := make([]GridSpec, numRows)
|
||||||
|
for i := 0; i < numRows; i++ {
|
||||||
|
rows[i] = GridSpec{rowStrategy, 1}
|
||||||
|
}
|
||||||
|
cols := make([]GridSpec, numCols)
|
||||||
|
for i := 0; i < numCols; i++ {
|
||||||
|
cols[i] = GridSpec{colStrategy, 1}
|
||||||
|
}
|
||||||
|
return NewGrid().Rows(rows).Columns(cols)
|
||||||
|
}
|
||||||
|
|
||||||
func (cell *GridCell) At(row, col int) *GridCell {
|
func (cell *GridCell) At(row, col int) *GridCell {
|
||||||
cell.Row = row
|
cell.Row = row
|
||||||
cell.Column = col
|
cell.Column = col
|
||||||
|
|
|
@ -45,53 +45,26 @@ type PartSwitcher struct {
|
||||||
|
|
||||||
func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
store *lib.MessageStore, msg *models.MessageInfo) *MessageViewer {
|
store *lib.MessageStore, msg *models.MessageInfo) *MessageViewer {
|
||||||
|
header, headerHeight := createHeader(msg, conf.Viewer.HeaderLayout)
|
||||||
|
|
||||||
grid := ui.NewGrid().Rows([]ui.GridSpec{
|
grid := ui.NewGrid().Rows([]ui.GridSpec{
|
||||||
{ui.SIZE_EXACT, 4}, // TODO: Based on number of header rows
|
{ui.SIZE_EXACT, headerHeight},
|
||||||
{ui.SIZE_WEIGHT, 1},
|
{ui.SIZE_WEIGHT, 1},
|
||||||
}).Columns([]ui.GridSpec{
|
}).Columns([]ui.GridSpec{
|
||||||
{ui.SIZE_WEIGHT, 1},
|
{ui.SIZE_WEIGHT, 1},
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: let user specify additional headers to show by default
|
|
||||||
headers := ui.NewGrid().Rows([]ui.GridSpec{
|
|
||||||
{ui.SIZE_EXACT, 1},
|
|
||||||
{ui.SIZE_EXACT, 1},
|
|
||||||
{ui.SIZE_EXACT, 1},
|
|
||||||
{ui.SIZE_EXACT, 1},
|
|
||||||
}).Columns([]ui.GridSpec{
|
|
||||||
{ui.SIZE_WEIGHT, 1},
|
|
||||||
{ui.SIZE_WEIGHT, 1},
|
|
||||||
})
|
|
||||||
headers.AddChild(
|
|
||||||
&HeaderView{
|
|
||||||
Name: "From",
|
|
||||||
Value: models.FormatAddresses(msg.Envelope.From),
|
|
||||||
}).At(0, 0)
|
|
||||||
headers.AddChild(
|
|
||||||
&HeaderView{
|
|
||||||
Name: "To",
|
|
||||||
Value: models.FormatAddresses(msg.Envelope.To),
|
|
||||||
}).At(0, 1)
|
|
||||||
headers.AddChild(
|
|
||||||
&HeaderView{
|
|
||||||
Name: "Date",
|
|
||||||
Value: msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM"),
|
|
||||||
}).At(1, 0).Span(1, 2)
|
|
||||||
headers.AddChild(
|
|
||||||
&HeaderView{
|
|
||||||
Name: "Subject",
|
|
||||||
Value: msg.Envelope.Subject,
|
|
||||||
}).At(2, 0).Span(1, 2)
|
|
||||||
headers.AddChild(ui.NewFill(' ')).At(3, 0).Span(1, 2)
|
|
||||||
|
|
||||||
switcher := &PartSwitcher{}
|
switcher := &PartSwitcher{}
|
||||||
err := createSwitcher(switcher, conf, store, msg, conf.Viewer.ShowHeaders)
|
err := createSwitcher(switcher, conf, store, msg, conf.Viewer.ShowHeaders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
goto handle_error
|
return &MessageViewer{
|
||||||
|
err: err,
|
||||||
|
grid: grid,
|
||||||
|
msg: msg,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.AddChild(headers).At(0, 0)
|
grid.AddChild(header).At(0, 0)
|
||||||
grid.AddChild(switcher).At(1, 0)
|
grid.AddChild(switcher).At(1, 0)
|
||||||
|
|
||||||
return &MessageViewer{
|
return &MessageViewer{
|
||||||
|
@ -102,12 +75,60 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
store: store,
|
store: store,
|
||||||
switcher: switcher,
|
switcher: switcher,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handle_error:
|
func createHeader(msg *models.MessageInfo, layout [][]string) (grid *ui.Grid, height int) {
|
||||||
return &MessageViewer{
|
presentHeaders := presentHeaders(msg, layout)
|
||||||
err: err,
|
rowCount := len(presentHeaders) + 1 // extra row for spacer
|
||||||
grid: grid,
|
grid = ui.MakeGrid(rowCount, 1, ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
||||||
msg: msg,
|
for i, cols := range presentHeaders {
|
||||||
|
r := ui.MakeGrid(1, len(cols), ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
||||||
|
for j, col := range cols {
|
||||||
|
r.AddChild(
|
||||||
|
&HeaderView{
|
||||||
|
Name: col,
|
||||||
|
Value: fmtHeader(msg, col),
|
||||||
|
}).At(0, j)
|
||||||
|
}
|
||||||
|
grid.AddChild(r).At(i, 0)
|
||||||
|
}
|
||||||
|
grid.AddChild(ui.NewFill(' ')).At(rowCount-1, 0)
|
||||||
|
return grid, rowCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// presentHeaders returns a filtered header layout, removing rows whose headers
|
||||||
|
// do not appear in the provided message.
|
||||||
|
func presentHeaders(msg *models.MessageInfo, layout [][]string) [][]string {
|
||||||
|
headers := msg.RFC822Headers
|
||||||
|
result := make([][]string, 0, len(layout))
|
||||||
|
for _, row := range layout {
|
||||||
|
// To preserve layout alignment, only hide rows if all columns are empty
|
||||||
|
for _, col := range row {
|
||||||
|
if headers.Get(col) != "" {
|
||||||
|
result = append(result, row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func fmtHeader(msg *models.MessageInfo, header string) string {
|
||||||
|
switch header {
|
||||||
|
case "From":
|
||||||
|
return models.FormatAddresses(msg.Envelope.From)
|
||||||
|
case "To":
|
||||||
|
return models.FormatAddresses(msg.Envelope.To)
|
||||||
|
case "Cc":
|
||||||
|
return models.FormatAddresses(msg.Envelope.Cc)
|
||||||
|
case "Bcc":
|
||||||
|
return models.FormatAddresses(msg.Envelope.Bcc)
|
||||||
|
case "Date":
|
||||||
|
return msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM")
|
||||||
|
case "Subject":
|
||||||
|
return msg.Envelope.Subject
|
||||||
|
default:
|
||||||
|
return msg.RFC822Headers.Get(header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue