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