1package main
2
3import (
4 "fmt"
5 "log"
6 "os"
7 "time"
8
9 "github.com/andrinoff/email-cli/config"
10 "github.com/andrinoff/email-cli/fetcher"
11 "github.com/andrinoff/email-cli/sender"
12 "github.com/andrinoff/email-cli/tui"
13 tea "github.com/charmbracelet/bubbletea"
14)
15
16// mainModel now holds the state for the entire application.
17type mainModel struct {
18 current tea.Model
19 config *config.Config
20 emails []fetcher.Email
21 width int
22 height int
23 err error
24}
25
26// newInitialModel now returns a pointer, which is crucial for state management.
27func newInitialModel(cfg *config.Config) *mainModel {
28 return &mainModel{
29 current: tui.NewChoice(),
30 config: cfg,
31 }
32}
33
34func (m *mainModel) Init() tea.Cmd {
35 return m.current.Init()
36}
37
38func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
39 var cmd tea.Cmd
40 var cmds []tea.Cmd
41
42 switch msg := msg.(type) {
43 case tea.WindowSizeMsg:
44 m.width = msg.Width
45 m.height = msg.Height
46 // Pass the window size message to the current view
47 m.current, cmd = m.current.Update(msg)
48 cmds = append(cmds, cmd)
49 return m, tea.Batch(cmds...)
50
51 case tea.KeyMsg:
52 if msg.String() == "ctrl+c" {
53 return m, tea.Quit
54 }
55 // Allow ESC to go back to the main menu
56 if msg.String() == "esc" {
57 if _, ok := m.current.(*tui.Choice); !ok {
58 m.current = tui.NewChoice()
59 return m, m.current.Init()
60 }
61 }
62
63 // --- Custom Messages for switching views ---
64 case tui.GoToInboxMsg:
65 m.current = tui.NewStatus("Fetching emails...")
66 return m, tea.Batch(m.current.Init(), fetchEmails(m.config))
67
68 case tui.EmailsFetchedMsg:
69 m.emails = msg.Emails
70 m.current = tui.NewInbox(m.emails)
71 // Manually set the size of the new view
72 m.current, _ = m.current.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
73 cmds = append(cmds, m.current.Init())
74
75 case tui.GoToSendMsg:
76 m.current = tui.NewComposer(m.config.Email)
77 m.current, _ = m.current.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
78 cmds = append(cmds, m.current.Init())
79
80 case tui.ViewEmailMsg:
81 m.current = tui.NewEmailView(m.emails[msg.Index], m.width, m.height)
82 cmds = append(cmds, m.current.Init())
83
84 case tui.SendEmailMsg:
85 m.current = tui.NewStatus("Sending email...")
86 cmds = append(cmds, m.current.Init(), sendEmail(m.config, msg))
87
88 case tui.EmailResultMsg:
89 m.current = tui.NewChoice()
90 cmds = append(cmds, m.current.Init())
91 }
92
93 // Pass all other messages to the current view
94 m.current, cmd = m.current.Update(msg)
95 cmds = append(cmds, cmd)
96
97 return m, tea.Batch(cmds...)
98}
99
100func (m *mainModel) View() string {
101 return m.current.View()
102}
103
104// sendEmail is a command that sends an email in the background.
105func sendEmail(cfg *config.Config, msg tui.SendEmailMsg) tea.Cmd {
106 return func() tea.Msg {
107 recipients := []string{msg.To}
108 err := sender.SendEmail(cfg, recipients, msg.Subject, msg.Body)
109 if err != nil {
110 log.Printf("Failed to send email: %v", err) // Log error
111 return tui.EmailResultMsg{Err: err}
112 }
113 time.Sleep(1 * time.Second) // Give user time to see the "Sending" message
114 return tui.EmailResultMsg{}
115 }
116}
117
118func fetchEmails(cfg *config.Config) tea.Cmd {
119 return func() tea.Msg {
120 emails, err := fetcher.FetchEmails(cfg)
121 if err != nil {
122 return tui.FetchErr(err)
123 }
124 return tui.EmailsFetchedMsg{Emails: emails}
125 }
126}
127
128func main() {
129 cfg, err := config.LoadConfig()
130 if err != nil {
131 // If the config doesn't exist, we can guide the user to create one.
132 // For now, we'll just log a fatal error.
133 log.Fatalf("could not load config: %v", err)
134 }
135
136 p := tea.NewProgram(newInitialModel(cfg), tea.WithAltScreen())
137
138 if _, err := p.Run(); err != nil {
139 fmt.Printf("Alas, there's been an error: %v", err)
140 os.Exit(1)
141 }
142}