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