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}