settings_encryption.go

  1package tui
  2
  3import (
  4	"strings"
  5
  6	tea "charm.land/bubbletea/v2"
  7	"charm.land/lipgloss/v2"
  8	"github.com/floatpane/matcha/config"
  9	"github.com/floatpane/matcha/internal/passwordstrength"
 10)
 11
 12func (m *Settings) updateEncryption(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
 13	isEnabled := config.IsSecureModeEnabled()
 14
 15	if isEnabled {
 16		if m.confirmingDisable {
 17			switch msg.String() {
 18			case "y", "Y":
 19				m.confirmingDisable = false
 20				cfg := m.cfg
 21				return m, func() tea.Msg {
 22					err := config.DisableSecureMode(cfg)
 23					return SecureModeDisabledMsg{Err: err}
 24				}
 25			case "n", "N", "esc":
 26				m.confirmingDisable = false
 27				return m, nil
 28			}
 29			return m, nil
 30		}
 31		if msg.String() == keyEnter {
 32			m.confirmingDisable = true
 33		}
 34		return m, nil
 35	}
 36
 37	switch msg.String() {
 38	case "esc":
 39		m.leaveEncryptionSettings()
 40		return m, nil
 41	case keyLeft:
 42		if m.encryptionInputCursorAtStart() {
 43			m.leaveEncryptionSettings()
 44			return m, nil
 45		}
 46		return m.updateFocusedEncryptionInput(msg)
 47	case "tab", keyShiftTab, keyDown, "up":
 48		if msg.String() == keyShiftTab || msg.String() == "up" {
 49			m.encFocusIndex--
 50			if m.encFocusIndex < 0 {
 51				m.encFocusIndex = 2
 52			}
 53		} else {
 54			m.encFocusIndex++
 55			if m.encFocusIndex > 2 {
 56				m.encFocusIndex = 0
 57			}
 58		}
 59		m.encPasswordInput.Blur()
 60		m.encConfirmInput.Blur()
 61		var cmds []tea.Cmd
 62		if m.encFocusIndex == 0 {
 63			cmds = append(cmds, m.encPasswordInput.Focus())
 64		}
 65		if m.encFocusIndex == 1 {
 66			cmds = append(cmds, m.encConfirmInput.Focus())
 67		}
 68		return m, tea.Batch(cmds...)
 69	case keyEnter:
 70		switch m.encFocusIndex {
 71		case 0:
 72			m.encFocusIndex = 1
 73			m.encPasswordInput.Blur()
 74			return m, m.encConfirmInput.Focus()
 75		case 1:
 76			m.encFocusIndex = 2
 77			m.encConfirmInput.Blur()
 78			return m, nil
 79		case 2:
 80			password := m.encPasswordInput.Value()
 81			confirm := m.encConfirmInput.Value()
 82			if password == "" {
 83				m.encError = t("settings_encryption.error_empty")
 84				return m, nil
 85			}
 86			if password != confirm {
 87				m.encError = t("settings_encryption.error_mismatch")
 88				return m, nil
 89			}
 90			m.encEnabling = true
 91			m.encError = ""
 92			cfg := m.cfg
 93			return m, func() tea.Msg {
 94				err := config.EnableSecureMode(password, cfg)
 95				return SecureModeEnabledMsg{Err: err}
 96			}
 97		}
 98	default:
 99		return m.updateFocusedEncryptionInput(msg)
100	}
101	return m, nil
102}
103
104func (m *Settings) encryptionInputCursorAtStart() bool {
105	switch m.encFocusIndex {
106	case 0:
107		return m.encPasswordInput.Position() == 0
108	case 1:
109		return m.encConfirmInput.Position() == 0
110	default:
111		return false
112	}
113}
114
115func (m *Settings) leaveEncryptionSettings() {
116	m.encPasswordInput.SetValue("")
117	m.encConfirmInput.SetValue("")
118	m.encPasswordStrength = ""
119	m.encPasswordInput.Blur()
120	m.encConfirmInput.Blur()
121	m.encError = ""
122	m.activePane = PaneMenu
123}
124
125func (m *Settings) updateFocusedEncryptionInput(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
126	var cmd tea.Cmd
127	switch m.encFocusIndex {
128	case 0:
129		before := m.encPasswordInput.Value()
130		m.encPasswordInput, cmd = m.encPasswordInput.Update(msg)
131		if m.encPasswordInput.Value() != before {
132			m.handlePasswordChanged()
133		}
134	case 1:
135		m.encConfirmInput, cmd = m.encConfirmInput.Update(msg)
136	}
137	return m, cmd
138}
139
140func (m *Settings) viewEncryption() string {
141	var b strings.Builder
142	isEnabled := config.IsSecureModeEnabled()
143
144	b.WriteString(titleStyle.Render(t("settings_encryption.title")) + "\n\n")
145
146	if isEnabled {
147		if m.confirmingDisable {
148			dialog := DialogBoxStyle.Render(
149				lipgloss.JoinVertical(lipgloss.Center,
150					dangerStyle.Render(t("settings_encryption.disable_confirm")),
151					accountEmailStyle.Render(t("settings_encryption.disable_warning")),
152					HelpStyle.Render("\n(y/n)"),
153				),
154			)
155			b.WriteString(dialog + "\n")
156		} else {
157			b.WriteString(m.contentFocusStyle().Render("  "+t("settings_encryption.enabled")) + "\n\n")
158			b.WriteString(accountEmailStyle.Render("  "+t("settings_encryption.disable_button")) + "\n\n")
159			b.WriteString(helpStyle.Render("enter: disable"))
160		}
161	} else {
162		b.WriteString(accountEmailStyle.Render(t("settings_encryption.disabled")) + "\n\n")
163
164		if m.encFocusIndex == 0 {
165			b.WriteString(m.contentFocusStyle().Render(t("settings_encryption.password_label") + "\n"))
166		} else {
167			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.password_label") + "\n"))
168		}
169		b.WriteString(m.encPasswordInput.View() + "\n\n")
170		if m.encPasswordStrength != "" {
171			b.WriteString("  " + m.renderPasswordStrength() + "\n\n")
172		}
173
174		if m.encFocusIndex == 1 {
175			b.WriteString(m.contentFocusStyle().Render(t("settings_encryption.confirm_label") + "\n"))
176		} else {
177			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.confirm_label") + "\n"))
178		}
179		b.WriteString(m.encConfirmInput.View() + "\n")
180		if status := m.renderPasswordMatch(); status != "" {
181			b.WriteString("  " + status + "\n")
182		}
183		b.WriteString("\n")
184
185		saveBtn := "[ " + t("settings_encryption.enable_button") + " ]"
186		if m.encFocusIndex == 2 {
187			b.WriteString(m.contentFocusStyle().Render(saveBtn) + "\n")
188		} else {
189			b.WriteString(settingsBlurredStyle.Render(saveBtn) + "\n")
190		}
191
192		if m.encEnabling {
193			b.WriteString("\n" + accountEmailStyle.Render("  "+t("settings_encryption.encrypting")) + "\n")
194		}
195
196		b.WriteString("\n" + helpStyle.Render(t("settings_encryption.help")))
197	}
198
199	if m.encError != "" {
200		b.WriteString("\n" + dangerStyle.Render("  "+m.encError) + "\n")
201	}
202
203	return b.String()
204}
205
206func (m *Settings) renderPasswordMatch() string {
207	password := m.encPasswordInput.Value()
208	confirm := m.encConfirmInput.Value()
209	if confirm == "" {
210		return ""
211	}
212	if password == confirm {
213		return successStyle.Render(t("settings_encryption.passwords_match"))
214	}
215	return dangerStyle.Render(t("settings_encryption.passwords_do_not_match"))
216}
217
218func (m *Settings) handlePasswordChanged() {
219	password := m.encPasswordInput.Value()
220	if password == "" {
221		m.encPasswordStrength = ""
222		return
223	}
224	m.encPasswordStrength = m.passwordMeter.Strength(password)
225}
226
227func (m *Settings) renderPasswordStrength() string {
228	switch m.encPasswordStrength {
229	case passwordstrength.Strong:
230		return successStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_strong"))
231	case passwordstrength.Medium:
232		return settingsFocusedStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_medium"))
233	case passwordstrength.Weak:
234		return dangerStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_weak"))
235	}
236	return dangerStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_weak"))
237}