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.date_format", getDateFormatLabel(m.cfg.DateFormat), "Change how dates and times are displayed.", false, ""},
27 {"settings_general.language", getLanguageLabel(m.cfg.GetLanguage()), "Change the interface language. Changes apply instantly.", false, ""},
28 {"settings_general.signature", getSignatureStatus(), "Configure the global signature appended to your outgoing emails.", false, ""},
29 }
30
31 for _, acc := range m.cfg.Accounts {
32 status := t("settings_general.signature_not_configured")
33 accCopy := acc // capture for pointer safety
34 if config.HasAccountSignature(&accCopy) {
35 status = t("settings_general.signature_configured")
36 }
37 opts = append(opts, generalOption{
38 labelKey: fmt.Sprintf("Signature (%s)", acc.Email),
39 value: status,
40 tip: fmt.Sprintf("Configure the signature for %s", acc.Email),
41 isAccountSig: true,
42 accountID: acc.ID,
43 })
44 }
45
46 return opts
47}
48
49func (m *Settings) updateGeneral(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
50 opts := m.buildGeneralOptions()
51
52 switch msg.String() {
53 case "up", "k":
54 m.generalCursor = (m.generalCursor - 1 + len(opts)) % len(opts)
55 case "down", "j":
56 m.generalCursor = (m.generalCursor + 1) % len(opts)
57 case "enter", "space", "right", "l":
58 if m.generalCursor < len(opts) {
59 opt := opts[m.generalCursor]
60 if opt.isAccountSig {
61 if msg.String() == "enter" || msg.String() == "right" || msg.String() == "l" {
62 return m, func() tea.Msg { return GoToSignatureEditorMsg{AccountID: opt.accountID} }
63 }
64 return m, nil
65 }
66
67 saved := false
68 switch m.generalCursor {
69 case 0: // Image Display
70 m.cfg.DisableImages = !m.cfg.DisableImages
71 _ = config.SaveConfig(m.cfg)
72 saved = true
73 case 1: // Contextual Tips
74 m.cfg.HideTips = !m.cfg.HideTips
75 _ = config.SaveConfig(m.cfg)
76 saved = true
77 case 2: // Desktop Notifications
78 m.cfg.DisableNotifications = !m.cfg.DisableNotifications
79 _ = config.SaveConfig(m.cfg)
80 saved = true
81 case 3: // Split Pane View
82 m.cfg.EnableSplitPane = !m.cfg.EnableSplitPane
83 _ = config.SaveConfig(m.cfg)
84 saved = true
85 case 4: // 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 5: // 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)
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 6: // Edit Signature
118 if msg.String() == "enter" || msg.String() == "right" || 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 cursor := " "
139 style := accountItemStyle
140 if m.generalCursor == i {
141 cursor = "> "
142 style = selectedAccountItemStyle
143 }
144
145 label := opt.labelKey
146 if !opt.isAccountSig {
147 label = t(opt.labelKey)
148 }
149 text := fmt.Sprintf("%s: %s", label, opt.value)
150 if opt.labelKey == "settings_general.signature" || opt.isAccountSig {
151 text = fmt.Sprintf("%s (%s)", label, opt.value)
152 }
153
154 b.WriteString(style.Render(cursor+text) + "\n")
155 }
156
157 b.WriteString("\n\n")
158
159 if !m.cfg.HideTips && m.generalCursor < len(options) {
160 b.WriteString(TipStyle.Render("Tip: " + options[m.generalCursor].tip))
161 }
162
163 return b.String()
164}
165
166func onOff(b bool) string {
167 if b {
168 return t("settings_general.on")
169 }
170 return t("settings_general.off")
171}
172
173func getDateFormatLabel(f string) string {
174 if f == "" {
175 f = config.DateFormatEU
176 }
177 switch f {
178 case config.DateFormatUS:
179 return "US (MM/DD/YYYY hh:MM AM)"
180 case config.DateFormatISO:
181 return "ISO (YYYY-MM-DD HH:MM)"
182 default:
183 return "EU (DD/MM/YYYY HH:MM)"
184 }
185}
186
187func getSignatureStatus() string {
188 if config.HasSignature() {
189 return t("settings_general.signature_configured")
190 }
191 return t("settings_general.signature_not_configured")
192}
193
194func getLanguageLabel(langCode string) string {
195 if locale, ok := i18n.GetLanguage(langCode); ok {
196 return fmt.Sprintf("%s (%s)", locale.NativeName, locale.Code)
197 }
198 return langCode
199}