Files
MujicaUI/mail_reader.go
T

136 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mujica
import (
"log/slog"
"strings"
"time"
"github.com/egoist/mygo/ui"
"mujicaui/theme"
)
// MailFormat selects plain text or safe Markdown.
type MailFormat int
const (
MailPlain MailFormat = iota
// MailMarkdown uses MarkdownView’s safe subset.
MailMarkdown
)
// MailAttachment is a file sent with a mail.
type MailAttachment struct {
Name string
Size int64
Type string
}
// Mail supplies headers, body and attachments.
type Mail struct {
From string
To, Cc []string
Subject string
Date time.Time
Body string
Format MailFormat
Attachments []MailAttachment
}
// MailReaderResult is a MailReader's element and this frame's events.
type MailReaderResult struct {
Element *ui.Element
// Link contains the activated URL, or an empty string.
Link string
// Opened and Downloaded are attachment indexes, -1 for none.
Opened, Downloaded int
}
type mailQuotesKey struct{}
// MailReader displays headers, body, folded quotes and attachments.
func MailReader(c *ui.Context, m Mail) MailReaderResult {
if m.From == "" || m.Subject == "" {
panic("mujica: MailReader needs From and Subject")
}
if m.Format != MailPlain && m.Format != MailMarkdown {
panic("mujica: unknown MailFormat")
}
k := Tokens(c)
u := Density(c).Unit()
r := MailReaderResult{Opened: -1, Downloaded: -1}
col := ui.Column(c).Gap(u * 4).MinWidth(0).Role(ui.RoleGroup).Label(m.Subject)
r.Element = col
small := FontSize(c, theme.CaptionSize)
col.Children(func() {
ui.Text(c, m.Subject).Font(theme.SerifFont).FontSize(FontSize(c, theme.H2Size)).FontWeight(600)
ui.Row(c).Gap(u * 3).AlignItems(ui.Start).Children(func() {
Avatar(c, m.From, AvatarOptions{Size: 36})
ui.Column(c).Gap(2).Grow(1).MinWidth(0).Children(func() {
ui.Text(c, m.From).FontWeight(600).Selectable().FocusRing(false)
for _, line := range [][2]string{{"mail_reader.to", strings.Join(m.To, ", ")}, {"mail_reader.cc", strings.Join(m.Cc, ", ")}} {
if line[1] != "" {
ui.Text(c, msg(c, line[0])+" "+line[1]).FontSize(small).TextColor(k.TextMuted).Selectable().FocusRing(false)
}
}
})
if !m.Date.IsZero() {
ui.Text(c, messagingDate(c, m.Date, "mail_reader.date")).FontSize(small).TextColor(k.TextMuted).FontFeatures("tnum").SingleLine()
}
})
ui.Box(c).Height(1).Background(k.Border)
if m.Format == MailMarkdown {
if url, ok := MarkdownView(c, m.Body).Link(); ok {
r.Link = url
}
} else {
mailPlainBody(c, ui.Local(col, mailQuotesKey{}, func() map[string]bool { return map[string]bool{} }), m.Body)
}
if len(m.Attachments) > 0 {
ui.Text(c, msg(c, "mail_reader.attachments")).FontSize(small).FontWeight(600).TextColor(k.TextMuted)
ui.Row(c).Gap(u * 2).Wrap().Children(func() {
for i, a := range m.Attachments {
f := FileMessage(c, FileMessageOptions(a))
f.Element.Key(i)
if f.Opened() {
r.Opened = i
}
if f.Downloaded() {
r.Downloaded = i
}
}
})
}
})
if r.Link != "" || r.Opened >= 0 || r.Downloaded >= 0 {
slog.Debug("mujica: mail_reader action", "opened", r.Opened, "downloaded", r.Downloaded)
}
return r
}
// mailPlainBody shows plain text, folding each run of quoted lines.
func mailPlainBody(c *ui.Context, open *map[string]bool, body string) {
lines := strings.Split(body, "\n")
ui.Column(c).Gap(Density(c).Unit() * 2).MinWidth(0).Children(func() {
for i := 0; i < len(lines); {
j := i
quoted := strings.HasPrefix(lines[i], ">")
for j < len(lines) && strings.HasPrefix(lines[j], ">") == quoted {
j++
}
chunk := strings.Join(lines[i:j], "\n")
ui.Box(c).Key(i).Children(func() {
if quoted {
// Fold state is kept per quote text.
o := (*open)[chunk]
QuotedText(c, chunk, &o)
(*open)[chunk] = o
} else if strings.TrimSpace(chunk) != "" {
ui.Text(c, strings.Trim(chunk, "\n")).Selectable().FocusRing(false).LineHeight(theme.LineHeight)
}
})
i = j
}
})
}