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 if m.generalCursor > 0 {
55 m.generalCursor--
56 }
57 case "down", "j":
58 if m.generalCursor < len(opts)-1 {
59 m.generalCursor++
60 }
61 case "enter", "space", "right", "l":
62 if m.generalCursor < len(opts) {
63 opt := opts[m.generalCursor]
64 if opt.isAccountSig {
65 if msg.String() == "enter" || msg.String() == "right" || msg.String() == "l" {
66 return m, func() tea.Msg { return GoToSignatureEditorMsg{AccountID: opt.accountID} }
67 }
68 return m, nil
69 }
70
71 saved := false
72 switch m.generalCursor {
73 case 0: // Image Display
74 m.cfg.DisableImages = !m.cfg.DisableImages
75 _ = config.SaveConfig(m.cfg)
76 saved = true
77 case 1: // Contextual Tips
78 m.cfg.HideTips = !m.cfg.HideTips
79 _ = config.SaveConfig(m.cfg)
80 saved = true
81 case 2: // Desktop Notifications
82 m.cfg.DisableNotifications = !m.cfg.DisableNotifications
83 _ = config.SaveConfig(m.cfg)
84 saved = true
85 case 3: // Split Pane View
86 m.cfg.EnableSplitPane = !m.cfg.EnableSplitPane
87 _ = config.SaveConfig(m.cfg)
88 saved = true
89 case 4: // Date Format
90 switch m.cfg.DateFormat {
91 case config.DateFormatEU:
92 m.cfg.DateFormat = config.DateFormatUS
93 case config.DateFormatUS:
94 m.cfg.DateFormat = config.DateFormatISO
95 default: // or ISO
96 m.cfg.DateFormat = config.DateFormatEU
97 }
98 _ = config.SaveConfig(m.cfg)
99 saved = true
100 case 5: // Language
101 // Cycle through available languages
102 langs := i18n.LanguageCodes()
103 currentLang := m.cfg.GetLanguage()
104 currentIdx := -1
105 for i, lang := range langs {
106 if lang == currentLang {
107 currentIdx = i
108 break
109 }
110 }
111 nextIdx := (currentIdx + 1) % len(langs)
112 m.cfg.Language = langs[nextIdx]
113 _ = config.SaveConfig(m.cfg)
114 // Apply language change immediately
115 i18n.GetManager().SetLanguage(m.cfg.Language)
116 // Trigger full UI rebuild
117 return m, tea.Batch(
118 func() tea.Msg { return ConfigSavedMsg{} },
119 func() tea.Msg { return LanguageChangedMsg{} },
120 )
121 case 6: // Edit Signature
122 if msg.String() == "enter" || msg.String() == "right" || msg.String() == "l" {
123 return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
124 }
125 }
126 if saved {
127 return m, func() tea.Msg { return ConfigSavedMsg{} }
128 }
129 }
130 }
131 return m, nil
132}
133
134func (m *Settings) viewGeneral() string {
135 var b strings.Builder
136
137 b.WriteString(titleStyle.Render("General Settings") + "\n\n")
138
139 options := m.buildGeneralOptions()
140
141 for i, opt := range options {
142 cursor := " "
143 style := accountItemStyle
144 if m.generalCursor == i {
145 cursor = "> "
146 style = selectedAccountItemStyle
147 }
148
149 label := opt.labelKey
150 if !opt.isAccountSig {
151 label = t(opt.labelKey)
152 }
153 text := fmt.Sprintf("%s: %s", label, opt.value)
154 if opt.labelKey == "settings_general.signature" || opt.isAccountSig {
155 text = fmt.Sprintf("%s (%s)", label, opt.value)
156 }
157
158 b.WriteString(style.Render(cursor+text) + "\n")
159 }
160
161 b.WriteString("\n\n")
162
163 if !m.cfg.HideTips && m.generalCursor < len(options) {
164 b.WriteString(TipStyle.Render("Tip: " + options[m.generalCursor].tip))
165 }
166
167 return b.String()
168}
169
170func onOff(b bool) string {
171 if b {
172 return t("settings_general.on")
173 }
174 return t("settings_general.off")
175}
176
177func getDateFormatLabel(f string) string {
178 if f == "" {
179 f = config.DateFormatEU
180 }
181 switch f {
182 case config.DateFormatUS:
183 return "US (MM/DD/YYYY hh:MM AM)"
184 case config.DateFormatISO:
185 return "ISO (YYYY-MM-DD HH:MM)"
186 default:
187 return "EU (DD/MM/YYYY HH:MM)"
188 }
189}
190
191func getSignatureStatus() string {
192 if config.HasSignature() {
193 return t("settings_general.signature_configured")
194 }
195 return t("settings_general.signature_not_configured")
196}
197
198func getLanguageLabel(langCode string) string {
199 if locale, ok := i18n.GetLanguage(langCode); ok {
200 return fmt.Sprintf("%s (%s)", locale.NativeName, locale.Code)
201 }
202 return langCode
203}