enbas/internal/printer/printer.go
Dan Anglin eb016b96e9
fix(BREAKING): update poll interaction
Summary:

This commit updates and enhances poll interaction. From now on users
will interact with a poll via the status that contains it. Direct
interaction with the poll (via the poll's ID) is no longer supported.
This helps resolve an issue where it wasn't possible to find the owner
of the poll when interacting with it directly.

Changes:

- Users can no longer view a poll directly using the Poll ID.
  Instead polls can be viewed when viewing statuses or timelines.
- More details about a poll is shown in statuses and timelines.
- Votes are now added to polls via statuses.
- Poll results are hidden unless the following conditions are met.
    - The user is the owner of the poll.
    - The poll has expired.
    - The user has already voted in the poll.
- Enbas can now detect and stop a poll owner from voting in their own
  poll.
- When a status is created Enbas will now only print the ID of the
  created status instead of the whole thing.

PR: apollo/enbas#43

Resolves apollo/enbas#39
2024-08-14 11:29:30 +01:00

186 lines
3.7 KiB
Go

package printer
import (
"os"
"os/exec"
"regexp"
"strings"
"time"
)
const (
minTerminalWidth = 40
noMediaDescription = "This media attachment has no description."
symbolBullet = "\u2022"
symbolPollMeter = "\u2501"
symbolCheckMark = "\u2714"
symbolFailure = "\u2717"
symbolImage = "\uf03e"
symbolLiked = "\uf51f"
symbolNotLiked = "\uf41e"
symbolBookmarked = "\uf47a"
symbolNotBookmarked = "\uf461"
symbolBoosted = "\u2BAD"
dateFormat = "02 Jan 2006"
dateTimeFormat = "02 Jan 2006, 15:04 (MST)"
)
type theme struct {
reset string
boldblue string
boldmagenta string
green string
boldgreen string
grey string
red string
boldred string
yellow string
boldyellow string
}
type Printer struct {
theme theme
noColor bool
lineWrapCharacterLimit int
pager string
statusSeparator string
}
func NewPrinter(
noColor bool,
pager string,
lineWrapCharacterLimit int,
) *Printer {
theme := theme{
reset: "\033[0m",
boldblue: "\033[34;1m",
boldmagenta: "\033[35;1m",
green: "\033[32m",
boldgreen: "\033[32;1m",
grey: "\033[90m",
red: "\033[31m",
boldred: "\033[31;1m",
yellow: "\033[33m",
boldyellow: "\033[33;1m",
}
if lineWrapCharacterLimit < minTerminalWidth {
lineWrapCharacterLimit = minTerminalWidth
}
return &Printer{
theme: theme,
noColor: noColor,
lineWrapCharacterLimit: lineWrapCharacterLimit,
pager: pager,
statusSeparator: strings.Repeat("\u2501", lineWrapCharacterLimit),
}
}
func (p Printer) PrintSuccess(text string) {
success := p.theme.boldgreen + symbolCheckMark + p.theme.reset
if p.noColor {
success = symbolCheckMark
}
printToStdout(success + " " + text + "\n")
}
func (p Printer) PrintFailure(text string) {
failure := p.theme.boldred + symbolFailure + p.theme.reset
if p.noColor {
failure = symbolFailure
}
printToStderr(failure + " " + text + "\n")
}
func (p Printer) PrintInfo(text string) {
printToStdout(text)
}
func (p Printer) headerFormat(text string) string {
if p.noColor {
return text
}
return p.theme.boldblue + text + p.theme.reset
}
func (p Printer) fieldFormat(text string) string {
if p.noColor {
return text
}
return p.theme.green + text + p.theme.reset
}
func (p Printer) fullDisplayNameFormat(displayName, acct string) string {
// use this pattern to remove all emoji strings
pattern := regexp.MustCompile(`\s:[A-Za-z0-9_]*:`)
var builder strings.Builder
if p.noColor {
builder.WriteString(pattern.ReplaceAllString(displayName, ""))
} else {
builder.WriteString(p.theme.boldmagenta + pattern.ReplaceAllString(displayName, "") + p.theme.reset)
}
builder.WriteString(" (@" + acct + ")")
return builder.String()
}
func (p Printer) formatDate(date time.Time) string {
return date.Local().Format(dateFormat)
}
func (p Printer) formatDateTime(date time.Time) string {
return date.Local().Format(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)
}
func printToStderr(text string) {
os.Stderr.WriteString(text)
}