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