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.disable_daemon", onOff(!m.cfg.DisableDaemon), "Run a background daemon for push notifications and sync. Takes effect on restart."},
 24		{"settings_general.enable_split_pane", onOff(m.cfg.EnableSplitPane), "View inbox and email side-by-side."},
 25		{"settings_general.enable_threaded", onOff(m.cfg.EnableThreaded), "Group emails into conversations by reply chain. Per-folder overrides are kept."},
 26		{"settings_general.enable_detailed_dates", onOff(m.cfg.EnableDetailedDates), "Show detailed inbox dates."},
 27		{"settings_general.spellcheck", onOff(!m.cfg.DisableSpellcheck), "Underline misspelled words while composing."},
 28		{"settings_general.spell_suggestions", onOff(!m.cfg.DisableSpellSuggestions), "Show suggestion popup for misspelled words."},
 29		{"settings_general.date_format", getDateFormatLabel(m.cfg.DateFormat), "Change how dates and times are displayed."},
 30		{"settings_general.language", getLanguageLabel(m.cfg.GetLanguage()), "Change the interface language. Changes apply instantly."},
 31		{"settings_general.signature", getSignatureStatus(), "Configure the global signature appended to your outgoing emails."},
 32	}
 33
 34	return opts
 35}
 36
 37func (m *Settings) updateGeneral(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
 38	opts := m.buildGeneralOptions()
 39
 40	switch msg.String() {
 41	case "up", "k":
 42		m.generalCursor = (m.generalCursor - 1 + len(opts)) % len(opts)
 43	case keyDown, "j":
 44		m.generalCursor = (m.generalCursor + 1) % len(opts)
 45	case keyEnter, "space", keyRight, "l":
 46		if m.generalCursor < len(opts) {
 47			saved := false
 48			switch m.generalCursor {
 49			case 0: // Image Display
 50				m.cfg.DisableImages = !m.cfg.DisableImages
 51				_ = config.SaveConfig(m.cfg)
 52				saved = true
 53			case 1: // Contextual Tips
 54				m.cfg.HideTips = !m.cfg.HideTips
 55				_ = config.SaveConfig(m.cfg)
 56				saved = true
 57			case 2: // Desktop Notifications
 58				m.cfg.DisableNotifications = !m.cfg.DisableNotifications
 59				_ = config.SaveConfig(m.cfg)
 60				saved = true
 61			case 3: // Background Daemon
 62				m.cfg.DisableDaemon = !m.cfg.DisableDaemon
 63				_ = config.SaveConfig(m.cfg)
 64				saved = true
 65			case 4: // Split Pane View
 66				m.cfg.EnableSplitPane = !m.cfg.EnableSplitPane
 67				_ = config.SaveConfig(m.cfg)
 68				saved = true
 69			case 5: // Threaded Conversation View
 70				m.cfg.EnableThreaded = !m.cfg.EnableThreaded
 71				_ = config.SaveConfig(m.cfg)
 72				saved = true
 73			case 6: // Detailed Dates
 74				m.cfg.EnableDetailedDates = !m.cfg.EnableDetailedDates
 75				_ = config.SaveConfig(m.cfg)
 76				saved = true
 77			case 7: // Spellcheck
 78				m.cfg.DisableSpellcheck = !m.cfg.DisableSpellcheck
 79				_ = config.SaveConfig(m.cfg)
 80				saved = true
 81			case 8: // Spell Suggestions
 82				m.cfg.DisableSpellSuggestions = !m.cfg.DisableSpellSuggestions
 83				_ = config.SaveConfig(m.cfg)
 84				saved = true
 85			case 9: // Date Format
 86				switch m.cfg.DateFormat {
 87				case config.DateFormatEU:
 88					m.cfg.DateFormat = config.DateFormatUS
 89				case config.DateFormatUS:
 90					m.cfg.DateFormat = config.DateFormatISO
 91				default: // or ISO
 92					m.cfg.DateFormat = config.DateFormatEU
 93				}
 94				_ = config.SaveConfig(m.cfg)
 95				saved = true
 96			case 10: // Language
 97				// Cycle through available languages
 98				langs := i18n.LanguageCodes()
 99				currentLang := m.cfg.GetLanguage()
100				currentIdx := -1
101				for i, lang := range langs {
102					if lang == currentLang {
103						currentIdx = i
104						break
105					}
106				}
107				nextIdx := (currentIdx + 1) % len(langs)
108				m.cfg.Language = langs[nextIdx]
109				_ = config.SaveConfig(m.cfg)
110				// Apply language change immediately
111				i18n.GetManager().SetLanguage(m.cfg.Language) //nolint:errcheck,gosec
112				// Trigger full UI rebuild
113				return m, tea.Batch(
114					func() tea.Msg { return ConfigSavedMsg{} },
115					func() tea.Msg { return LanguageChangedMsg{} },
116				)
117			case 11: // Edit Signature
118				if msg.String() == keyEnter || msg.String() == keyRight || msg.String() == "l" {
119					return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
120				}
121			}
122			if saved {
123				return m, func() tea.Msg { return ConfigSavedMsg{} }
124			}
125		}
126	}
127	return m, nil
128}
129
130func (m *Settings) viewGeneral() string {
131	var b strings.Builder
132
133	b.WriteString(titleStyle.Render("General Settings") + "\n\n")
134
135	options := m.buildGeneralOptions()
136
137	for i, opt := range options {
138		selected := m.generalCursor == i
139		cursor := m.contentCursor(selected)
140		style := m.contentItemStyle(selected)
141
142		label := t(opt.labelKey)
143		text := fmt.Sprintf("%s: %s", label, opt.value)
144		if opt.labelKey == "settings_general.signature" {
145			text = fmt.Sprintf("%s (%s)", label, opt.value)
146		}
147
148		b.WriteString(style.Render(cursor+text) + "\n")
149	}
150
151	b.WriteString("\n\n")
152
153	if !m.cfg.HideTips && m.generalCursor < len(options) {
154		b.WriteString(TipStyle.Render("Tip: " + options[m.generalCursor].tip))
155	}
156
157	return b.String()
158}
159
160func onOff(b bool) string {
161	if b {
162		return t("settings_general.on")
163	}
164	return t("settings_general.off")
165}
166
167func getDateFormatLabel(f string) string {
168	if f == "" {
169		f = config.DateFormatEU
170	}
171	switch f {
172	case config.DateFormatUS:
173		return "US (MM/DD/YYYY hh:MM AM)"
174	case config.DateFormatISO:
175		return "ISO (YYYY-MM-DD HH:MM)"
176	default:
177		return "EU (DD/MM/YYYY HH:MM)"
178	}
179}
180
181func getSignatureStatus() string {
182	if config.HasSignature() {
183		return t("settings_general.signature_configured")
184	}
185	return t("settings_general.signature_not_configured")
186}
187
188func getLanguageLabel(langCode string) string {
189	if locale, ok := i18n.GetLanguage(langCode); ok {
190		return fmt.Sprintf("%s (%s)", locale.NativeName, locale.Code)
191	}
192	return langCode
193}