Refactor message part into dedicated widget
This commit is contained in:
parent
5de1bb8cc3
commit
3376f926ed
|
@ -25,14 +25,10 @@ import (
|
||||||
type MessageViewer struct {
|
type MessageViewer struct {
|
||||||
conf *config.AercConfig
|
conf *config.AercConfig
|
||||||
err error
|
err error
|
||||||
filter *exec.Cmd
|
|
||||||
msg *types.MessageInfo
|
msg *types.MessageInfo
|
||||||
pager *exec.Cmd
|
|
||||||
source io.Reader
|
|
||||||
pagerin io.WriteCloser
|
|
||||||
sink io.WriteCloser
|
|
||||||
grid *ui.Grid
|
grid *ui.Grid
|
||||||
term *Terminal
|
parts []*PartViewer
|
||||||
|
selected int
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatAddresses(addrs []*imap.Address) string {
|
func formatAddresses(addrs []*imap.Address) string {
|
||||||
|
@ -95,23 +91,111 @@ func NewMessageViewer(conf *config.AercConfig, store *lib.MessageStore,
|
||||||
{ui.SIZE_EXACT, 20},
|
{ui.SIZE_EXACT, 20},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
for i, part := range msg.BodyStructure.Parts {
|
||||||
|
fmt.Println(i, part.MIMEType, part.MIMESubType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add multipart switcher and configure additional parts
|
||||||
|
pv, err := NewPartViewer(conf, msg, 0)
|
||||||
|
if err != nil {
|
||||||
|
goto handle_error
|
||||||
|
}
|
||||||
|
body.AddChild(pv).At(0, 0).Span(1, 2)
|
||||||
|
|
||||||
|
grid.AddChild(headers).At(0, 0)
|
||||||
|
grid.AddChild(body).At(1, 0)
|
||||||
|
|
||||||
|
store.FetchBodyPart(msg.Uid, 0, pv.SetSource)
|
||||||
|
|
||||||
|
return &MessageViewer{
|
||||||
|
grid: grid,
|
||||||
|
msg: msg,
|
||||||
|
parts: []*PartViewer{pv},
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_error:
|
||||||
|
return &MessageViewer{
|
||||||
|
err: err,
|
||||||
|
grid: grid,
|
||||||
|
msg: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MessageViewer) Draw(ctx *ui.Context) {
|
||||||
|
if mv.err != nil {
|
||||||
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||||
|
ctx.Printf(0, 0, tcell.StyleDefault, "%s", mv.err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mv.grid.Draw(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MessageViewer) Invalidate() {
|
||||||
|
mv.grid.Invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MessageViewer) OnInvalidate(fn func(d ui.Drawable)) {
|
||||||
|
mv.grid.OnInvalidate(func(_ ui.Drawable) {
|
||||||
|
fn(mv)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MessageViewer) Event(event tcell.Event) bool {
|
||||||
|
// What is encapsulation even
|
||||||
|
if mv.parts[mv.selected].term != nil {
|
||||||
|
return mv.parts[mv.selected].term.Event(event)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MessageViewer) Focus(focus bool) {
|
||||||
|
if mv.parts[mv.selected].term != nil {
|
||||||
|
mv.parts[mv.selected].term.Focus(focus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartViewer struct {
|
||||||
|
err error
|
||||||
|
filter *exec.Cmd
|
||||||
|
index string
|
||||||
|
msg *types.MessageInfo
|
||||||
|
pager *exec.Cmd
|
||||||
|
pagerin io.WriteCloser
|
||||||
|
part *imap.BodyStructure
|
||||||
|
sink io.WriteCloser
|
||||||
|
source io.Reader
|
||||||
|
term *Terminal
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPartViewer(conf *config.AercConfig,
|
||||||
|
msg *types.MessageInfo, index int) (*PartViewer, error) {
|
||||||
|
var (
|
||||||
|
part *imap.BodyStructure
|
||||||
|
)
|
||||||
|
// TODO: Find IMAP index, which may differ
|
||||||
|
if len(msg.BodyStructure.Parts) != 0 {
|
||||||
|
part = msg.BodyStructure.Parts[index]
|
||||||
|
} else {
|
||||||
|
part = msg.BodyStructure
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
filter *exec.Cmd
|
filter *exec.Cmd
|
||||||
pager *exec.Cmd
|
pager *exec.Cmd
|
||||||
pipe io.WriteCloser
|
pipe io.WriteCloser
|
||||||
pagerin io.WriteCloser
|
pagerin io.WriteCloser
|
||||||
term *Terminal
|
term *Terminal
|
||||||
viewer *MessageViewer
|
|
||||||
)
|
)
|
||||||
cmd, err := shlex.Split(conf.Viewer.Pager)
|
cmd, err := shlex.Split(conf.Viewer.Pager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
goto handle_error
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pager = exec.Command(cmd[0], cmd[1:]...)
|
pager = exec.Command(cmd[0], cmd[1:]...)
|
||||||
|
|
||||||
for _, f := range conf.Filters {
|
for _, f := range conf.Filters {
|
||||||
mime := strings.ToLower(msg.BodyStructure.MIMEType) +
|
mime := strings.ToLower(part.MIMEType) +
|
||||||
"/" + strings.ToLower(msg.BodyStructure.MIMESubType)
|
"/" + strings.ToLower(part.MIMESubType)
|
||||||
switch f.FilterType {
|
switch f.FilterType {
|
||||||
case config.FILTER_MIMETYPE:
|
case config.FILTER_MIMETYPE:
|
||||||
if fnmatch.Match(f.Filter, mime, 0) {
|
if fnmatch.Match(f.Filter, mime, 0) {
|
||||||
|
@ -138,120 +222,98 @@ func NewMessageViewer(conf *config.AercConfig, store *lib.MessageStore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
pipe, _ = filter.StdinPipe()
|
if pipe, err = filter.StdinPipe(); err != nil {
|
||||||
pagerin, _ = pager.StdinPipe()
|
return nil, err
|
||||||
|
}
|
||||||
|
if pagerin, _ = pager.StdinPipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pipe, _ = pager.StdinPipe()
|
if pipe, err = pager.StdinPipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if term, err = NewTerminal(pager); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
term, _ = NewTerminal(pager)
|
pv := &PartViewer{
|
||||||
// TODO: configure multipart view. I left a spot for it in the grid
|
|
||||||
body.AddChild(term).At(0, 0).Span(1, 2)
|
|
||||||
|
|
||||||
grid.AddChild(headers).At(0, 0)
|
|
||||||
grid.AddChild(body).At(1, 0)
|
|
||||||
|
|
||||||
viewer = &MessageViewer{
|
|
||||||
filter: filter,
|
filter: filter,
|
||||||
grid: grid,
|
|
||||||
msg: msg,
|
|
||||||
pager: pager,
|
pager: pager,
|
||||||
pagerin: pagerin,
|
pagerin: pagerin,
|
||||||
|
part: part,
|
||||||
sink: pipe,
|
sink: pipe,
|
||||||
term: term,
|
term: term,
|
||||||
}
|
}
|
||||||
|
|
||||||
store.FetchBodyPart(msg.Uid, 0, func(reader io.Reader) {
|
|
||||||
viewer.source = reader
|
|
||||||
viewer.attemptCopy()
|
|
||||||
})
|
|
||||||
|
|
||||||
term.OnStart = func() {
|
term.OnStart = func() {
|
||||||
viewer.attemptCopy()
|
pv.attemptCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return viewer
|
return pv, nil
|
||||||
|
|
||||||
handle_error:
|
|
||||||
viewer = &MessageViewer{
|
|
||||||
err: err,
|
|
||||||
grid: grid,
|
|
||||||
msg: msg,
|
|
||||||
}
|
|
||||||
return viewer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) attemptCopy() {
|
func (pv *PartViewer) SetSource(reader io.Reader) {
|
||||||
if mv.source != nil && mv.pager.Process != nil {
|
pv.source = reader
|
||||||
|
pv.attemptCopy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pv *PartViewer) attemptCopy() {
|
||||||
|
if pv.source != nil && pv.pager.Process != nil {
|
||||||
header := message.Header{}
|
header := message.Header{}
|
||||||
header.SetText("Content-Transfer-Encoding",
|
header.SetText("Content-Transfer-Encoding", pv.part.Encoding)
|
||||||
mv.msg.BodyStructure.Encoding)
|
header.SetContentType(pv.part.MIMEType, pv.part.Params)
|
||||||
header.SetContentType(
|
header.SetText("Content-Description", pv.part.Description)
|
||||||
mv.msg.BodyStructure.MIMEType, mv.msg.BodyStructure.Params)
|
if pv.filter != nil {
|
||||||
header.SetText("Content-Description", mv.msg.BodyStructure.Description)
|
stdout, _ := pv.filter.StdoutPipe()
|
||||||
if mv.filter != nil {
|
pv.filter.Start()
|
||||||
stdout, _ := mv.filter.StdoutPipe()
|
|
||||||
mv.filter.Start()
|
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(mv.pagerin, stdout)
|
_, err := io.Copy(pv.pagerin, stdout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mv.err = err
|
pv.err = err
|
||||||
mv.Invalidate()
|
pv.Invalidate()
|
||||||
}
|
}
|
||||||
mv.pagerin.Close()
|
pv.pagerin.Close()
|
||||||
stdout.Close()
|
stdout.Close()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
entity, err := message.New(header, mv.source)
|
entity, err := message.New(header, pv.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mv.err = err
|
pv.err = err
|
||||||
mv.Invalidate()
|
pv.Invalidate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reader := mail.NewReader(entity)
|
reader := mail.NewReader(entity)
|
||||||
part, err := reader.NextPart()
|
part, err := reader.NextPart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mv.err = err
|
pv.err = err
|
||||||
mv.Invalidate()
|
pv.Invalidate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
io.Copy(mv.sink, part.Body)
|
io.Copy(pv.sink, part.Body)
|
||||||
mv.sink.Close()
|
pv.sink.Close()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) Draw(ctx *ui.Context) {
|
func (pv *PartViewer) OnInvalidate(fn func(ui.Drawable)) {
|
||||||
if mv.err != nil {
|
pv.term.OnInvalidate(func(_ ui.Drawable) {
|
||||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
fn(pv)
|
||||||
ctx.Printf(0, 0, tcell.StyleDefault, "%s", mv.err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mv.grid.Draw(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mv *MessageViewer) Invalidate() {
|
|
||||||
mv.grid.Invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mv *MessageViewer) OnInvalidate(fn func(d ui.Drawable)) {
|
|
||||||
mv.grid.OnInvalidate(func(_ ui.Drawable) {
|
|
||||||
fn(mv)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) Event(event tcell.Event) bool {
|
func (pv *PartViewer) Invalidate() {
|
||||||
if mv.term != nil {
|
pv.term.Invalidate()
|
||||||
return mv.term.Event(event)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) Focus(focus bool) {
|
func (pv *PartViewer) Draw(ctx *ui.Context) {
|
||||||
if mv.term != nil {
|
if pv.err != nil {
|
||||||
mv.term.Focus(focus)
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||||
|
ctx.Printf(0, 0, tcell.StyleDefault, "%s", pv.err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
pv.term.Draw(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeaderView struct {
|
type HeaderView struct {
|
||||||
|
|
Loading…
Reference in New Issue