fix: returned old main.go

drew created

Change summary

main.go | 237 ++++++++++++++++++----------------------------------------
1 file changed, 74 insertions(+), 163 deletions(-)

Detailed changes

main.go 🔗

@@ -1,211 +1,122 @@
 package main
 
 import (
+	"fmt"
 	"log"
 	"os"
 	"time"
 
-	// These imports were missing, causing all the "undefined" errors.
 	"github.com/andrinoff/email-cli/config"
-	"github.com/andrinoff/email-cli/fetcher"
 	"github.com/andrinoff/email-cli/sender"
 	"github.com/andrinoff/email-cli/tui"
-
 	tea "github.com/charmbracelet/bubbletea"
 )
 
-type modelState int
-
-const (
-	loginView modelState = iota
-	mainMenu
-	inboxView
-	emailView
-	composeView
-)
-
-// Model holds the application's state.
-type Model struct {
-	state         modelState
-	login         tea.Model
-	mainMenu      tea.Model
-	inbox         tea.Model
-	emailView     tea.Model
-	composer      tea.Model
-	fetcher       *fetcher.Fetcher
-	sender        *sender.Sender
-	emails        []fetcher.Email
-	statusMessage string
-	err           error
+// mainModel now holds the state for the entire application.
+type mainModel struct {
+	current tea.Model
+	config  *config.Config
+	width   int
+	height  int
+	err     error
 }
 
-// New creates the initial model.
-func New() Model {
-	// We start with the login view.
-	return Model{
-		state:    loginView,
-		login:    tui.NewLogin(),
-		mainMenu: tui.NewChoice(),
+// newInitialModel now returns a pointer, which is crucial for state management.
+func newInitialModel(cfg *config.Config) *mainModel {
+	return &mainModel{
+		current: tui.NewChoice(),
+		config:  cfg,
 	}
 }
 
-// Init runs any initial commands.
-func (m Model) Init() tea.Cmd {
-	return tea.EnterAltScreen
+func (m *mainModel) Init() tea.Cmd {
+	return m.current.Init()
 }
 
-// Update handles all messages and updates the model.
-func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	var cmd tea.Cmd
 	var cmds []tea.Cmd
 
-	// Global messages that can be handled regardless of state.
 	switch msg := msg.(type) {
+	case tea.WindowSizeMsg:
+		m.width = msg.Width
+		m.height = msg.Height
+		// Pass the window size message to the current view
+		m.current, cmd = m.current.Update(msg)
+		cmds = append(cmds, cmd)
+		return m, tea.Batch(cmds...)
+
 	case tea.KeyMsg:
 		if msg.String() == "ctrl+c" {
 			return m, tea.Quit
 		}
-	case tui.EmailResultMsg:
-		if msg.Err != nil {
-			m.statusMessage = "❌ Error: " + msg.Err.Error()
-		} else {
-			m.statusMessage = "✅ Email sent successfully!"
-		}
-		m.state = mainMenu
-		return m, tea.Tick(3*time.Second, func(t time.Time) tea.Msg {
-			return tui.ClearStatusMsg{}
-		})
-	case tui.ClearStatusMsg:
-		m.statusMessage = ""
-		return m, nil
-	}
-
-	// State-specific message handling.
-	switch m.state {
-	case loginView:
-		// When the user submits their credentials...
-		if msg, ok := msg.(tui.Credentials); ok {
-			// For now, we'll default to Gmail.
-			service := "gmail"
-			cfg := &config.Config{Email: msg.Email, Password: msg.Password, Service: service}
-			config.Save(cfg)
-			m.fetcher = fetcher.New(cfg)
-			m.sender = sender.New(msg.Email, msg.Password, service)
-			m.state = mainMenu
-			return m, nil
-		}
-
-	case mainMenu:
-		// Handle user's choice from the main menu.
-		if msg, ok := msg.(tea.KeyMsg); ok {
-			switch msg.String() {
-			case "1", "j", "down": // View Inbox
-				m.state = inboxView
-				m.inbox = tui.NewInbox(m.emails)
-				return m, func() tea.Msg {
-					emails, err := m.fetcher.FetchEmails("INBOX")
-					if err != nil {
-						return tui.FetchErr(err)
-					}
-					return tui.EmailsFetchedMsg{Emails: emails}
-				}
-			case "2", "k", "up": // Compose Email
-				m.state = composeView
-				m.composer = tui.NewComposer(m.fetcher.Config.Email)
-				return m, nil
-			}
-		}
-
-	case inboxView:
-		switch msg := msg.(type) {
-		case tui.EmailsFetchedMsg:
-			m.emails = msg.Emails
-			m.inbox = tui.NewInbox(m.emails)
-			return m, nil
-		case tui.ViewEmailMsg:
-			m.state = emailView
-			m.emailView = tui.NewEmailView(m.emails[msg.Index])
-			return m, nil
-		case tea.KeyMsg:
-			if msg.String() == "esc" {
-				m.state = mainMenu
+		// Allow ESC to go back to the main menu
+		if msg.String() == "esc" {
+			if _, ok := m.current.(*tui.Choice); !ok {
+				m.current = tui.NewChoice()
+				return m, m.current.Init()
 			}
 		}
 
-	case emailView:
-		if msg, ok := msg.(tea.KeyMsg); ok && msg.String() == "esc" {
-			m.state = inboxView // Go back to the inbox list.
-		}
-
-	case composeView:
-		switch msg := msg.(type) {
-		case tui.SendEmailMsg:
-			m.statusMessage = "⏳ Sending email..."
-			return m, func() tea.Msg {
-				err := m.sender.Send(msg.To, msg.Subject, msg.Body)
-				return tui.EmailResultMsg{Err: err}
-			}
-		case tea.KeyMsg:
-			if msg.String() == "esc" {
-				m.state = mainMenu
-			}
-		}
+	// --- Custom Messages for switching views ---
+	case tui.GoToInboxMsg:
+		m.current = tui.NewInbox()
+		// Manually set the size of the new view
+		m.current, _ = m.current.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
+		cmds = append(cmds, m.current.Init())
+
+	case tui.GoToSendMsg:
+		m.current = tui.NewComposer(m.config.Email)
+		m.current, _ = m.current.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
+		cmds = append(cmds, m.current.Init())
+
+	case tui.ViewEmailMsg:
+		m.current = tui.NewEmailView(msg.Email, m.width, m.height)
+		cmds = append(cmds, m.current.Init())
+
+	case tui.SendEmailMsg:
+		m.current = tui.NewStatus("Sending email...")
+		cmds = append(cmds, m.current.Init(), sendEmail(m.config, msg))
+
+	case tui.EmailSentMsg:
+		m.current = tui.NewChoice()
+		cmds = append(cmds, m.current.Init())
 	}
 
-	// Pass the message to the current view's Update function.
-	switch m.state {
-	case loginView:
-		m.login, cmd = m.login.Update(msg)
-	case mainMenu:
-		m.mainMenu, cmd = m.mainMenu.Update(msg)
-	case inboxView:
-		m.inbox, cmd = m.inbox.Update(msg)
-	case emailView:
-		m.emailView, cmd = m.emailView.Update(msg)
-	case composeView:
-		m.composer, cmd = m.composer.Update(msg)
-	}
+	// Pass all other messages to the current view
+	m.current, cmd = m.current.Update(msg)
 	cmds = append(cmds, cmd)
+
 	return m, tea.Batch(cmds...)
 }
 
-// View renders the current UI.
-func (m Model) View() string {
-	var s string
-	switch m.state {
-	case loginView:
-		s = m.login.View()
-	case mainMenu:
-		s = m.mainMenu.View()
-	case inboxView:
-		s = m.inbox.View()
-	case emailView:
-		s = m.emailView.View()
-	case composeView:
-		s = m.composer.View()
-	default:
-		s = "Unknown state."
-	}
+func (m *mainModel) View() string {
+	return m.current.View()
+}
 
-	if m.statusMessage != "" {
-		return s + "\n\n" + m.statusMessage
+// sendEmail is a command that sends an email in the background.
+func sendEmail(cfg *config.Config, msg tui.SendEmailMsg) tea.Cmd {
+	return func() tea.Msg {
+		recipients := []string{msg.To}
+		err := sender.SendEmail(cfg, recipients, msg.Subject, msg.Body)
+		if err != nil {
+			log.Printf("Failed to send email: %v", err) // Log error
+		}
+		time.Sleep(1 * time.Second) // Give user time to see the "Sending" message
+		return tui.EmailSentMsg{}
 	}
-	return s
 }
 
 func main() {
-	// Load config first to see if we can skip login.
-	cfg := config.Load()
-	m := New()
-	if cfg.Email != "" && cfg.Password != "" && cfg.Service != "" {
-		m.fetcher = fetcher.New(cfg)
-		m.sender = sender.New(cfg.Email, cfg.Password, cfg.Service)
-		m.state = mainMenu
+	cfg, err := config.LoadConfig()
+	if err != nil {
+		log.Fatalf("could not load config: %v", err)
 	}
 
-	p := tea.NewProgram(m)
+	p := tea.NewProgram(newInitialModel(cfg), tea.WithAltScreen())
+
 	if _, err := p.Run(); err != nil {
-		log.Fatal(err)
+		fmt.Printf("Alas, there's been an error: %v", err)
 		os.Exit(1)
 	}
 }