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			// +1 for "Add Account" option
 74			if m.cursor < len(m.accounts) {
 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		case "esc":
 88			return m, func() tea.Msg { return GoToChoiceMenuMsg{} }
 89		}
 90	}
 91	return m, nil
 92}
 93
 94// View renders the settings screen.
 95func (m *Settings) View() string {
 96	var b strings.Builder
 97
 98	b.WriteString(titleStyle.Render("Account Settings") + "\n\n")
 99	b.WriteString(listHeader.Render("Your email accounts:"))
100	b.WriteString("\n\n")
101
102	if len(m.accounts) == 0 {
103		b.WriteString(accountEmailStyle.Render("  No accounts configured.\n"))
104		b.WriteString("\n")
105	}
106
107	for i, account := range m.accounts {
108		displayName := account.Email
109		if account.Name != "" {
110			displayName = fmt.Sprintf("%s (%s)", account.Name, account.Email)
111		}
112
113		providerInfo := account.ServiceProvider
114		if account.ServiceProvider == "custom" {
115			providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
116		}
117
118		line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
119
120		if m.cursor == i {
121			b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", line)))
122		} else {
123			b.WriteString(accountItemStyle.Render(fmt.Sprintf("  %s", line)))
124		}
125		b.WriteString("\n")
126	}
127
128	// Add Account option
129	addAccountText := "Add New Account"
130	if m.cursor == len(m.accounts) {
131		b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", addAccountText)))
132	} else {
133		b.WriteString(accountItemStyle.Render(fmt.Sprintf("  %s", addAccountText)))
134	}
135	b.WriteString("\n\n")
136
137	b.WriteString(helpStyle.Render("↑/↓: navigate • enter: select • d: delete account • esc: back"))
138
139	if m.confirmingDelete {
140		accountName := m.accounts[m.cursor].Email
141		dialog := DialogBoxStyle.Render(
142			lipgloss.JoinVertical(lipgloss.Center,
143				dangerStyle.Render("Delete account?"),
144				accountEmailStyle.Render(accountName),
145				HelpStyle.Render("\n(y/n)"),
146			),
147		)
148		return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog)
149	}
150
151	return docStyle.Render(b.String())
152}
153
154// UpdateAccounts updates the list of accounts.
155func (m *Settings) UpdateAccounts(accounts []config.Account) {
156	m.accounts = accounts
157	if m.cursor >= len(accounts) {
158		m.cursor = len(accounts)
159	}
160}