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}