settings_accounts.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"charm.land/bubbles/v2/textinput"
  8	tea "charm.land/bubbletea/v2"
  9	"charm.land/lipgloss/v2"
 10	"github.com/floatpane/matcha/config"
 11)
 12
 13func (m *Settings) updateAccounts(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
 14	if m.isCryptoConfig {
 15		var m2 *Settings
 16		var cmd tea.Cmd
 17		m2, cmd = m.updateSMIMEConfig(msg)
 18		return m2, cmd
 19	}
 20
 21	if m.confirmingDelete {
 22		switch msg.String() {
 23		case "y", "Y":
 24			if m.accountsCursor < len(m.cfg.Accounts) {
 25				accountID := m.cfg.Accounts[m.accountsCursor].ID
 26				m.confirmingDelete = false
 27				return m, func() tea.Msg {
 28					return DeleteAccountMsg{AccountID: accountID}
 29				}
 30			}
 31		case "n", "N", "esc":
 32			m.confirmingDelete = false
 33			return m, nil
 34		}
 35		return m, nil
 36	}
 37
 38	switch msg.String() {
 39	case "up", "k":
 40		if m.accountsCursor > 0 {
 41			m.accountsCursor--
 42		}
 43	case "down", "j":
 44		if m.accountsCursor < len(m.cfg.Accounts) {
 45			m.accountsCursor++
 46		}
 47	case "d":
 48		if m.accountsCursor < len(m.cfg.Accounts) && len(m.cfg.Accounts) > 0 {
 49			m.confirmingDelete = true
 50		}
 51	case "e":
 52		if m.accountsCursor < len(m.cfg.Accounts) {
 53			acc := m.cfg.Accounts[m.accountsCursor]
 54			return m, func() tea.Msg {
 55				return GoToEditAccountMsg{
 56					AccountID:    acc.ID,
 57					Provider:     acc.ServiceProvider,
 58					Name:         acc.Name,
 59					Email:        acc.Email,
 60					FetchEmail:   acc.FetchEmail,
 61					SendAsEmail:  acc.SendAsEmail,
 62					CatchAll:     acc.CatchAll,
 63					IMAPServer:   acc.IMAPServer,
 64					IMAPPort:     acc.IMAPPort,
 65					SMTPServer:   acc.SMTPServer,
 66					SMTPPort:     acc.SMTPPort,
 67					Insecure:     acc.Insecure,
 68					Protocol:     acc.Protocol,
 69					JMAPEndpoint: acc.JMAPEndpoint,
 70					POP3Server:   acc.POP3Server,
 71					POP3Port:     acc.POP3Port,
 72				}
 73			}
 74		}
 75	case "s": // Edit account signature
 76		if m.accountsCursor < len(m.cfg.Accounts) {
 77			return m, func() tea.Msg { return GoToSignatureEditorMsg{AccountID: m.cfg.Accounts[m.accountsCursor].ID} }
 78		}
 79	case "c": // Quick shortcut to crypto config
 80		if m.accountsCursor < len(m.cfg.Accounts) {
 81			m.enterCryptoConfig()
 82			return m, textinput.Blink
 83		}
 84	case "enter":
 85		if m.accountsCursor == len(m.cfg.Accounts) {
 86			return m, func() tea.Msg { return GoToAddAccountMsg{} }
 87		} else if m.accountsCursor < len(m.cfg.Accounts) {
 88			m.enterCryptoConfig()
 89			return m, textinput.Blink
 90		}
 91	}
 92	return m, nil
 93}
 94
 95func (m *Settings) enterCryptoConfig() {
 96	m.isCryptoConfig = true
 97	m.editingAccountIdx = m.accountsCursor
 98	acc := m.cfg.Accounts[m.accountsCursor]
 99
100	m.smimeCertInput.SetValue(acc.SMIMECert)
101	m.smimeKeyInput.SetValue(acc.SMIMEKey)
102	m.pgpPublicKeyInput.SetValue(acc.PGPPublicKey)
103	m.pgpPrivateKeyInput.SetValue(acc.PGPPrivateKey)
104	if acc.PGPKeySource == "" {
105		m.pgpKeySource = "file"
106	} else {
107		m.pgpKeySource = acc.PGPKeySource
108	}
109	m.pgpPINInput.SetValue(acc.PGPPIN)
110
111	m.cryptoFocusIndex = 0
112	m.smimeCertInput.Focus()
113	m.smimeKeyInput.Blur()
114	m.pgpPublicKeyInput.Blur()
115	m.pgpPrivateKeyInput.Blur()
116	m.pgpPINInput.Blur()
117}
118
119func (m *Settings) viewAccounts() string {
120	if m.isCryptoConfig {
121		return m.viewSMIMEConfig()
122	}
123
124	var b strings.Builder
125	b.WriteString(titleStyle.Render(t("settings_accounts.title")) + "\n\n")
126
127	if len(m.cfg.Accounts) == 0 {
128		b.WriteString(accountEmailStyle.Render("  " + t("settings_accounts.no_accounts") + "\n\n"))
129	}
130
131	for i, account := range m.cfg.Accounts {
132		displayName := account.Email
133		if account.Name != "" {
134			displayName = fmt.Sprintf("%s (%s)", account.Name, account.FetchEmail)
135		}
136
137		providerInfo := account.ServiceProvider
138		if account.ServiceProvider == "custom" {
139			providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
140		}
141
142		if account.SMIMECert != "" && account.SMIMEKey != "" {
143			providerInfo += " [S/MIME Configured]"
144		}
145		if account.PGPPublicKey != "" && account.PGPPrivateKey != "" {
146			providerInfo += " [PGP Configured]"
147		}
148		if config.HasAccountSignature(&account) {
149			providerInfo += " [Signature]"
150		}
151		if account.CatchAll {
152			providerInfo += " [Catch-All]"
153		}
154
155		line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
156
157		cursor := "  "
158		style := accountItemStyle
159		if m.accountsCursor == i {
160			cursor = "> "
161			style = selectedAccountItemStyle
162		}
163
164		b.WriteString(style.Render(cursor+line) + "\n")
165	}
166
167	// Add Account option
168	cursor := "  "
169	style := accountItemStyle
170	if m.accountsCursor == len(m.cfg.Accounts) {
171		cursor = "> "
172		style = selectedAccountItemStyle
173	}
174	b.WriteString(style.Render(cursor+t("settings_accounts.add_account")) + "\n\n")
175
176	b.WriteString(helpStyle.Render(t("settings_accounts.help")))
177
178	if m.confirmingDelete {
179		accountName := m.cfg.Accounts[m.accountsCursor].Email
180		dialog := DialogBoxStyle.Render(
181			lipgloss.JoinVertical(lipgloss.Center,
182				dangerStyle.Render("Delete account?"),
183				accountEmailStyle.Render(accountName),
184				HelpStyle.Render("\n(y/n)"),
185			),
186		)
187		// Try to overlay dialog in a reasonable way, since we don't have full screen width access easily here.
188		// Just append it.
189		b.WriteString("\n\n" + dialog)
190	}
191
192	return b.String()
193}