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}