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}