settings_crypto.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	tea "charm.land/bubbletea/v2"
  8	"github.com/floatpane/matcha/config"
  9)
 10
 11const cryptoConfigMaxFocus = 9
 12
 13func (m *Settings) updateSMIMEConfig(msg tea.KeyPressMsg) (*Settings, tea.Cmd) {
 14	var cmds []tea.Cmd
 15
 16	key := msg.Key()
 17	isEnter := key.Code == tea.KeyEnter || key.Code == tea.KeyReturn || key.Code == tea.KeyKpEnter
 18	isSpace := key.Code == tea.KeySpace
 19
 20	setFocus := func(next int) tea.Cmd {
 21		m.cryptoFocusIndex = next
 22		m.smimeCertInput.Blur()
 23		m.smimeKeyInput.Blur()
 24		m.pgpPublicKeyInput.Blur()
 25		m.pgpPrivateKeyInput.Blur()
 26		m.pgpPINInput.Blur()
 27
 28		switch m.cryptoFocusIndex {
 29		case 0:
 30			return m.smimeCertInput.Focus()
 31		case 1:
 32			return m.smimeKeyInput.Focus()
 33		case 3:
 34			return m.pgpPublicKeyInput.Focus()
 35		case 4:
 36			return m.pgpPrivateKeyInput.Focus()
 37		case 6:
 38			return m.pgpPINInput.Focus()
 39		}
 40		return nil
 41	}
 42
 43	switch msg.String() {
 44	case "esc":
 45		m.isCryptoConfig = false
 46		return m, nil
 47	case "tab", keyShiftTab, "up", keyDown:
 48		if msg.String() == keyShiftTab || msg.String() == "up" {
 49			m.cryptoFocusIndex--
 50			if m.cryptoFocusIndex < 0 {
 51				m.cryptoFocusIndex = cryptoConfigMaxFocus
 52			}
 53		} else {
 54			m.cryptoFocusIndex++
 55			if m.cryptoFocusIndex > cryptoConfigMaxFocus {
 56				m.cryptoFocusIndex = 0
 57			}
 58		}
 59		if m.cryptoFocusIndex == 6 && m.pgpKeySource != keyYubikey {
 60			if msg.String() == keyShiftTab || msg.String() == "up" {
 61				m.cryptoFocusIndex = 5
 62			} else {
 63				m.cryptoFocusIndex = 7
 64			}
 65		}
 66		cmds = append(cmds, setFocus(m.cryptoFocusIndex))
 67		return m, tea.Batch(cmds...)
 68	}
 69
 70	if isEnter {
 71		switch m.cryptoFocusIndex {
 72		case 8: // Save
 73			m.cfg.Accounts[m.editingAccountIdx].SMIMECert = m.smimeCertInput.Value()
 74			m.cfg.Accounts[m.editingAccountIdx].SMIMEKey = m.smimeKeyInput.Value()
 75			m.cfg.Accounts[m.editingAccountIdx].PGPPublicKey = m.pgpPublicKeyInput.Value()
 76			m.cfg.Accounts[m.editingAccountIdx].PGPPrivateKey = m.pgpPrivateKeyInput.Value()
 77			m.cfg.Accounts[m.editingAccountIdx].PGPKeySource = m.pgpKeySource
 78			m.cfg.Accounts[m.editingAccountIdx].PGPPIN = m.pgpPINInput.Value()
 79			_ = config.SaveConfig(m.cfg)
 80			m.isCryptoConfig = false
 81			return m, nil
 82		case 9: // Cancel
 83			m.isCryptoConfig = false
 84			return m, nil
 85		default:
 86			// advance to next
 87			next := m.cryptoFocusIndex + 1
 88			if next == 6 && m.pgpKeySource != keyYubikey {
 89				next = 7
 90			}
 91			cmds = append(cmds, setFocus(next))
 92			return m, tea.Batch(cmds...)
 93		}
 94	}
 95
 96	if isSpace {
 97		switch m.cryptoFocusIndex {
 98		case 2:
 99			m.cfg.Accounts[m.editingAccountIdx].SMIMESignByDefault = !m.cfg.Accounts[m.editingAccountIdx].SMIMESignByDefault
100			return m, nil
101		case 5:
102			if m.pgpKeySource == "file" {
103				m.pgpKeySource = keyYubikey
104			} else {
105				m.pgpKeySource = "file"
106			}
107			return m, nil
108		case 7:
109			m.cfg.Accounts[m.editingAccountIdx].PGPSignByDefault = !m.cfg.Accounts[m.editingAccountIdx].PGPSignByDefault
110			return m, nil
111		}
112	}
113
114	return m, tea.Batch(cmds...)
115}
116
117func (m *Settings) viewSMIMEConfig() string {
118	var b strings.Builder
119	account := m.cfg.Accounts[m.editingAccountIdx]
120	b.WriteString(titleStyle.Render(fmt.Sprintf("Crypto Config: %s", account.FetchEmail)) + "\n\n")
121
122	renderField := func(index int, label, content string) {
123		if m.cryptoFocusIndex == index {
124			b.WriteString(m.contentFocusStyle().Render(label) + "\n")
125		} else {
126			b.WriteString(settingsBlurredStyle.Render(label) + "\n")
127		}
128		b.WriteString(content + "\n\n")
129	}
130
131	// S/MIME
132	b.WriteString(settingsFocusedStyle.Render("S/MIME") + "\n")
133	renderField(0, "Certificate (PEM) Path:", m.smimeCertInput.View())
134	renderField(1, "Private Key (PEM) Path:", m.smimeKeyInput.View())
135	smimeSign := "OFF"
136	if account.SMIMESignByDefault {
137		smimeSign = "ON"
138	}
139	renderField(2, "Sign By Default:", smimeSign)
140
141	// PGP
142	b.WriteString(settingsFocusedStyle.Render("PGP") + "\n")
143	renderField(3, "Public Key Path:", m.pgpPublicKeyInput.View())
144	renderField(4, "Private Key Path:", m.pgpPrivateKeyInput.View())
145
146	keySource := "File"
147	if m.pgpKeySource == keyYubikey {
148		keySource = "YubiKey"
149	}
150	renderField(5, "Key Source:", keySource)
151
152	if m.pgpKeySource == keyYubikey {
153		renderField(6, "YubiKey PIN:", m.pgpPINInput.View())
154	}
155
156	pgpSign := "OFF"
157	if account.PGPSignByDefault {
158		pgpSign = "ON"
159	}
160	renderField(7, "Sign By Default:", pgpSign)
161
162	saveBtn := "[ Save ]"
163	cancelBtn := "[ Cancel ]"
164	if m.cryptoFocusIndex == 8 {
165		saveBtn = m.contentFocusStyle().Render(saveBtn)
166	} else {
167		saveBtn = settingsBlurredStyle.Render(saveBtn)
168	}
169	if m.cryptoFocusIndex == 9 {
170		cancelBtn = m.contentFocusStyle().Render(cancelBtn)
171	} else {
172		cancelBtn = settingsBlurredStyle.Render(cancelBtn)
173	}
174
175	b.WriteString(saveBtn + "  " + cancelBtn + "\n\n")
176	b.WriteString(helpStyle.Render("tab: next • enter: next/save • space: toggle • esc: cancel"))
177
178	return b.String()
179}