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() == "enter" {
 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", "shift+tab", "down", "up":
 49		if msg.String() == "shift+tab" || 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 "enter":
 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		if m.encFocusIndex == 0 {
103			before := m.encPasswordInput.Value()
104			m.encPasswordInput, cmd = m.encPasswordInput.Update(msg)
105			if m.encPasswordInput.Value() != before {
106				m.handlePasswordChanged()
107			}
108		} else if m.encFocusIndex == 1 {
109			m.encConfirmInput, cmd = m.encConfirmInput.Update(msg)
110		}
111		return m, cmd
112	}
113	return m, nil
114}
115
116func (m *Settings) viewEncryption() string {
117	var b strings.Builder
118	isEnabled := config.IsSecureModeEnabled()
119
120	b.WriteString(titleStyle.Render(t("settings_encryption.title")) + "\n\n")
121
122	if isEnabled {
123		if m.confirmingDisable {
124			dialog := DialogBoxStyle.Render(
125				lipgloss.JoinVertical(lipgloss.Center,
126					dangerStyle.Render(t("settings_encryption.disable_confirm")),
127					accountEmailStyle.Render(t("settings_encryption.disable_warning")),
128					HelpStyle.Render("\n(y/n)"),
129				),
130			)
131			b.WriteString(dialog + "\n")
132		} else {
133			b.WriteString(settingsFocusedStyle.Render("  "+t("settings_encryption.enabled")) + "\n\n")
134			b.WriteString(accountEmailStyle.Render("  "+t("settings_encryption.disable_button")) + "\n\n")
135			b.WriteString(helpStyle.Render("enter: disable"))
136		}
137	} else {
138		b.WriteString(accountEmailStyle.Render(t("settings_encryption.disabled")) + "\n\n")
139
140		if m.encFocusIndex == 0 {
141			b.WriteString(settingsFocusedStyle.Render(t("settings_encryption.password_label") + "\n"))
142		} else {
143			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.password_label") + "\n"))
144		}
145		b.WriteString(m.encPasswordInput.View() + "\n\n")
146		if m.encPasswordStrength != "" {
147			b.WriteString("  " + m.renderPasswordStrength() + "\n\n")
148		}
149
150		if m.encFocusIndex == 1 {
151			b.WriteString(settingsFocusedStyle.Render(t("settings_encryption.confirm_label") + "\n"))
152		} else {
153			b.WriteString(settingsBlurredStyle.Render(t("settings_encryption.confirm_label") + "\n"))
154		}
155		b.WriteString(m.encConfirmInput.View() + "\n")
156		if status := m.renderPasswordMatch(); status != "" {
157			b.WriteString("  " + status + "\n")
158		}
159		b.WriteString("\n")
160
161		saveBtn := "[ " + t("settings_encryption.enable_button") + " ]"
162		if m.encFocusIndex == 2 {
163			b.WriteString(settingsFocusedStyle.Render(saveBtn) + "\n")
164		} else {
165			b.WriteString(settingsBlurredStyle.Render(saveBtn) + "\n")
166		}
167
168		if m.encEnabling {
169			b.WriteString("\n" + accountEmailStyle.Render("  "+t("settings_encryption.encrypting")) + "\n")
170		}
171
172		b.WriteString("\n" + helpStyle.Render(t("settings_encryption.help")))
173	}
174
175	if m.encError != "" {
176		b.WriteString("\n" + dangerStyle.Render("  "+m.encError) + "\n")
177	}
178
179	return b.String()
180}
181
182func (m *Settings) renderPasswordMatch() string {
183	password := m.encPasswordInput.Value()
184	confirm := m.encConfirmInput.Value()
185	if confirm == "" {
186		return ""
187	}
188	if password == confirm {
189		return successStyle.Render(t("settings_encryption.passwords_match"))
190	}
191	return dangerStyle.Render(t("settings_encryption.passwords_do_not_match"))
192}
193
194func (m *Settings) handlePasswordChanged() {
195	password := m.encPasswordInput.Value()
196	if password == "" {
197		m.encPasswordStrength = ""
198		return
199	}
200	m.encPasswordStrength = m.passwordMeter.Strength(password)
201}
202
203func (m *Settings) renderPasswordStrength() string {
204	switch m.encPasswordStrength {
205	case passwordstrength.Strong:
206		return successStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_strong"))
207	case passwordstrength.Medium:
208		return settingsFocusedStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_medium"))
209	default:
210		return dangerStyle.Render(t("settings_encryption.strength_label") + " " + t("settings_encryption.strength_weak"))
211	}
212}