1package tui
2
3import (
4 "fmt"
5 "strings"
6
7 tea "github.com/charmbracelet/bubbletea"
8 "github.com/charmbracelet/lipgloss"
9 "github.com/floatpane/matcha/config"
10)
11
12var (
13 accountItemStyle = lipgloss.NewStyle().PaddingLeft(2)
14 selectedAccountItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("42"))
15 accountEmailStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
16 dangerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("196"))
17)
18
19// Settings displays the account management screen.
20type Settings struct {
21 accounts []config.Account
22 cursor int
23 confirmingDelete bool
24 width int
25 height int
26}
27
28// NewSettings creates a new settings model.
29func NewSettings(accounts []config.Account) *Settings {
30 return &Settings{
31 accounts: accounts,
32 cursor: 0,
33 }
34}
35
36// Init initializes the settings model.
37func (m *Settings) Init() tea.Cmd {
38 return nil
39}
40
41// Update handles messages for the settings model.
42func (m *Settings) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
43 switch msg := msg.(type) {
44 case tea.WindowSizeMsg:
45 m.width = msg.Width
46 m.height = msg.Height
47 return m, nil
48
49 case tea.KeyMsg:
50 if m.confirmingDelete {
51 switch msg.String() {
52 case "y", "Y":
53 if m.cursor < len(m.accounts) {
54 accountID := m.accounts[m.cursor].ID
55 m.confirmingDelete = false
56 return m, func() tea.Msg {
57 return DeleteAccountMsg{AccountID: accountID}
58 }
59 }
60 case "n", "N", "esc":
61 m.confirmingDelete = false
62 return m, nil
63 }
64 return m, nil
65 }
66
67 switch msg.String() {
68 case "up", "k":
69 if m.cursor > 0 {
70 m.cursor--
71 }
72 case "down", "j":
73 // +2 for "Add Account" and "Signature" options
74 if m.cursor < len(m.accounts)+1 {
75 m.cursor++
76 }
77 case "d":
78 // Delete selected account (not the "Add Account" option)
79 if m.cursor < len(m.accounts) && len(m.accounts) > 0 {
80 m.confirmingDelete = true
81 }
82 case "enter":
83 // If cursor is on "Add Account"
84 if m.cursor == len(m.accounts) {
85 return m, func() tea.Msg { return GoToAddAccountMsg{} }
86 }
87 // If cursor is on "Signature"
88 if m.cursor == len(m.accounts)+1 {
89 return m, func() tea.Msg { return GoToSignatureEditorMsg{} }
90 }
91 case "esc":
92 return m, func() tea.Msg { return GoToChoiceMenuMsg{} }
93 }
94 }
95 return m, nil
96}
97
98// View renders the settings screen.
99func (m *Settings) View() string {
100 var b strings.Builder
101
102 b.WriteString(titleStyle.Render("Account Settings") + "\n\n")
103 b.WriteString(listHeader.Render("Your email accounts:"))
104 b.WriteString("\n\n")
105
106 if len(m.accounts) == 0 {
107 b.WriteString(accountEmailStyle.Render(" No accounts configured.\n"))
108 b.WriteString("\n")
109 }
110
111 for i, account := range m.accounts {
112 displayName := account.Email
113 if account.Name != "" {
114 displayName = fmt.Sprintf("%s (%s)", account.Name, account.Email)
115 }
116
117 providerInfo := account.ServiceProvider
118 if account.ServiceProvider == "custom" {
119 providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
120 }
121
122 line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
123
124 if m.cursor == i {
125 b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", line)))
126 } else {
127 b.WriteString(accountItemStyle.Render(fmt.Sprintf(" %s", line)))
128 }
129 b.WriteString("\n")
130 }
131
132 // Add Account option
133 addAccountText := "Add New Account"
134 if m.cursor == len(m.accounts) {
135 b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", addAccountText)))
136 } else {
137 b.WriteString(accountItemStyle.Render(fmt.Sprintf(" %s", addAccountText)))
138 }
139 b.WriteString("\n")
140
141 // Signature option
142 signatureText := "Edit Signature"
143 if config.HasSignature() {
144 signatureText = "Edit Signature (configured)"
145 }
146 if m.cursor == len(m.accounts)+1 {
147 b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", signatureText)))
148 } else {
149 b.WriteString(accountItemStyle.Render(fmt.Sprintf(" %s", signatureText)))
150 }
151 b.WriteString("\n\n")
152
153 b.WriteString(helpStyle.Render("↑/↓: navigate • enter: select • d: delete account • esc: back"))
154
155 if m.confirmingDelete {
156 accountName := m.accounts[m.cursor].Email
157 dialog := DialogBoxStyle.Render(
158 lipgloss.JoinVertical(lipgloss.Center,
159 dangerStyle.Render("Delete account?"),
160 accountEmailStyle.Render(accountName),
161 HelpStyle.Render("\n(y/n)"),
162 ),
163 )
164 return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog)
165 }
166
167 return docStyle.Render(b.String())
168}
169
170// UpdateAccounts updates the list of accounts.
171func (m *Settings) UpdateAccounts(accounts []config.Account) {
172 m.accounts = accounts
173 if m.cursor >= len(accounts) {
174 m.cursor = len(accounts)
175 }
176}