Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions internal/tui/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -2981,16 +2981,23 @@ func (v *mailView) renderEntries(entries []mail.Entry) (string, []int) {
from = e.AlternativeSenderName
}

// A full blank line separates the header from whatever follows it —
// the summary here, or the body's own leading blank when there is none.
// Each arm below opens with a blank line, which is what separates the header
// from whatever follows it. The summary is HEY's ~105-character preview of the
// body, so it stands in only where HEY served no body at all — the same ladder
// as printThreadStyled in internal/cmd/topic.go. Printed beside a body it repeats
// the message's opening line; printed for a body that was read and rendered to
// nothing, or for one that was not read, it passes a preview off as the message.
fmt.Fprintf(&b, "%s %s\n", v.vc.styles.entryFrom.Render(terminal.SanitizeLine(from)), v.vc.styles.entryDate.Render(formatDisplayDateTime(e.CreatedAt)))
if e.Summary != "" {
fmt.Fprintf(&b, "\n%s\n", terminal.SanitizeLine(e.Summary))
}
switch {
case !e.Body.IsEmpty():
fmt.Fprintf(&b, "\n%s\n", v.vc.styles.entryBody.Render(markdown.Render(e.Body, sepWidth)))
case e.BodyState == string(threadload.StateOverLimit), e.BodyState == string(threadload.StateFailed):
case e.BodyState == string(threadload.StateHydrated):
fmt.Fprintf(&b, "\n%s\n", v.vc.styles.entryDate.Render("(empty body)"))
case e.BodyState == string(threadload.StateBodyless) && e.Summary != "":
fmt.Fprintf(&b, "\n%s\n", terminal.SanitizeLine(e.Summary))
case e.BodyState == string(threadload.StateBodyless):
fmt.Fprintf(&b, "\n%s\n", v.vc.styles.entryDate.Render("(no body)"))
default:
fmt.Fprintf(&b, "\n%s\n", v.vc.styles.entryDate.Render("(body not read: "+e.BodyState+")"))
}
entryAttachments := attachmentsForMessage(v.attachments, e.ID)
Expand Down
59 changes: 57 additions & 2 deletions internal/tui/mail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/basecamp/hey-cli/internal/apierr"
"github.com/basecamp/hey-cli/internal/htmlutil"
"github.com/basecamp/hey-cli/internal/mail"
"github.com/basecamp/hey-cli/internal/threadload"
)

func testPNG(t *testing.T) []byte {
Expand Down Expand Up @@ -331,8 +332,8 @@ func TestThreadViewShowsSubjectAndSpacesTheHeader(t *testing.T) {
if strings.TrimSpace(lines[header+1]) != "" {
t.Errorf("a blank line should follow the header: %q", lines[header+1])
}
if strings.TrimSpace(lines[header+2]) != "Can we meet Thursday?" {
t.Errorf("the summary should follow the blank line: %q", lines[header+2])
if strings.TrimSpace(lines[header+2]) != "Can we meet Thursday to walk through the numbers?" {
t.Errorf("the body should follow the blank line: %q", lines[header+2])
}
}

Expand Down Expand Up @@ -3710,6 +3711,60 @@ func TestMailViewReadsEveryPageOfAThreadAndMarksUnreadBodies(t *testing.T) {
}
}

// HEY's summary is its ~105-character preview of the body, so it belongs on screen only
// where the body itself cannot be. Beside a body it repeats the message's opening line;
// for a body that was not read it would pass a preview off as the message.
func TestRenderEntriesShowsTheBodyRatherThanHEYsPreviewOfIt(t *testing.T) {
v := newMailView(testVC())
v.vc.width = 80
entries := []mail.Entry{
{
ID: 1,
Creator: mail.Contact{Name: "Maria Gonzalez"},
Summary: "Moving the review to Friday so ...",
Body: htmlutil.ToMarkdown("<p>Moving the review to Friday so Sam can join.</p>"),
BodyState: string(threadload.StateHydrated),
},
{
ID: 2,
Creator: mail.Contact{Name: "Sam Rivera"},
Summary: "The quarterly figures are attached.",
BodyState: string(threadload.StateBodyless),
},
{
ID: 3,
Creator: mail.Contact{Name: "Ana Ortiz"},
Summary: "Never shown, the body went unread ...",
BodyState: string(threadload.StateFailed),
},
{
ID: 4,
Creator: mail.Contact{Name: "Lee Park"},
Summary: "Markup only, so HEY still has a preview ...",
Body: htmlutil.ToMarkdown("<div><br></div>"),
BodyState: string(threadload.StateHydrated),
},
}

rendered, _ := v.renderEntries(entries)

if strings.Contains(rendered, "Friday so ...") {
t.Errorf("a hydrated entry repeats HEY's preview of its own body:\n%s", rendered)
}
if !strings.Contains(rendered, "Sam can join.") {
t.Errorf("a hydrated entry lost its body:\n%s", rendered)
}
if !strings.Contains(rendered, "The quarterly figures are attached.") {
t.Errorf("a bodyless entry lost its summary, which is all HEY serves for it:\n%s", rendered)
}
if strings.Contains(rendered, "Never shown") || !strings.Contains(rendered, "(body not read: failed)") {
t.Errorf("an unread body should say so rather than show a preview:\n%s", rendered)
}
if strings.Contains(rendered, "Markup only") || !strings.Contains(rendered, "(empty body)") {
t.Errorf("a body that was read and rendered to nothing should say so rather than show a preview:\n%s", rendered)
}
}

func TestRenderEntriesReportsEveryMessageHeaderOffset(t *testing.T) {
v := newMailView(testVC())
v.vc.width = 60
Expand Down