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