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 // +1 for "Add Account" option
74 if m.cursor < len(m.accounts) {
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 case "esc":
88 return m, func() tea.Msg { return GoToChoiceMenuMsg{} }
89 }
90 }
91 return m, nil
92}
93
94// View renders the settings screen.
95func (m *Settings) View() string {
96 var b strings.Builder
97
98 b.WriteString(titleStyle.Render("Account Settings") + "\n\n")
99 b.WriteString(listHeader.Render("Your email accounts:"))
100 b.WriteString("\n\n")
101
102 if len(m.accounts) == 0 {
103 b.WriteString(accountEmailStyle.Render(" No accounts configured.\n"))
104 b.WriteString("\n")
105 }
106
107 for i, account := range m.accounts {
108 displayName := account.Email
109 if account.Name != "" {
110 displayName = fmt.Sprintf("%s (%s)", account.Name, account.Email)
111 }
112
113 providerInfo := account.ServiceProvider
114 if account.ServiceProvider == "custom" {
115 providerInfo = fmt.Sprintf("custom: %s", account.IMAPServer)
116 }
117
118 line := fmt.Sprintf("%s - %s", displayName, accountEmailStyle.Render(providerInfo))
119
120 if m.cursor == i {
121 b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", line)))
122 } else {
123 b.WriteString(accountItemStyle.Render(fmt.Sprintf(" %s", line)))
124 }
125 b.WriteString("\n")
126 }
127
128 // Add Account option
129 addAccountText := "Add New Account"
130 if m.cursor == len(m.accounts) {
131 b.WriteString(selectedAccountItemStyle.Render(fmt.Sprintf("> %s", addAccountText)))
132 } else {
133 b.WriteString(accountItemStyle.Render(fmt.Sprintf(" %s", addAccountText)))
134 }
135 b.WriteString("\n\n")
136
137 b.WriteString(helpStyle.Render("↑/↓: navigate • enter: select • d: delete account • esc: back"))
138
139 if m.confirmingDelete {
140 accountName := m.accounts[m.cursor].Email
141 dialog := DialogBoxStyle.Render(
142 lipgloss.JoinVertical(lipgloss.Center,
143 dangerStyle.Render("Delete account?"),
144 accountEmailStyle.Render(accountName),
145 HelpStyle.Render("\n(y/n)"),
146 ),
147 )
148 return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog)
149 }
150
151 return docStyle.Render(b.String())
152}
153
154// UpdateAccounts updates the list of accounts.
155func (m *Settings) UpdateAccounts(accounts []config.Account) {
156 m.accounts = accounts
157 if m.cursor >= len(accounts) {
158 m.cursor = len(accounts)
159 }
160}