checkpoint: printer now prints statuses and status lists

This commit is contained in:
Dan Anglin 2024-06-17 00:27:56 +01:00
parent d842233c98
commit 09cd13a2f7
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
6 changed files with 203 additions and 89 deletions

View file

@ -171,7 +171,7 @@ func (s *ShowExecutor) showStatus(gtsClient *client.Client) error {
return nil
}
utilities.Display(status, false, "")
s.printer.PrintStatus(status)
return nil
}
@ -220,7 +220,7 @@ func (s *ShowExecutor) showTimeline(gtsClient *client.Client) error {
return nil
}
utilities.Display(timeline, false, "")
s.printer.PrintStatusList(timeline)
return nil
}
@ -333,7 +333,7 @@ func (s *ShowExecutor) showBookmarks(gtsClient *client.Client) error {
}
if len(bookmarks.Statuses) > 0 {
utilities.Display(bookmarks, false, "")
s.printer.PrintStatusList(bookmarks)
} else {
s.printer.PrintInfo("You have no bookmarks.\n")
}
@ -348,7 +348,7 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
}
if len(liked.Statuses) > 0 {
utilities.Display(liked, false, "")
s.printer.PrintStatusList(liked)
} else {
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
}

View file

@ -5,11 +5,7 @@
package model
import (
"strconv"
"strings"
"time"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
type Status struct {
@ -139,86 +135,7 @@ type MediaDimensions struct {
Width int `json:"width"`
}
func (s Status) Display(noColor bool) string {
indent := " "
var builder strings.Builder
// The account information
builder.WriteString(utilities.FullDisplayNameFormat(noColor, s.Account.DisplayName, s.Account.Acct) + "\n\n")
// The content of the status.
builder.WriteString(utilities.HeaderFormat(noColor, "CONTENT:"))
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(s.Content), "\n ", 80))
// If a poll exists in a status, write the contents to the builder.
if s.Poll != nil {
displayPollContent(&builder, *s.Poll, noColor, indent)
}
// The ID of the status
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "STATUS ID:") + "\n" + indent + s.ID)
// Status creation time
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "CREATED AT:") + "\n" + indent + utilities.FormatTime(s.CreatedAt))
// Status stats
builder.WriteString(
"\n\n" +
utilities.HeaderFormat(noColor, "STATS:") +
"\n" + indent + utilities.FieldFormat(noColor, "Boosts: ") + strconv.Itoa(s.ReblogsCount) +
"\n" + indent + utilities.FieldFormat(noColor, "Likes: ") + strconv.Itoa(s.FavouritesCount) +
"\n" + indent + utilities.FieldFormat(noColor, "Replies: ") + strconv.Itoa(s.RepliesCount),
)
// Status visibility
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "VISIBILITY:") + "\n" + indent + s.Visibility.String())
// Status URL
builder.WriteString("\n\n" + utilities.HeaderFormat(noColor, "URL:") + "\n" + indent + s.URL)
return builder.String()
}
type StatusList struct {
Name string
Statuses []Status
}
func (s StatusList) Display(noColor bool) string {
var builder strings.Builder
separator := strings.Repeat("─", 80)
builder.WriteString(utilities.HeaderFormat(noColor, s.Name) + "\n")
for _, status := range s.Statuses {
builder.WriteString("\n" + utilities.FullDisplayNameFormat(noColor, status.Account.DisplayName, status.Account.Acct) + "\n")
statusID := status.ID
createdAt := status.CreatedAt
if status.Reblog != nil {
builder.WriteString("reposted this status from " + utilities.FullDisplayNameFormat(noColor, status.Reblog.Account.DisplayName, status.Reblog.Account.Acct) + "\n")
statusID = status.Reblog.ID
createdAt = status.Reblog.CreatedAt
}
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", 80))
if status.Poll != nil {
displayPollContent(&builder, *status.Poll, noColor, "")
}
builder.WriteString(
"\n\n" +
utilities.FieldFormat(noColor, "Status ID:") + " " + statusID + "\t" +
utilities.FieldFormat(noColor, "Created at:") + " " + utilities.FormatTime(createdAt) +
"\n",
)
builder.WriteString(separator + "\n")
}
return builder.String()
}

View file

@ -102,7 +102,7 @@ func (p Printer) PrintAccountRelationship(relationship model.AccountRelationship
builder.WriteString("\n" + utilities.WrapLines(relationship.PrivateNote, "\n", p.maxTerminalWidth))
}
builder.WriteString("\n")
builder.WriteString("\n\n")
printToStdout(builder.String())
}

62
internal/printer/poll.go Normal file
View file

@ -0,0 +1,62 @@
package printer
import (
"math"
"strconv"
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
func (p Printer) pollContent(poll model.Poll) string {
var builder strings.Builder
for ind, option := range poll.Options {
var (
votage float64
percentage int
)
if poll.VotesCount == 0 {
percentage = 0
} else {
votage = float64(option.VotesCount) / float64(poll.VotesCount)
percentage = int(math.Floor(100 * votage))
}
builder.WriteString("\n\n" + "[" + strconv.Itoa(ind) + "] " + option.Title)
builder.WriteString(p.pollMeter(votage))
builder.WriteString("\n" + strconv.Itoa(option.VotesCount) + " votes " + "(" + strconv.Itoa(percentage) + "%)")
}
builder.WriteString("\n\n" + p.fieldFormat("Total votes:") + " " + strconv.Itoa(poll.VotesCount))
builder.WriteString("\n" + p.fieldFormat("Poll ID:") + " " + poll.ID)
builder.WriteString("\n" + p.fieldFormat("Poll is open until:") + " " + utilities.FormatTime(poll.ExpiredAt))
return builder.String()
}
func (p Printer) pollMeter(votage float64) string {
numVoteBlocks := int(math.Floor(float64(p.maxTerminalWidth) * votage))
numBackgroundBlocks := p.maxTerminalWidth - numVoteBlocks
voteBlockColor := p.theme.boldgreen
backgroundBlockColor := p.theme.grey
if p.noColor {
voteBlockColor = p.theme.reset
if numVoteBlocks == 0 {
numVoteBlocks = 1
}
}
meter := "\n" + voteBlockColor + strings.Repeat(p.pollMeterSymbol, numVoteBlocks) + p.theme.reset
if !p.noColor {
meter += backgroundBlockColor + strings.Repeat(p.pollMeterSymbol, numBackgroundBlocks) + p.theme.reset
}
return meter
}

View file

@ -6,6 +6,7 @@ package printer
import (
"os"
"os/exec"
"regexp"
"strings"
"time"
@ -16,6 +17,8 @@ type theme struct {
boldblue string
boldmagenta string
green string
boldgreen string
grey string
}
type Printer struct {
@ -42,13 +45,15 @@ func NewPrinter(
boldblue: "\033[34;1m",
boldmagenta: "\033[35;1m",
green: "\033[32m",
boldgreen: "\033[32;1m",
grey: "\033[90m",
}
return &Printer{
noColor: noColor,
maxTerminalWidth: maxTerminalWidth,
pager: pager,
statusSeparator: strings.Repeat("", maxTerminalWidth),
statusSeparator: strings.Repeat("\u2501", maxTerminalWidth),
bullet: "\u2022",
pollMeterSymbol: "\u2501",
successSymbol: "\u2714",
@ -117,6 +122,43 @@ func (p Printer) formatDateTime(date time.Time) string {
return date.Local().Format(p.dateTimeFormat)
}
func (p Printer) print(text string) {
if p.pager == "" {
printToStdout(text)
return
}
cmdSplit := strings.Split(p.pager, " ")
pager := new(exec.Cmd)
if len(cmdSplit) == 1 {
pager = exec.Command(cmdSplit[0]) //nolint:gosec
} else {
pager = exec.Command(cmdSplit[0], cmdSplit[1:]...) //nolint:gosec
}
pipe, err := pager.StdinPipe()
if err != nil {
printToStdout(text)
return
}
pager.Stdout = os.Stdout
pager.Stderr = os.Stderr
_ = pager.Start()
defer func() {
_ = pipe.Close()
_ = pager.Wait()
}()
_, _ = pipe.Write([]byte(text))
}
func printToStdout(text string) {
os.Stdout.WriteString(text)
}

View file

@ -0,0 +1,93 @@
package printer
import (
"strconv"
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
func (p Printer) PrintStatus(status model.Status) {
var builder strings.Builder
// The account information
builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct))
// The content of the status.
builder.WriteString("\n\n" + p.headerFormat("CONTENT:"))
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
// If a poll exists in a status, write the contents to the builder.
if status.Poll != nil {
builder.WriteString(p.pollContent(*status.Poll))
}
// The ID of the status
builder.WriteString("\n\n" + p.headerFormat("STATUS ID:"))
builder.WriteString("\n" + status.ID)
// Status creation time
builder.WriteString("\n\n" + p.headerFormat("CREATED AT:"))
builder.WriteString("\n" + p.formatDateTime(status.CreatedAt))
// Status stats
builder.WriteString("\n\n" + p.headerFormat("STATS:"))
builder.WriteString("\n" + p.fieldFormat("Boosts: ") + strconv.Itoa(status.ReblogsCount))
builder.WriteString("\n" + p.fieldFormat("Likes: ") + strconv.Itoa(status.FavouritesCount))
builder.WriteString("\n" + p.fieldFormat("Replies: ") + strconv.Itoa(status.RepliesCount))
// Status visibility
builder.WriteString("\n\n" + p.headerFormat("VISIBILITY:"))
builder.WriteString("\n" + status.Visibility.String())
// Status URL
builder.WriteString("\n\n" + p.headerFormat("URL:"))
builder.WriteString("\n" + status.URL)
builder.WriteString("\n\n")
p.print(builder.String())
}
func (p Printer) PrintStatusList(list model.StatusList) {
var builder strings.Builder
builder.WriteString(p.headerFormat(list.Name) + "\n")
for _, status := range list.Statuses {
builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct))
statusID := status.ID
createdAt := status.CreatedAt
if status.Reblog != nil {
builder.WriteString(utilities.WrapLines(
"\n"+
"reposted this status from "+
p.fullDisplayNameFormat(status.Reblog.Account.DisplayName, status.Reblog.Account.Acct),
"\n",
p.maxTerminalWidth,
))
statusID = status.Reblog.ID
createdAt = status.Reblog.CreatedAt
}
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
if status.Poll != nil {
builder.WriteString(p.pollContent(*status.Poll))
}
builder.WriteString(
"\n\n" +
p.fieldFormat("Status ID:") + " " + statusID + "\t" +
p.fieldFormat("Created at:") + " " + utilities.FormatTime(createdAt) +
"\n",
)
builder.WriteString(p.statusSeparator + "\n")
}
p.print(builder.String())
}