settings_general.go

  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.spellcheck", onOff(!m.cfg.DisableSpellcheck), "Underline misspelled words while composing."},
 27		{"settings_general.spell_suggestions", onOff(!m.cfg.DisableSpellSuggestions), "Show suggestion popup for misspelled words."},
 28		{"settings_general.date_format", getDateFormatLabel(m.cfg.DateFormat), "Change how dates and times are displayed."},
 29		{"settings_general.language", getLanguageLabel(m.cfg.GetLanguage()), "Change the interface language. Changes apply instantly."},
 30		{"settings_general.signature", getSignatureStatus(), "Configure the global signature appended to your outgoing emails."},
 31	}
 32
 33	return opts
 34}
 35
 36func (m *Settings) updateGeneral(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
 37	opts := m.buildGeneralOptions()
 38
 39	switch msg.String() {
 40	case "up", "k":
 41		m.generalCursor = (m.generalCursor - 1 + len(opts)) % len(opts)
 42	case keyDown, "j":
 43		m.generalCursor = (m.generalCursor + 1) % len(opts)
 44	case keyEnter, "space", keyRight, "l":
 45		if m.generalCursor < len(opts) {
 46			saved := false
 47			switch m.generalCursor {
 48			case 0: // Image Display
 49				m.cfg.DisableImages = !m.cfg.DisableImages
 50				_ = config.SaveConfig(m.cfg)
 51				saved = true
 52			case 1: // Contextual Tips
 53				m.cfg.HideTips = !m.cfg.HideTips
 54				_ = config.SaveConfig(m.cfg)
 55				saved = true
 56			case 2: // Desktop Notifications
 57				m.cfg.DisableNotifications = !m.cfg.DisableNotifications
 58				_ = config.SaveConfig(m.cfg)
 59				saved = true
 60			case 3: // Split Pane View
 61				m.cfg.EnableSplitPane = !m.cfg.EnableSplitPane
 62				_ = config.SaveConfig(m.cfg)
 63				saved = true
 64			case 4: // Threaded Conversation View
 65				m.cfg.EnableThreaded = !m.cfg.EnableThreaded
 66				_ = config.SaveConfig(m.cfg)
 67				saved = true
 68			case 5: // Detailed Dates
 69				m.cfg.EnableDetailedDates = !m.cfg.EnableDetailedDates
 70				_ = config.SaveConfig(m.cfg)
 71				saved = true
 72			case 6: // Spellcheck
 73				m.cfg.DisableSpellcheck = !m.cfg.DisableSpellcheck
 74				_ = config.SaveConfig(m.cfg)
 75				saved = true
 76			case 7: // Spell Suggestions
 77				m.cfg.DisableSpellSuggestions = !m.cfg.DisableSpellSuggestions
 78				_ = config.SaveConfig(m.cfg)
 79				saved = true
 80			case 8: // 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				saved = true
 91			case 9: // Language
 92				// Cycle through available languages
 93				langs := i18n.LanguageCodes()
 94				currentLang := m.cfg.GetLanguage()
 95				currentIdx := -1
 96				for i, lang := range langs {
 97					if lang == currentLang {
 98						currentIdx = i
 99						break
100					}
101				}
102				nextIdx := (currentIdx + 1) % len(langs)
103				m.cfg.Language = langs[nextIdx]
104				_ = config.SaveConfig(m.cfg)
105				// Apply language change immediately
106				i18n.GetManager().SetLanguage(m.cfg.Language) //nolint:errcheck,gosec
107				// Trigger full UI rebuild
108				return m, tea.Batch(
109					func() tea.Msg { return ConfigSavedMsg{} },
110					func() tea.Msg { return LanguageChangedMsg{} },
111				)
112			case 10: // Edit Signature
113				if msg.String() == keyEnter || msg.String() == keyRight || msg.String() == "l" {
114					return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
115				}
116			}
117			if saved {
118				return m, func() tea.Msg { return ConfigSavedMsg{} }
119			}
120		}
121	}
122	return m, nil
123}
124
125func (m *Settings) viewGeneral() string {
126	var b strings.Builder
127
128	b.WriteString(titleStyle.Render("General Settings") + "\n\n")
129
130	options := m.buildGeneralOptions()
131
132	for i, opt := range options {
133		cursor := "  "
134		style := accountItemStyle
135		if m.generalCursor == i {
136			cursor = "> "
137			style = selectedAccountItemStyle
138		}
139
140		label := t(opt.labelKey)
141		text := fmt.Sprintf("%s: %s", label, opt.value)
142		if opt.labelKey == "settings_general.signature" {
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}