settings.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	tea "github.com/charmbracelet/bubbletea"
  8	"github.com/charmbracelet/lipgloss"
  9	"github.com/floatpane/matcha/config"
 10)
 11
 12var (
 13	accountItemStyle         = lipgloss.NewStyle().PaddingLeft(2)
 14	selectedAccountItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("42"))
 15	accountEmailStyle        = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
 16	dangerStyle              = lipgloss.NewStyle().Foreground(lipgloss.Color("196"))
 17)
 18
 19// Settings displays the account management screen.
 20type Settings struct {
 21	accounts         []config.Account
 22	cursor           int
 23	confirmingDelete bool
 24	width            int
 25	height           int
 26}
 27
 28// NewSettings creates a new settings model.
 29func NewSettings(accounts []config.Account) *Settings {
 30	return &Settings{
 31		accounts: accounts,
 32		cursor:   0,
 33	}
 34}
 35
 36// Init initializes the settings model.
 37func (m *Settings) Init() tea.Cmd {
 38	return nil
 39}
 40
 41// Update handles messages for the settings model.
 42func (m *Settings) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 43	switch msg := msg.(type) {
 44	case tea.WindowSizeMsg:
 45		m.width = msg.Width
 46		m.height = msg.Height
 47		return m, nil
 48
 49	case tea.KeyMsg:
 50		if m.confirmingDelete {
 51			switch msg.String() {
 52			case "y", "Y":
 53				if m.cursor < len(m.accounts) {
 54					accountID := m.accounts[m.cursor].ID
 55					m.confirmingDelete = false
 56					return m, func() tea.Msg {
 57						return DeleteAccountMsg{AccountID: accountID}
 58					}
 59				}
 60			case "n", "N", "esc":
 61				m.confirmingDelete = false
 62				return m, nil
 63			}
 64			return m, nil
 65		}
 66
 67		switch msg.String() {
 68		case "up", "k":
 69			if m.cursor > 0 {
 70				m.cursor--
 71			}
 72		case "down", "j":
 73			// +2 for "Add Account" and "Signature" options
 74			if m.cursor < len(m.accounts)+1 {
 75				m.cursor++
 76			}
 77		case "d":
 78			// Delete selected account (not the "Add Account" option)
 79			if m.cursor < len(m.accounts) && len(m.accounts) > 0 {
 80				m.confirmingDelete = true
 81			}
 82		case "enter":
 83			// If cursor is on "Add Account"
 84			if m.cursor == len(m.accounts) {
 85				return m, func() tea.Msg { return GoToAddAccountMsg{} }
 86			}
 87			// If cursor is on "Signature"
 88			if m.cursor == len(m.accounts)+1 {
 89				return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
 90			}
 91		case "esc":
 92			return m, func() tea.Msg { return GoToChoiceMenuMsg{} }
 93		}
 94	}
 95	return m, nil
 96}
 97
 98// View renders the settings screen.
 99func (m *Settings) View() string {
100	var b strings.Builder
101
102	b.WriteString(titleStyle.Render("Account Settings") + "\n\n")
103	b.WriteString(listHeader.Render("Your email accounts:"))
104	b.WriteString("\n\n")
105
106	if len(m.accounts) == 0 {
107		b.WriteString(accountEmailStyle.Render("  No accounts configured.\n"))
108		b.WriteString("\n")
109	}
110
111	for i, account := range m.accounts {
112		displayName := account.Email
113		if account.Name != "" {
114			displayName = fmt.Sprintf("%s (%s)", account.Name, account.Email)
115		}
116
117		providerInfo := account.ServiceProvider
118		if account.ServiceProvider == "custom" {
119			providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
120		}
121
122		line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
123
124		if m.cursor == i {
125			b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", line)))
126		} else {
127			b.WriteString(accountItemStyle.Render(fmt.Sprintf("  %s", line)))
128		}
129		b.WriteString("\n")
130	}
131
132	// Add Account option
133	addAccountText := "Add New Account"
134	if m.cursor == len(m.accounts) {
135		b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", addAccountText)))
136	} else {
137		b.WriteString(accountItemStyle.Render(fmt.Sprintf("  %s", addAccountText)))
138	}
139	b.WriteString("\n")
140
141	// Signature option
142	signatureText := "Edit Signature"
143	if config.HasSignature() {
144		signatureText = "Edit Signature (configured)"
145	}
146	if m.cursor == len(m.accounts)+1 {
147		b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", signatureText)))
148	} else {
149		b.WriteString(accountItemStyle.Render(fmt.Sprintf("  %s", signatureText)))
150	}
151	b.WriteString("\n\n")
152
153	b.WriteString(helpStyle.Render("↑/↓: navigate • enter: select • d: delete account • esc: back"))
154
155	if m.confirmingDelete {
156		accountName := m.accounts[m.cursor].Email
157		dialog := DialogBoxStyle.Render(
158			lipgloss.JoinVertical(lipgloss.Center,
159				dangerStyle.Render("Delete account?"),
160				accountEmailStyle.Render(accountName),
161				HelpStyle.Render("\n(y/n)"),
162			),
163		)
164		return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog)
165	}
166
167	return docStyle.Render(b.String())
168}
169
170// UpdateAccounts updates the list of accounts.
171func (m *Settings) UpdateAccounts(accounts []config.Account) {
172	m.accounts = accounts
173	if m.cursor >= len(accounts) {
174		m.cursor = len(accounts)
175	}
176}