fixes #5, #6

drew created

Change summary

main.go              | 19 ++++++++++++++-----
tui/composer.go      | 31 ++++++++++++++++---------------
tui/composer_test.go | 16 ++++++++--------
tui/inbox.go         | 18 ++++++++++--------
view/html.go         |  3 +++
5 files changed, 51 insertions(+), 36 deletions(-)

Detailed changes

main.go 🔗

@@ -18,6 +18,7 @@ type mainModel struct {
 	current tea.Model
 	config  *config.Config
 	emails  []fetcher.Email
+	inbox   *tui.Inbox
 	width   int
 	height  int
 	err     error
@@ -58,10 +59,15 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if msg.String() == "ctrl+c" {
 			return m, tea.Quit
 		}
-		// Allow ESC to go back to the main menu
+		// Allow ESC to go back
 		if msg.String() == "esc" {
-			if _, ok := m.current.(*tui.Choice); !ok {
-				m.current = tui.NewChoice()
+			// Check the type of the current view to decide where to go.
+			switch m.current.(type) {
+			case *tui.EmailView:
+				m.current = m.inbox // Go back to the cached inbox
+				return m, nil
+			case *tui.Inbox, *tui.Composer:
+				m.current = tui.NewChoice() // Go back to the main menu
 				return m, m.current.Init()
 			}
 		}
@@ -89,18 +95,21 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 
 	case tui.EmailsFetchedMsg:
 		m.emails = msg.Emails
-		m.current = tui.NewInbox(m.emails)
+		m.inbox = tui.NewInbox(m.emails)
+		m.current = m.inbox
 		// 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:
+		// NewComposer now returns *Composer, so we assign it directly.
 		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(m.emails[msg.Index], m.width, m.height)
+		emailView := tui.NewEmailView(m.emails[msg.Index], m.width, m.height)
+		m.current = emailView
 		cmds = append(cmds, m.current.Init())
 
 	case tui.SendEmailMsg:

tui/composer.go 🔗

@@ -20,16 +20,16 @@ var (
 
 // Composer model holds the state of the email composition UI.
 type Composer struct {
-	focusIndex int
-	toInput    textinput.Model
+	focusIndex   int
+	toInput      textinput.Model
 	subjectInput textinput.Model
-	bodyInput  textarea.Model
-	fromAddr   string
+	bodyInput    textarea.Model
+	fromAddr     string
 }
 
 // NewComposer initializes a new composer model.
-func NewComposer(from string) Composer {
-	m := Composer{fromAddr: from}
+func NewComposer(from string) *Composer {
+	m := &Composer{fromAddr: from}
 
 	m.toInput = textinput.New()
 	m.toInput.Cursor.Style = cursorStyle
@@ -54,18 +54,19 @@ func NewComposer(from string) Composer {
 	return m
 }
 
-func (m Composer) Init() tea.Cmd {
+func (m *Composer) Init() tea.Cmd {
 	return textinput.Blink
 }
 
-func (m Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	var cmds []tea.Cmd
 	var cmd tea.Cmd
 
 	switch msg := msg.(type) {
 	case tea.KeyMsg:
 		switch msg.Type {
-		case tea.KeyCtrlC, tea.KeyEsc:
+		// IMPORTANT: Removed tea.KeyEsc from this case
+		case tea.KeyCtrlC:
 			return m, tea.Quit
 
 		// Handle Tab and Shift+Tab to cycle focus between inputs.
@@ -82,7 +83,7 @@ func (m Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			} else if m.focusIndex < 0 {
 				m.focusIndex = 3
 			}
-			
+
 			// Blur all inputs
 			m.toInput.Blur()
 			m.subjectInput.Blur()
@@ -91,11 +92,11 @@ func (m Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			// Focus the correct input
 			switch m.focusIndex {
 			case 0:
-				m.toInput.Focus()
+				cmds = append(cmds, m.toInput.Focus())
 			case 1:
-				m.subjectInput.Focus()
+				cmds = append(cmds, m.subjectInput.Focus())
 			case 2:
-				m.bodyInput.Focus()
+				cmds = append(cmds, m.bodyInput.Focus())
 			}
 			return m, tea.Batch(cmds...)
 
@@ -131,7 +132,7 @@ func (m Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 }
 
 // View renders the UI.
-func (m Composer) View() string {
+func (m *Composer) View() string {
 	button := &blurredButton
 	if m.focusIndex == 3 {
 		button = &focusedButton
@@ -144,6 +145,6 @@ func (m Composer) View() string {
 		m.subjectInput.View(),
 		m.bodyInput.View(),
 		*button,
-		helpStyle.Render("tab: next field • esc: quit"),
+		helpStyle.Render("tab: next field • esc: back to menu"),
 	)
 }

tui/composer_test.go 🔗

@@ -19,29 +19,29 @@ func TestComposerUpdate(t *testing.T) {
 		}
 
 		// Simulate pressing Tab to move to the 'Subject' field.
-		model, _ := composer.Update(tea.KeyMsg{Type: tea.KeyTab})
-		composer = model.(Composer)
+		model, _ := composer.Update(tea.KeyMsg{Type: tea.KeyTab}) // model is tea.Model
+		composer = model.(*Composer) // Cast to *Composer
 		if composer.focusIndex != 1 {
 			t.Errorf("After one Tab, focusIndex should be 1, got %d", composer.focusIndex)
 		}
 
 		// Simulate pressing Tab again to move to the 'Body' field.
-		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab})
-		composer = model.(Composer)
+		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) // model is tea.Model
+		composer = model.(*Composer) // Cast to *Composer
 		if composer.focusIndex != 2 {
 			t.Errorf("After two Tabs, focusIndex should be 2, got %d", composer.focusIndex)
 		}
 
 		// Simulate pressing Tab again to move to the 'Send' button.
-		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab})
-		composer = model.(Composer)
+		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) // model is tea.Model
+		composer = model.(*Composer) // Cast to *Composer
 		if composer.focusIndex != 3 {
 			t.Errorf("After three Tabs, focusIndex should be 3 (Send), got %d", composer.focusIndex)
 		}
 
 		// Simulate one more Tab to wrap around to the 'To' field.
-		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab})
-		composer = model.(Composer)
+		model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) // model is tea.Model
+		composer = model.(*Composer) // Cast to *Composer
 		if composer.focusIndex != 0 {
 			t.Errorf("After four Tabs, focusIndex should wrap to 0, got %d", composer.focusIndex)
 		}

tui/inbox.go 🔗

@@ -11,8 +11,8 @@ import (
 )
 
 var (
-	paginationStyle   = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
-	inboxHelpStyle    = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
+	paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
+	inboxHelpStyle  = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
 )
 
 type item struct {
@@ -50,7 +50,7 @@ type Inbox struct {
 	list list.Model
 }
 
-func NewInbox(emails []fetcher.Email) Inbox {
+func NewInbox(emails []fetcher.Email) *Inbox {
 	items := make([]list.Item, len(emails))
 	for i, email := range emails {
 		items[i] = item{
@@ -67,14 +67,14 @@ func NewInbox(emails []fetcher.Email) Inbox {
 	l.Styles.PaginationStyle = paginationStyle
 	l.Styles.HelpStyle = inboxHelpStyle
 
-	return Inbox{list: l}
+	return &Inbox{list: l}
 }
 
-func (m Inbox) Init() tea.Cmd {
+func (m *Inbox) Init() tea.Cmd {
 	return nil
 }
 
-func (m Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	switch msg := msg.(type) {
 	case tea.KeyMsg:
 		if msg.String() == "enter" {
@@ -92,10 +92,12 @@ func (m Inbox) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	}
 
 	var cmd tea.Cmd
-	m.list, cmd = m.list.Update(msg)
+	var newModel list.Model
+	newModel, cmd = m.list.Update(msg)
+	m.list = newModel
 	return m, cmd
 }
 
-func (m Inbox) View() string {
+func (m *Inbox) View() string {
 	return "\n" + m.list.View()
 }

view/html.go 🔗

@@ -40,6 +40,9 @@ func ProcessBody(rawBody string) (string, error) {
 		return "", fmt.Errorf("could not parse email body: %w", err)
 	}
 
+	// Remove style and script tags to clean up the view
+	doc.Find("style, script").Remove()
+
 	// Add newlines after block elements for better spacing
 	doc.Find("p, div, h1, h2, h3, h4, h5, h6").Each(func(i int, s *goquery.Selection) {
 		s.After("\n\n")