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		selected := m.generalCursor == i
134		cursor := m.contentCursor(selected)
135		style := m.contentItemStyle(selected)
136
137		label := t(opt.labelKey)
138		text := fmt.Sprintf("%s: %s", label, opt.value)
139		if opt.labelKey == "settings_general.signature" {
140			text = fmt.Sprintf("%s (%s)", label, opt.value)
141		}
142
143		b.WriteString(style.Render(cursor+text) + "\n")
144	}
145
146	b.WriteString("\n\n")
147
148	if !m.cfg.HideTips && m.generalCursor < len(options) {
149		b.WriteString(TipStyle.Render("Tip: " + options[m.generalCursor].tip))
150	}
151
152	return b.String()
153}
154
155func onOff(b bool) string {
156	if b {
157		return t("settings_general.on")
158	}
159	return t("settings_general.off")
160}
161
162func getDateFormatLabel(f string) string {
163	if f == "" {
164		f = config.DateFormatEU
165	}
166	switch f {
167	case config.DateFormatUS:
168		return "US (MM/DD/YYYY hh:MM AM)"
169	case config.DateFormatISO:
170		return "ISO (YYYY-MM-DD HH:MM)"
171	default:
172		return "EU (DD/MM/YYYY HH:MM)"
173	}
174}
175
176func getSignatureStatus() string {
177	if config.HasSignature() {
178		return t("settings_general.signature_configured")
179	}
180	return t("settings_general.signature_not_configured")
181}
182
183func getLanguageLabel(langCode string) string {
184	if locale, ok := i18n.GetLanguage(langCode); ok {
185		return fmt.Sprintf("%s (%s)", locale.NativeName, locale.Code)
186	}
187	return langCode
188}