checkpoint: printer now prints statuses and status lists
This commit is contained in:
parent
d842233c98
commit
09cd13a2f7
6 changed files with 203 additions and 89 deletions
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
62
internal/printer/poll.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
93
internal/printer/status.go
Normal file
93
internal/printer/status.go
Normal 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())
|
||||
}
|
Loading…
Reference in a new issue