1package tui
2
3import (
4 "fmt"
5 "strings"
6
7 tea "charm.land/bubbletea/v2"
8 "github.com/floatpane/matcha/config"
9 "github.com/floatpane/matcha/i18n"
10)
11
12type generalOption struct {
13 labelKey string
14 value string
15 tip string
16}
17
18func (m *Settings) buildGeneralOptions() []generalOption {
19 opts := []generalOption{
20 {"settings_general.disable_images", onOff(m.cfg.DisableImages), "Prevent images from loading automatically in emails."},
21 {"settings_general.hide_tips", onOff(m.cfg.HideTips), "Hide helpful hints displayed at the bottom of the screen."},
22 {"settings_general.disable_notifications", onOff(m.cfg.DisableNotifications), "Turn off desktop notifications for new mail."},
23 {"settings_general.disable_daemon", onOff(!m.cfg.DisableDaemon), "Run a background daemon for push notifications and sync. Takes effect on restart."},
24 {"settings_general.enable_split_pane", onOff(m.cfg.EnableSplitPane), "View inbox and email side-by-side."},
25 {"settings_general.enable_threaded", onOff(m.cfg.EnableThreaded), "Group emails into conversations by reply chain. Per-folder overrides are kept."},
26 {"settings_general.enable_detailed_dates", onOff(m.cfg.EnableDetailedDates), "Show detailed inbox dates."},
27 {"settings_general.spellcheck", onOff(!m.cfg.DisableSpellcheck), "Underline misspelled words while composing."},
28 {"settings_general.spell_suggestions", onOff(!m.cfg.DisableSpellSuggestions), "Show suggestion popup for misspelled words."},
29 {"settings_general.date_format", getDateFormatLabel(m.cfg.DateFormat), "Change how dates and times are displayed."},
30 {"settings_general.language", getLanguageLabel(m.cfg.GetLanguage()), "Change the interface language. Changes apply instantly."},
31 {"settings_general.signature", getSignatureStatus(), "Configure the global signature appended to your outgoing emails."},
32 }
33
34 return opts
35}
36
37func (m *Settings) updateGeneral(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
38 opts := m.buildGeneralOptions()
39
40 switch msg.String() {
41 case "up", "k":
42 m.generalCursor = (m.generalCursor - 1 + len(opts)) % len(opts)
43 case keyDown, "j":
44 m.generalCursor = (m.generalCursor + 1) % len(opts)
45 case keyEnter, "space", keyRight, "l":
46 if m.generalCursor < len(opts) {
47 saved := false
48 switch m.generalCursor {
49 case 0: // Image Display
50 m.cfg.DisableImages = !m.cfg.DisableImages
51 _ = config.SaveConfig(m.cfg)
52 saved = true
53 case 1: // Contextual Tips
54 m.cfg.HideTips = !m.cfg.HideTips
55 _ = config.SaveConfig(m.cfg)
56 saved = true
57 case 2: // Desktop Notifications
58 m.cfg.DisableNotifications = !m.cfg.DisableNotifications
59 _ = config.SaveConfig(m.cfg)
60 saved = true
61 case 3: // Background Daemon
62 m.cfg.DisableDaemon = !m.cfg.DisableDaemon
63 _ = config.SaveConfig(m.cfg)
64 saved = true
65 case 4: // Split Pane View
66 m.cfg.EnableSplitPane = !m.cfg.EnableSplitPane
67 _ = config.SaveConfig(m.cfg)
68 saved = true
69 case 5: // Threaded Conversation View
70 m.cfg.EnableThreaded = !m.cfg.EnableThreaded
71 _ = config.SaveConfig(m.cfg)
72 saved = true
73 case 6: // Detailed Dates
74 m.cfg.EnableDetailedDates = !m.cfg.EnableDetailedDates
75 _ = config.SaveConfig(m.cfg)
76 saved = true
77 case 7: // Spellcheck
78 m.cfg.DisableSpellcheck = !m.cfg.DisableSpellcheck
79 _ = config.SaveConfig(m.cfg)
80 saved = true
81 case 8: // Spell Suggestions
82 m.cfg.DisableSpellSuggestions = !m.cfg.DisableSpellSuggestions
83 _ = config.SaveConfig(m.cfg)
84 saved = true
85 case 9: // Date Format
86 switch m.cfg.DateFormat {
87 case config.DateFormatEU:
88 m.cfg.DateFormat = config.DateFormatUS
89 case config.DateFormatUS:
90 m.cfg.DateFormat = config.DateFormatISO
91 default: // or ISO
92 m.cfg.DateFormat = config.DateFormatEU
93 }
94 _ = config.SaveConfig(m.cfg)
95 saved = true
96 case 10: // Language
97 // Cycle through available languages
98 langs := i18n.LanguageCodes()
99 currentLang := m.cfg.GetLanguage()
100 currentIdx := -1
101 for i, lang := range langs {
102 if lang == currentLang {
103 currentIdx = i
104 break
105 }
106 }
107 nextIdx := (currentIdx + 1) % len(langs)
108 m.cfg.Language = langs[nextIdx]
109 _ = config.SaveConfig(m.cfg)
110 // Apply language change immediately
111 i18n.GetManager().SetLanguage(m.cfg.Language) //nolint:errcheck,gosec
112 // Trigger full UI rebuild
113 return m, tea.Batch(
114 func() tea.Msg { return ConfigSavedMsg{} },
115 func() tea.Msg { return LanguageChangedMsg{} },
116 )
117 case 11: // Edit Signature
118 if msg.String() == keyEnter || msg.String() == keyRight || msg.String() == "l" {
119 return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
120 }
121 }
122 if saved {
123 return m, func() tea.Msg { return ConfigSavedMsg{} }
124 }
125 }
126 }
127 return m, nil
128}
129
130func (m *Settings) viewGeneral() string {
131 var b strings.Builder
132
133 b.WriteString(titleStyle.Render("General Settings") + "\n\n")
134
135 options := m.buildGeneralOptions()
136
137 for i, opt := range options {
138 selected := m.generalCursor == i
139 cursor := m.contentCursor(selected)
140 style := m.contentItemStyle(selected)
141
142 label := t(opt.labelKey)
143 text := fmt.Sprintf("%s: %s", label, opt.value)
144 if opt.labelKey == "settings_general.signature" {
145 text = fmt.Sprintf("%s (%s)", label, opt.value)
146 }
147
148 b.WriteString(style.Render(cursor+text) + "\n")
149 }
150
151 b.WriteString("\n\n")
152
153 if !m.cfg.HideTips && m.generalCursor < len(options) {
154 b.WriteString(TipStyle.Render("Tip: " + options[m.generalCursor].tip))
155 }
156
157 return b.String()
158}
159
160func onOff(b bool) string {
161 if b {
162 return t("settings_general.on")
163 }
164 return t("settings_general.off")
165}
166
167func getDateFormatLabel(f string) string {
168 if f == "" {
169 f = config.DateFormatEU
170 }
171 switch f {
172 case config.DateFormatUS:
173 return "US (MM/DD/YYYY hh:MM AM)"
174 case config.DateFormatISO:
175 return "ISO (YYYY-MM-DD HH:MM)"
176 default:
177 return "EU (DD/MM/YYYY HH:MM)"
178 }
179}
180
181func getSignatureStatus() string {
182 if config.HasSignature() {
183 return t("settings_general.signature_configured")
184 }
185 return t("settings_general.signature_not_configured")
186}
187
188func getLanguageLabel(langCode string) string {
189 if locale, ok := i18n.GetLanguage(langCode); ok {
190 return fmt.Sprintf("%s (%s)", locale.NativeName, locale.Code)
191 }
192 return langCode
193}