1package tui
2
3import (
4 "fmt"
5 "strings"
6 "time"
7
8 "github.com/andrinoff/email-cli/config"
9 "github.com/andrinoff/email-cli/sender"
10 "github.com/charmbracelet/bubbles/textarea"
11 "github.com/charmbracelet/bubbles/textinput"
12 tea "github.com/charmbracelet/bubbletea"
13)
14
15type composerModel struct {
16 recipientInput textinput.Model
17 subjectInput textinput.Model
18 bodyArea textarea.Model
19 index int
20 sending bool
21 sent bool
22 err error
23 config *config.Config
24}
25
26func initialComposerModel(cfg *config.Config) composerModel {
27 ti := textinput.New()
28 ti.Placeholder = "recipient@example.com"
29 ti.Focus()
30 ti.CharLimit = 156
31 ti.Width = 50
32 ti.Prompt = "To: "
33 si := textinput.New()
34 si.Placeholder = "Hello there!"
35 si.CharLimit = 156
36 si.Width = 50
37 si.Prompt = "Subject: "
38 ta := textarea.New()
39 ta.Placeholder = "Dear friend, ..."
40 ta.SetWidth(50)
41 ta.SetHeight(10)
42 return composerModel{
43 recipientInput: ti,
44 subjectInput: si,
45 bodyArea: ta,
46 index: 0,
47 config: cfg,
48 err: nil,
49 }
50}
51
52func (m composerModel) Init() tea.Cmd { return textinput.Blink }
53
54func (m composerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
55 var cmds []tea.Cmd
56 switch msg := msg.(type) {
57 case tea.KeyMsg:
58 switch msg.Type {
59 case tea.KeyCtrlC, tea.KeyEsc:
60 return m, tea.Quit
61 case tea.KeyCtrlS:
62 if !m.sending {
63 m.sending = true
64 to := []string{m.recipientInput.Value()}
65 subject := m.subjectInput.Value()
66 body := m.bodyArea.Value()
67 return m, func() tea.Msg {
68 err := sender.SendEmail(m.config, to, subject, body)
69 if err != nil {
70 return err
71 }
72 return "sent"
73 }
74 }
75 case tea.KeyTab, tea.KeyShiftTab:
76 if msg.Type == tea.KeyTab {
77 m.index = (m.index + 1) % 3
78 } else {
79 m.index--
80 if m.index < 0 {
81 m.index = 2
82 }
83 }
84 m.recipientInput.Blur()
85 m.subjectInput.Blur()
86 m.bodyArea.Blur()
87 switch m.index {
88 case 0:
89 m.recipientInput.Focus()
90 case 1:
91 m.subjectInput.Focus()
92 case 2:
93 m.bodyArea.Focus()
94 }
95 return m, tea.Batch(cmds...)
96 }
97 case string:
98 if msg == "sent" {
99 m.sending = false
100 m.sent = true
101 return m, tea.Sequence(func() tea.Msg { time.Sleep(time.Second); return nil }, tea.Quit)
102 }
103 case error:
104 m.sending = false
105 m.err = msg
106 return m, nil
107 }
108 var cmd tea.Cmd
109 switch m.index {
110 case 0:
111 m.recipientInput, cmd = m.recipientInput.Update(msg)
112 case 1:
113 m.subjectInput, cmd = m.subjectInput.Update(msg)
114 case 2:
115 m.bodyArea, cmd = m.bodyArea.Update(msg)
116 }
117 cmds = append(cmds, cmd)
118 return m, tea.Batch(cmds...)
119}
120
121func (m composerModel) View() string {
122 if m.err != nil {
123 return InfoStyle.Render(fmt.Sprintf("\n Error: %v \n\n Press any key to exit.", m.err))
124 }
125 if m.sent {
126 return SuccessStyle.Render("\n ✓ Email sent successfully! \n\n")
127 }
128 if m.sending {
129 return InfoStyle.Render("\n Sending email... \n\n")
130 }
131 var s strings.Builder
132 s.WriteString("\n")
133 s.WriteString(m.recipientInput.View() + "\n")
134 s.WriteString(m.subjectInput.View() + "\n")
135 s.WriteString(m.bodyArea.View())
136 help := fmt.Sprintf("\n\n %s | %s | %s \n", "Tab: Next Field", "Esc: Quit", "Ctrl+S: Send")
137 s.WriteString(HelpStyle.Render(help))
138 return DialogBoxStyle.Render(s.String())
139}
140
141// RunComposer starts the email composer UI.
142func RunComposer(cfg *config.Config) error {
143 p := tea.NewProgram(initialComposerModel(cfg))
144 _, err := p.Run()
145 return err
146}