composer.go

  1package tui
  2
  3import (
  4	"github.com/charmbracelet/bubbles/textinput"
  5	tea "github.com/charmbracelet/bubbletea"
  6	"github.com/charmbracelet/lipgloss"
  7)
  8
  9type Composer struct {
 10	focusIndex int
 11	inputs     []textinput.Model
 12	fromAddr   string
 13}
 14
 15func NewComposer(from string) Composer {
 16	m := Composer{
 17		inputs:   make([]textinput.Model, 3),
 18		fromAddr: from,
 19	}
 20
 21	var t textinput.Model
 22	for i := range m.inputs {
 23		t = textinput.New()
 24		t.Cursor.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
 25		t.CharLimit = 0 // no limit
 26
 27		switch i {
 28		case 0:
 29			t.Placeholder = "To"
 30			t.Focus()
 31			t.Prompt = "> "
 32		case 1:
 33			t.Placeholder = "Subject"
 34			t.Prompt = "> "
 35		case 2:
 36			t.Placeholder = "Body..."
 37			t.Prompt = "> "
 38		}
 39		m.inputs[i] = t
 40	}
 41	return m
 42}
 43
 44func (m Composer) Init() tea.Cmd {
 45	return textinput.Blink
 46}
 47
 48func (m Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 49	switch msg := msg.(type) {
 50	case tea.KeyMsg:
 51		s := msg.String()
 52		switch s {
 53		case "tab", "shift+tab", "enter", "up", "down":
 54			if s == "enter" {
 55				// If we're on the last input, send the email
 56				if m.focusIndex == len(m.inputs)-1 {
 57					return m, func() tea.Msg {
 58						return SendEmailMsg{
 59							To:      m.inputs[0].Value(),
 60							Subject: m.inputs[1].Value(),
 61							Body:    m.inputs[2].Value(),
 62						}
 63					}
 64				}
 65			}
 66
 67			// Cycle focus
 68			if s == "up" || s == "shift+tab" {
 69				m.focusIndex--
 70			} else {
 71				m.focusIndex++
 72			}
 73
 74			if m.focusIndex > len(m.inputs)-1 {
 75				m.focusIndex = 0
 76			} else if m.focusIndex < 0 {
 77				m.focusIndex = len(m.inputs) - 1
 78			}
 79
 80			cmds := make([]tea.Cmd, len(m.inputs))
 81			for i := 0; i < len(m.inputs); i++ {
 82				if i == m.focusIndex {
 83					cmds[i] = m.inputs[i].Focus()
 84				} else {
 85					m.inputs[i].Blur()
 86				}
 87			}
 88			return m, tea.Batch(cmds...)
 89		}
 90	}
 91
 92	// Update the focused input
 93	cmd := m.updateInputs(msg)
 94	return m, cmd
 95}
 96
 97func (m *Composer) updateInputs(msg tea.Msg) tea.Cmd {
 98	var cmds = make([]tea.Cmd, len(m.inputs))
 99	for i := range m.inputs {
100		m.inputs[i], cmds[i] = m.inputs[i].Update(msg)
101	}
102	return tea.Batch(cmds...)
103}
104
105func (m Composer) View() string {
106	return lipgloss.JoinVertical(
107		lipgloss.Left,
108		"Compose Email (Press Enter on Body to Send)",
109		m.inputs[0].View(),
110		m.inputs[1].View(),
111		m.inputs[2].View(),
112	)
113}