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