190 lines
3.9 KiB
Go
190 lines
3.9 KiB
Go
package inbox
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/bubbles/list"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
type EmailItem struct {
|
|
UID uint32
|
|
From string
|
|
FromName string
|
|
Subject string
|
|
Date time.Time
|
|
Preview string
|
|
Account string
|
|
AccountID string
|
|
}
|
|
|
|
func (e EmailItem) FilterValue() string {
|
|
return fmt.Sprintf("%s %s %s %s", e.Account, e.FromName, e.From, e.Subject)
|
|
}
|
|
|
|
func (e EmailItem) Title() string {
|
|
return fmt.Sprintf("%s · %s - %s", e.Account, e.FromName, e.Subject)
|
|
}
|
|
|
|
func (e EmailItem) Description() string {
|
|
return e.Preview
|
|
}
|
|
|
|
func formatTimeAgo(d time.Duration) string {
|
|
if d < time.Minute {
|
|
return "刚刚"
|
|
}
|
|
if d < time.Hour {
|
|
mins := int(d.Minutes())
|
|
return fmt.Sprintf("%d分钟前", mins)
|
|
}
|
|
if d < 24*time.Hour {
|
|
hours := int(d.Hours())
|
|
return fmt.Sprintf("%d小时前", hours)
|
|
}
|
|
if d < 7*24*time.Hour {
|
|
days := int(d.Hours() / 24)
|
|
return fmt.Sprintf("%d天前", days)
|
|
}
|
|
return d.Truncate(24 * time.Hour).String()
|
|
}
|
|
|
|
type InboxModel struct {
|
|
list list.Model
|
|
emails []EmailItem
|
|
loading bool
|
|
err error
|
|
selectedEmail *EmailItem
|
|
}
|
|
|
|
func NewInboxModel() *InboxModel {
|
|
l := list.New(nil, emailDelegate{}, 0, 10)
|
|
l.Title = "收件箱"
|
|
l.Styles.Title = inboxTitleStyle
|
|
l.Styles.NoItems = inboxNoItemsStyle
|
|
l.SetShowHelp(true)
|
|
l.SetShowPagination(false)
|
|
|
|
return &InboxModel{
|
|
list: l,
|
|
emails: []EmailItem{},
|
|
}
|
|
}
|
|
|
|
func (m *InboxModel) SetEmails(emails []EmailItem) {
|
|
m.emails = emails
|
|
items := make([]list.Item, len(emails))
|
|
for i, e := range emails {
|
|
items[i] = e
|
|
}
|
|
m.list.SetItems(items)
|
|
m.list.Title = fmt.Sprintf("收件箱 (%d 封新邮件)", len(items))
|
|
}
|
|
|
|
func (m InboxModel) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (m *InboxModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
if msg.String() == "ctrl+c" {
|
|
return m, tea.Quit
|
|
}
|
|
case tea.WindowSizeMsg:
|
|
m.list.SetWidth(msg.Width)
|
|
}
|
|
var cmd tea.Cmd
|
|
m.list, cmd = m.list.Update(msg)
|
|
return m, cmd
|
|
}
|
|
|
|
func (m InboxModel) View() string {
|
|
if m.loading {
|
|
return loadingStyle.Render("正在加载邮件...")
|
|
}
|
|
if m.err != nil {
|
|
return errorStyle.Render(fmt.Sprintf("错误: %v", m.err))
|
|
}
|
|
return m.list.View()
|
|
}
|
|
|
|
func (m *InboxModel) SetLoading(loading bool) {
|
|
m.loading = loading
|
|
}
|
|
|
|
func (m *InboxModel) SetError(err error) {
|
|
m.err = err
|
|
}
|
|
|
|
func (m InboxModel) SelectedEmail() *EmailItem {
|
|
idx := m.list.Index()
|
|
if idx >= 0 && idx < len(m.emails) {
|
|
return &m.emails[idx]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
inboxTitleStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("86")).
|
|
Bold(true)
|
|
|
|
inboxNoItemsStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("241")).
|
|
Padding(1, 2)
|
|
|
|
loadingStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("86")).
|
|
Padding(1, 2)
|
|
|
|
errorStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("196")).
|
|
Padding(1, 2)
|
|
)
|
|
|
|
type emailDelegate struct{}
|
|
|
|
func (d emailDelegate) Height() int { return 1 }
|
|
func (d emailDelegate) Spacing() int { return 0 }
|
|
func (d emailDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
|
|
return nil
|
|
}
|
|
func (d emailDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
|
|
email, ok := item.(EmailItem)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
isSelected := index == m.Index()
|
|
|
|
titleStyle := emailTitleStyle
|
|
descStyle := emailDescStyle
|
|
|
|
if isSelected {
|
|
titleStyle = emailTitleStyleSelected
|
|
descStyle = emailDescStyleSelected
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s\n%s", titleStyle.Render(email.Title()), descStyle.Render(email.Description()))
|
|
}
|
|
|
|
var (
|
|
emailTitleStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("255"))
|
|
|
|
emailTitleStyleSelected = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("255")).
|
|
Background(lipgloss.Color("68"))
|
|
|
|
emailDescStyle = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("241"))
|
|
|
|
emailDescStyleSelected = lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("186")).
|
|
Background(lipgloss.Color("68"))
|
|
)
|