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 keyDown, "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 MaildirPath: acc.MaildirPath,
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 keyEnter:
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 if account.CatchAll {
151 providerInfo += " [Catch-All]"
152 }
153
154 line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
155
156 selected := m.accountsCursor == i
157 cursor := m.contentCursor(selected)
158 style := m.contentItemStyle(selected)
159
160 b.WriteString(style.Render(cursor+line) + "\n")
161 }
162
163 // Add Account option
164 selected := m.accountsCursor == len(m.cfg.Accounts)
165 cursor := m.contentCursor(selected)
166 style := m.contentItemStyle(selected)
167 b.WriteString(style.Render(cursor+t("settings_accounts.add_account")) + "\n\n")
168
169 b.WriteString(helpStyle.Render(t("settings_accounts.help")))
170
171 if m.confirmingDelete {
172 accountName := m.cfg.Accounts[m.accountsCursor].Email
173 dialog := DialogBoxStyle.Render(
174 lipgloss.JoinVertical(lipgloss.Center,
175 dangerStyle.Render("Delete account?"),
176 accountEmailStyle.Render(accountName),
177 HelpStyle.Render("\n(y/n)"),
178 ),
179 )
180 // Try to overlay dialog in a reasonable way, since we don't have full screen width access easily here.
181 // Just append it.
182 b.WriteString("\n\n" + dialog)
183 }
184
185 return b.String()
186}