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		// Clear inputs and return to menu
 40		m.encPasswordInput.SetValue("")
 41		m.encConfirmInput.SetValue("")
 42		m.encPasswordStrength = ""
 43		m.encPasswordInput.Blur()
 44		m.encConfirmInput.Blur()
 45		m.encError = ""
 46		m.activePane = PaneMenu
 47		return m, nil
 48	case "tab", keyShiftTab, keyDown, "up":
 49		if msg.String() == keyShiftTab || msg.String() == "up" {
 50			m.encFocusIndex--
 51			if m.encFocusIndex < 0 {
 52				m.encFocusIndex = 2
 53			}
 54		} else {
 55			m.encFocusIndex++
 56			if m.encFocusIndex > 2 {
 57				m.encFocusIndex = 0
 58			}
 59		}
 60		m.encPasswordInput.Blur()
 61		m.encConfirmInput.Blur()
 62		var cmds []tea.Cmd
 63		if m.encFocusIndex == 0 {
 64			cmds = append(cmds, m.encPasswordInput.Focus())
 65		}
 66		if m.encFocusIndex == 1 {
 67			cmds = append(cmds, m.encConfirmInput.Focus())
 68		}
 69		return m, tea.Batch(cmds...)
 70	case keyEnter:
 71		switch m.encFocusIndex {
 72		case 0:
 73			m.encFocusIndex = 1
 74			m.encPasswordInput.Blur()
 75			return m, m.encConfirmInput.Focus()
 76		case 1:
 77			m.encFocusIndex = 2
 78			m.encConfirmInput.Blur()
 79			return m, nil
 80		case 2:
 81			password := m.encPasswordInput.Value()
 82			confirm := m.encConfirmInput.Value()
 83			if password == "" {
 84				m.encError = t("settings_encryption.error_empty")
 85				return m, nil
 86			}
 87			if password != confirm {
 88				m.encError = t("settings_encryption.error_mismatch")
 89				return m, nil
 90			}
 91			m.encEnabling = true
 92			m.encError = ""
 93			cfg := m.cfg
 94			return m, func() tea.Msg {
 95				err := config.EnableSecureMode(password, cfg)
 96				return SecureModeEnabledMsg{Err: err}
 97			}
 98		}
 99	default:
100		// Forward input to focused textinput
101		var cmd tea.Cmd
102		switch m.encFocusIndex {
103		case 0:
104			before := m.encPasswordInput.Value()
105			m.encPasswordInput, cmd = m.encPasswordInput.Update(msg)
106			if m.encPasswordInput.Value() != before {
107				m.handlePasswordChanged()
108			}
109		case 1:
110			m.encConfirmInput, cmd = m.encConfirmInput.Update(msg)
111		}
112		return m, cmd
113	}
114	return m, nil
115}
116
117func (m *Settings) viewEncryption() string {
118	var b strings.Builder
119	isEnabled := config.IsSecureModeEnabled()
120
121	b.WriteString(titleStyle.Render(t("settings_encryption.title")) + "\n\n")
122
123	if isEnabled {
124		if m.confirmingDisable {
125			dialog := DialogBoxStyle.Render(
126				lipgloss.JoinVertical(lipgloss.Center,
127					dangerStyle.Render(t("settings_encryption.disable_confirm")),
128					accountEmailStyle.Render(t("settings_encryption.disable_warning")),
129					HelpStyle.Render("\n(y/n)"),
130				),
131			)
132			b.WriteString(dialog + "\n")
133		} else {
134			b.WriteString(settingsFocusedStyle.Render("  "+t("settings_encryption.enabled")) + "\n\n")
135			b.WriteString(accountEmailStyle.Render("  "+t("settings_encryption.disable_button")) + "\n\n")
136			b.WriteString(helpStyle.Render("enter: disable"))
137		}
138	} else {
139		b.WriteString(accountEmailStyle.Render(t("settings_encryption.disabled")) + "\n\n")
140
141		if m.encFocusIndex == 0 {
142			b.WriteString(settingsFocusedStyle.Render(t("settings_encryption.password_label") + "\n"))
143		} else {
144			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.password_label") + "\n"))
145		}
146		b.WriteString(m.encPasswordInput.View() + "\n\n")
147		if m.encPasswordStrength != "" {
148			b.WriteString("  " + m.renderPasswordStrength() + "\n\n")
149		}
150
151		if m.encFocusIndex == 1 {
152			b.WriteString(settingsFocusedStyle.Render(t("settings_encryption.confirm_label") + "\n"))
153		} else {
154			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.confirm_label") + "\n"))
155		}
156		b.WriteString(m.encConfirmInput.View() + "\n")
157		if status := m.renderPasswordMatch(); status != "" {
158			b.WriteString("  " + status + "\n")
159		}
160		b.WriteString("\n")
161
162		saveBtn := "[ " + t("settings_encryption.enable_button") + " ]"
163		if m.encFocusIndex == 2 {
164			b.WriteString(settingsFocusedStyle.Render(saveBtn) + "\n")
165		} else {
166			b.WriteString(settingsBlurredStyle.Render(saveBtn) + "\n")
167		}
168
169		if m.encEnabling {
170			b.WriteString("\n" + accountEmailStyle.Render("  "+t("settings_encryption.encrypting")) + "\n")
171		}
172
173		b.WriteString("\n" + helpStyle.Render(t("settings_encryption.help")))
174	}
175
176	if m.encError != "" {
177		b.WriteString("\n" + dangerStyle.Render("  "+m.encError) + "\n")
178	}
179
180	return b.String()
181}
182
183func (m *Settings) renderPasswordMatch() string {
184	password := m.encPasswordInput.Value()
185	confirm := m.encConfirmInput.Value()
186	if confirm == "" {
187		return ""
188	}
189	if password == confirm {
190		return successStyle.Render(t("settings_encryption.passwords_match"))
191	}
192	return dangerStyle.Render(t("settings_encryption.passwords_do_not_match"))
193}
194
195func (m *Settings) handlePasswordChanged() {
196	password := m.encPasswordInput.Value()
197	if password == "" {
198		m.encPasswordStrength = ""
199		return
200	}
201	m.encPasswordStrength = m.passwordMeter.Strength(password)
202}
203
204func (m *Settings) renderPasswordStrength() string {
205	switch m.encPasswordStrength {
206	case passwordstrength.Strong:
207		return successStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_strong"))
208	case passwordstrength.Medium:
209		return settingsFocusedStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_medium"))
210	case passwordstrength.Weak:
211		return dangerStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_weak"))
212	}
213	return dangerStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_weak"))
214}