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