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					IMAPServer:   acc.IMAPServer,
 63					IMAPPort:     acc.IMAPPort,
 64					SMTPServer:   acc.SMTPServer,
 65					SMTPPort:     acc.SMTPPort,
 66					Insecure:     acc.Insecure,
 67					Protocol:     acc.Protocol,
 68					JMAPEndpoint: acc.JMAPEndpoint,
 69					POP3Server:   acc.POP3Server,
 70					POP3Port:     acc.POP3Port,
 71				}
 72			}
 73		}
 74	case "s": // Edit account signature
 75		if m.accountsCursor < len(m.cfg.Accounts) {
 76			return m, func() tea.Msg { return GoToSignatureEditorMsg{AccountID: m.cfg.Accounts[m.accountsCursor].ID} }
 77		}
 78	case "c": // Quick shortcut to crypto config
 79		if m.accountsCursor < len(m.cfg.Accounts) {
 80			m.enterCryptoConfig()
 81			return m, textinput.Blink
 82		}
 83	case "enter":
 84		if m.accountsCursor == len(m.cfg.Accounts) {
 85			return m, func() tea.Msg { return GoToAddAccountMsg{} }
 86		} else if m.accountsCursor < len(m.cfg.Accounts) {
 87			m.enterCryptoConfig()
 88			return m, textinput.Blink
 89		}
 90	}
 91	return m, nil
 92}
 93
 94func (m *Settings) enterCryptoConfig() {
 95	m.isCryptoConfig = true
 96	m.editingAccountIdx = m.accountsCursor
 97	acc := m.cfg.Accounts[m.accountsCursor]
 98
 99	m.smimeCertInput.SetValue(acc.SMIMECert)
100	m.smimeKeyInput.SetValue(acc.SMIMEKey)
101	m.pgpPublicKeyInput.SetValue(acc.PGPPublicKey)
102	m.pgpPrivateKeyInput.SetValue(acc.PGPPrivateKey)
103	if acc.PGPKeySource == "" {
104		m.pgpKeySource = "file"
105	} else {
106		m.pgpKeySource = acc.PGPKeySource
107	}
108	m.pgpPINInput.SetValue(acc.PGPPIN)
109
110	m.cryptoFocusIndex = 0
111	m.smimeCertInput.Focus()
112	m.smimeKeyInput.Blur()
113	m.pgpPublicKeyInput.Blur()
114	m.pgpPrivateKeyInput.Blur()
115	m.pgpPINInput.Blur()
116}
117
118func (m *Settings) viewAccounts() string {
119	if m.isCryptoConfig {
120		return m.viewSMIMEConfig()
121	}
122
123	var b strings.Builder
124	b.WriteString(titleStyle.Render(t("settings_accounts.title")) + "\n\n")
125
126	if len(m.cfg.Accounts) == 0 {
127		b.WriteString(accountEmailStyle.Render("  " + t("settings_accounts.no_accounts") + "\n\n"))
128	}
129
130	for i, account := range m.cfg.Accounts {
131		displayName := account.Email
132		if account.Name != "" {
133			displayName = fmt.Sprintf("%s (%s)", account.Name, account.FetchEmail)
134		}
135
136		providerInfo := account.ServiceProvider
137		if account.ServiceProvider == "custom" {
138			providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
139		}
140
141		if account.SMIMECert != "" && account.SMIMEKey != "" {
142			providerInfo += " [S/MIME Configured]"
143		}
144		if account.PGPPublicKey != "" && account.PGPPrivateKey != "" {
145			providerInfo += " [PGP Configured]"
146		}
147		if config.HasAccountSignature(&account) {
148			providerInfo += " [Signature]"
149		}
150
151		line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
152
153		cursor := "  "
154		style := accountItemStyle
155		if m.accountsCursor == i {
156			cursor = "> "
157			style = selectedAccountItemStyle
158		}
159
160		b.WriteString(style.Render(cursor+line) + "\n")
161	}
162
163	// Add Account option
164	cursor := "  "
165	style := accountItemStyle
166	if m.accountsCursor == len(m.cfg.Accounts) {
167		cursor = "> "
168		style = selectedAccountItemStyle
169	}
170	b.WriteString(style.Render(cursor+t("settings_accounts.add_account")) + "\n\n")
171
172	b.WriteString(helpStyle.Render(t("settings_accounts.help")))
173
174	if m.confirmingDelete {
175		accountName := m.cfg.Accounts[m.accountsCursor].Email
176		dialog := DialogBoxStyle.Render(
177			lipgloss.JoinVertical(lipgloss.Center,
178				dangerStyle.Render("Delete account?"),
179				accountEmailStyle.Render(accountName),
180				HelpStyle.Render("\n(y/n)"),
181			),
182		)
183		// Try to overlay dialog in a reasonable way, since we don't have full screen width access easily here.
184		// Just append it.
185		b.WriteString("\n\n" + dialog)
186	}
187
188	return b.String()
189}