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