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