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
This commit is contained in:
parent
a0eab3b6ae
commit
eb016b96e9
13 changed files with 215 additions and 215 deletions
|
@ -39,6 +39,7 @@
|
|||
- [View a list of statuses that you've liked](#view-a-list-of-statuses-that-youve-liked)
|
||||
- [Mute a status](#mute-a-status)
|
||||
- [Unmute a status](#unmute-a-status)
|
||||
- [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||
- [Polls](#polls)
|
||||
- [Create a poll](#create-a-poll)
|
||||
- [View a poll](#view-a-poll)
|
||||
|
@ -528,6 +529,21 @@ _Not yet supported_
|
|||
|
||||
_Not yet supported_
|
||||
|
||||
### Vote in a poll within a status
|
||||
|
||||
Adds your vote(s) to a poll within a status.
|
||||
|
||||
```
|
||||
enbas add --type vote --to status --status-id 01J55XVV2MM6MKQ7QHFBAVAE8R --vote 3
|
||||
```
|
||||
|
||||
| flag | type | required | description | default |
|
||||
|------|------|----------|-------------|---------|
|
||||
| `type` | string | true | The resource you want to add.<br>Here this should be `vote`. | |
|
||||
| `to` | string | true | The resource you want to add the vote to.<br>Here this should be `status`. | |
|
||||
| `status-id` | string | true | The ID of the poll you want to add the votes to. | |
|
||||
| `vote` | int | true | The ID of the option that you want to vote for.<br>You can use this flag multiple times to vote for more than one option if the poll allows multiple choices. | |
|
||||
|
||||
## Polls
|
||||
|
||||
### Create a poll
|
||||
|
@ -536,31 +552,11 @@ See [Create a status](#create-a-status).
|
|||
|
||||
### View a poll
|
||||
|
||||
Prints the poll information to the screen.
|
||||
|
||||
```
|
||||
enbas show --type poll --poll-id 01J0CEEZBZ6E6AYQSJPHCQYBDA
|
||||
```
|
||||
|
||||
| flag | type | required | description | default |
|
||||
|------|------|----------|-------------|---------|
|
||||
| `type` | string | true | The resource you want to view.<br>Here this should be `poll`. | |
|
||||
| `poll-id` | string | true | The ID of the poll that you want to view. | |
|
||||
You can view a poll within a [status](#view-a-status) or within a [timeline](#view-a-timeline).
|
||||
|
||||
### Vote in a poll
|
||||
|
||||
Add your vote(s) to a poll.
|
||||
|
||||
```
|
||||
enbas add --type vote --to poll --poll-id 01J1TVJ705VV3VP02FVVBSMX7E --vote 3
|
||||
```
|
||||
|
||||
| flag | type | required | description | default |
|
||||
|------|------|----------|-------------|---------|
|
||||
| `type` | string | true | The resource you want to add.<br>Here this should be `vote`. | |
|
||||
| `to` | string | true | The resource you want to add the vote to.<br>Here this should be `poll`. | |
|
||||
| `poll-id` | string | true | The ID of the poll you want to add the votes to. | |
|
||||
| `vote` | int | true | The ID of the option that you want to vote for.<br>You can use this flag multiple times to vote for more than one option if the poll allows multiple choices. | |
|
||||
See [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||
|
||||
## Lists
|
||||
|
||||
|
|
|
@ -5,36 +5,34 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
pollPath string = "/api/v1/polls"
|
||||
)
|
||||
|
||||
func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
||||
url := g.Authentication.Instance + pollPath + "/" + pollID
|
||||
|
||||
var poll model.Poll
|
||||
|
||||
params := requestParameters{
|
||||
httpMethod: http.MethodGet,
|
||||
url: url,
|
||||
requestBody: nil,
|
||||
contentType: "",
|
||||
output: &poll,
|
||||
}
|
||||
|
||||
if err := g.sendRequest(params); err != nil {
|
||||
return model.Poll{}, fmt.Errorf(
|
||||
"received an error after sending the request to get the poll: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return poll, nil
|
||||
}
|
||||
// func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
||||
// url := g.Authentication.Instance + pollPath + "/" + pollID
|
||||
//
|
||||
// var poll model.Poll
|
||||
//
|
||||
// params := requestParameters{
|
||||
// httpMethod: http.MethodGet,
|
||||
// url: url,
|
||||
// requestBody: nil,
|
||||
// contentType: "",
|
||||
// output: &poll,
|
||||
// }
|
||||
//
|
||||
// if err := g.sendRequest(params); err != nil {
|
||||
// return model.Poll{}, fmt.Errorf(
|
||||
// "received an error after sending the request to get the poll: %w",
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// return poll, nil
|
||||
// }
|
||||
|
||||
func (g *Client) VoteInPoll(pollID string, choices []int) error {
|
||||
form := struct {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||
|
@ -17,7 +16,6 @@ func (a *AddExecutor) Execute() error {
|
|||
resourceAccount: a.addToAccount,
|
||||
resourceBookmarks: a.addToBookmarks,
|
||||
resourceStatus: a.addToStatus,
|
||||
resourcePoll: a.addToPoll,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[a.toResourceType]
|
||||
|
@ -164,6 +162,7 @@ func (a *AddExecutor) addToStatus(gtsClient *client.Client) error {
|
|||
resourceStar: a.addStarToStatus,
|
||||
resourceLike: a.addStarToStatus,
|
||||
resourceBoost: a.addBoostToStatus,
|
||||
resourceVote: a.addVoteToStatus,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[a.resourceType]
|
||||
|
@ -197,45 +196,40 @@ func (a *AddExecutor) addBoostToStatus(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *AddExecutor) addToPoll(gtsClient *client.Client) error {
|
||||
if a.pollID == "" {
|
||||
return FlagNotSetError{flagText: flagPollID}
|
||||
}
|
||||
|
||||
funcMap := map[string]func(*client.Client) error{
|
||||
resourceVote: a.addVoteToPoll,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[a.resourceType]
|
||||
if !ok {
|
||||
return UnsupportedAddOperationError{
|
||||
ResourceType: a.resourceType,
|
||||
AddToResourceType: a.toResourceType,
|
||||
}
|
||||
}
|
||||
|
||||
return doFunc(gtsClient)
|
||||
}
|
||||
|
||||
func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
|
||||
func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error {
|
||||
if a.votes.Empty() {
|
||||
return errors.New("please use --" + flagVote + " to make a choice in this poll")
|
||||
return NoVotesError{}
|
||||
}
|
||||
|
||||
poll, err := gtsClient.GetPoll(a.pollID)
|
||||
status, err := gtsClient.GetStatus(a.statusID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
||||
return fmt.Errorf("unable to get the status: %w", err)
|
||||
}
|
||||
|
||||
if poll.Expired {
|
||||
if status.Poll == nil {
|
||||
return NoPollInStatusError{}
|
||||
}
|
||||
|
||||
if status.Poll.Expired {
|
||||
return PollClosedError{}
|
||||
}
|
||||
|
||||
if !poll.Multiple && !a.votes.ExpectedLength(1) {
|
||||
if !status.Poll.Multiple && !a.votes.ExpectedLength(1) {
|
||||
return MultipleChoiceError{}
|
||||
}
|
||||
|
||||
if err := gtsClient.VoteInPoll(a.pollID, []int(a.votes)); err != nil {
|
||||
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
|
||||
if status.Account.ID == myAccountID {
|
||||
return PollOwnerVoteError{}
|
||||
}
|
||||
|
||||
pollID := status.Poll.ID
|
||||
|
||||
if err := gtsClient.VoteInPoll(pollID, []int(a.votes)); err != nil {
|
||||
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -149,8 +149,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
return fmt.Errorf("unable to create the status: %w", err)
|
||||
}
|
||||
|
||||
c.printer.PrintSuccess("Successfully created the following status:")
|
||||
c.printer.PrintStatus(status)
|
||||
c.printer.PrintSuccess("Successfully created the status with ID: " + status.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -104,6 +104,24 @@ func (e NoPollOptionError) Error() string {
|
|||
" flag to add options to the poll"
|
||||
}
|
||||
|
||||
type NoVotesError struct{}
|
||||
|
||||
func (e NoVotesError) Error() string {
|
||||
return "no votes were made, please add your vote(s) using the --vote flag"
|
||||
}
|
||||
|
||||
type NoPollInStatusError struct{}
|
||||
|
||||
func (e NoPollInStatusError) Error() string {
|
||||
return "this status does not have a poll"
|
||||
}
|
||||
|
||||
type PollOwnerVoteError struct{}
|
||||
|
||||
func (e PollOwnerVoteError) Error() string {
|
||||
return "you cannot vote in your own poll"
|
||||
}
|
||||
|
||||
type NotFollowingError struct {
|
||||
Account string
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ type AddExecutor struct {
|
|||
accountNames internalFlag.StringSliceValue
|
||||
content string
|
||||
listID string
|
||||
pollID string
|
||||
statusID string
|
||||
toResourceType string
|
||||
resourceType string
|
||||
|
@ -80,7 +79,6 @@ func NewAddExecutor(
|
|||
exe.Var(&exe.accountNames, "account-name", "The name of the account")
|
||||
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
|
||||
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||
exe.StringVar(&exe.pollID, "poll-id", "", "The ID of the poll")
|
||||
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
|
||||
exe.StringVar(&exe.toResourceType, "to", "", "The resource type to action the target resource to (e.g. status)")
|
||||
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||
|
@ -434,7 +432,6 @@ type ShowExecutor struct {
|
|||
onlyMedia bool
|
||||
onlyPinned bool
|
||||
onlyPublic bool
|
||||
pollID string
|
||||
showUserPreferences bool
|
||||
showStatuses bool
|
||||
skipAccountRelationship bool
|
||||
|
@ -472,7 +469,6 @@ func NewShowExecutor(
|
|||
exe.BoolVar(&exe.onlyMedia, "only-media", false, "Set to true to show only the statuses with media attachments")
|
||||
exe.BoolVar(&exe.onlyPinned, "only-pinned", false, "Set to true to show only the account's pinned statuses")
|
||||
exe.BoolVar(&exe.onlyPublic, "only-public", false, "Set to true to show only the account's public posts")
|
||||
exe.StringVar(&exe.pollID, "poll-id", "", "The ID of the poll")
|
||||
exe.BoolVar(&exe.showUserPreferences, "show-preferences", false, "Set to true to view your posting preferences when viewing your account information")
|
||||
exe.BoolVar(&exe.showStatuses, "show-statuses", false, "Set to true to view the statuses created from the account you are viewing")
|
||||
exe.BoolVar(&exe.skipAccountRelationship, "skip-relationship", false, "Set to true to skip showing your relationship to the account that you are viewing")
|
||||
|
|
|
@ -8,11 +8,9 @@ const (
|
|||
flagInstance = "instance"
|
||||
flagListID = "list-id"
|
||||
flagListTitle = "list-title"
|
||||
flagPollID = "poll-id"
|
||||
flagPollOption = "poll-option"
|
||||
flagStatusID = "status-id"
|
||||
flagTag = "tag"
|
||||
flagTo = "to"
|
||||
flagType = "type"
|
||||
flagVote = "vote"
|
||||
)
|
||||
|
|
|
@ -28,7 +28,6 @@ func (s *ShowExecutor) Execute() error {
|
|||
resourceLiked: s.showLiked,
|
||||
resourceStarred: s.showLiked,
|
||||
resourceFollowRequest: s.showFollowRequests,
|
||||
resourcePoll: s.showPoll,
|
||||
resourceMutedAccounts: s.showMutedAccounts,
|
||||
resourceMedia: s.showMedia,
|
||||
resourceMediaAttachment: s.showMediaAttachment,
|
||||
|
@ -76,6 +75,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
relationship *model.AccountRelationship
|
||||
preferences *model.Preferences
|
||||
statuses *model.StatusList
|
||||
myAccountID string
|
||||
)
|
||||
|
||||
if !s.myAccount && !s.skipAccountRelationship {
|
||||
|
@ -85,10 +85,13 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
}
|
||||
|
||||
if s.myAccount && s.showUserPreferences {
|
||||
preferences, err = gtsClient.GetUserPreferences()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
||||
if s.myAccount {
|
||||
myAccountID = account.ID
|
||||
if s.showUserPreferences {
|
||||
preferences, err = gtsClient.GetUserPreferences()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +112,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
}
|
||||
|
||||
s.printer.PrintAccount(account, relationship, preferences, statuses)
|
||||
s.printer.PrintAccount(account, relationship, preferences, statuses, myAccountID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -132,7 +135,12 @@ func (s *ShowExecutor) showStatus(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
s.printer.PrintStatus(status)
|
||||
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintStatus(status, myAccountID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -181,7 +189,12 @@ func (s *ShowExecutor) showTimeline(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
s.printer.PrintStatusList(timeline)
|
||||
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintStatusList(timeline, myAccountID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -334,7 +347,12 @@ func (s *ShowExecutor) showBookmarks(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
if len(bookmarks.Statuses) > 0 {
|
||||
s.printer.PrintStatusList(bookmarks)
|
||||
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintStatusList(bookmarks, myAccountID)
|
||||
} else {
|
||||
s.printer.PrintInfo("You have no bookmarks.\n")
|
||||
}
|
||||
|
@ -349,7 +367,12 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
if len(liked.Statuses) > 0 {
|
||||
s.printer.PrintStatusList(liked)
|
||||
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintStatusList(liked, myAccountID)
|
||||
} else {
|
||||
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
|
||||
}
|
||||
|
@ -372,21 +395,6 @@ func (s *ShowExecutor) showFollowRequests(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ShowExecutor) showPoll(gtsClient *client.Client) error {
|
||||
if s.pollID == "" {
|
||||
return FlagNotSetError{flagText: flagPollID}
|
||||
}
|
||||
|
||||
poll, err := gtsClient.GetPoll(s.pollID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintPoll(poll)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ShowExecutor) showMutedAccounts(gtsClient *client.Client) error {
|
||||
muted, err := gtsClient.GetMutedAccounts(s.limit)
|
||||
if err != nil {
|
||||
|
|
|
@ -12,6 +12,7 @@ func (p Printer) PrintAccount(
|
|||
relationship *model.AccountRelationship,
|
||||
preferences *model.Preferences,
|
||||
statuses *model.StatusList,
|
||||
userAccountID string,
|
||||
) {
|
||||
var builder strings.Builder
|
||||
|
||||
|
@ -47,7 +48,7 @@ func (p Printer) PrintAccount(
|
|||
}
|
||||
|
||||
if statuses != nil {
|
||||
builder.WriteString("\n\n" + p.statusList(*statuses))
|
||||
builder.WriteString("\n\n" + p.statusList(*statuses, userAccountID))
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n")
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
package printer
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
func (p Printer) PrintPoll(poll model.Poll) {
|
||||
var builder strings.Builder
|
||||
|
||||
builder.WriteString("\n" + p.headerFormat("POLL ID:"))
|
||||
builder.WriteString("\n" + poll.ID)
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("OPTIONS:"))
|
||||
builder.WriteString(p.pollOptions(poll))
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("MULTIPLE CHOICES ALLOWED:"))
|
||||
builder.WriteString("\n" + strconv.FormatBool(poll.Multiple))
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("YOU VOTED:"))
|
||||
builder.WriteString("\n" + strconv.FormatBool(poll.Voted))
|
||||
|
||||
if len(poll.OwnVotes) > 0 {
|
||||
builder.WriteString("\n\n" + p.headerFormat("YOUR VOTES:"))
|
||||
|
||||
for _, vote := range poll.OwnVotes {
|
||||
builder.WriteString("\n" + "[" + strconv.Itoa(vote) + "] " + poll.Options[vote].Title)
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("EXPIRED:"))
|
||||
builder.WriteString("\n" + strconv.FormatBool(poll.Expired))
|
||||
builder.WriteString("\n\n")
|
||||
|
||||
p.print(builder.String())
|
||||
}
|
||||
|
||||
func (p Printer) pollOptions(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:") + " " + p.formatDateTime(poll.ExpiredAt))
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (p Printer) pollMeter(votage float64) string {
|
||||
numVoteBlocks := int(math.Floor(float64(p.lineWrapCharacterLimit) * votage))
|
||||
numBackgroundBlocks := p.lineWrapCharacterLimit - 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(symbolPollMeter, numVoteBlocks) + p.theme.reset
|
||||
|
||||
if !p.noColor {
|
||||
meter += backgroundBlockColor + strings.Repeat(symbolPollMeter, numBackgroundBlocks) + p.theme.reset
|
||||
}
|
||||
|
||||
return meter
|
||||
}
|
|
@ -13,7 +13,7 @@ const (
|
|||
noMediaDescription = "This media attachment has no description."
|
||||
symbolBullet = "\u2022"
|
||||
symbolPollMeter = "\u2501"
|
||||
symbolSuccess = "\u2714"
|
||||
symbolCheckMark = "\u2714"
|
||||
symbolFailure = "\u2717"
|
||||
symbolImage = "\uf03e"
|
||||
symbolLiked = "\uf51f"
|
||||
|
@ -78,9 +78,9 @@ func NewPrinter(
|
|||
}
|
||||
|
||||
func (p Printer) PrintSuccess(text string) {
|
||||
success := p.theme.boldgreen + symbolSuccess + p.theme.reset
|
||||
success := p.theme.boldgreen + symbolCheckMark + p.theme.reset
|
||||
if p.noColor {
|
||||
success = symbolSuccess
|
||||
success = symbolCheckMark
|
||||
}
|
||||
|
||||
printToStdout(success + " " + text + "\n")
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package printer
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
func (p Printer) PrintStatus(status model.Status) {
|
||||
func (p Printer) PrintStatus(status model.Status, userAccountID string) {
|
||||
var builder strings.Builder
|
||||
|
||||
// The account information
|
||||
|
@ -41,7 +42,13 @@ func (p Printer) PrintStatus(status model.Status) {
|
|||
|
||||
// If a poll exists in a status, write the contents to the builder.
|
||||
if status.Poll != nil {
|
||||
builder.WriteString(p.pollOptions(*status.Poll))
|
||||
pollOwner := false
|
||||
if status.Account.ID == userAccountID {
|
||||
pollOwner = true
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("POLL DETAILS:"))
|
||||
builder.WriteString(p.pollDetails(*status.Poll, pollOwner))
|
||||
}
|
||||
|
||||
// Status creation time
|
||||
|
@ -72,17 +79,18 @@ func (p Printer) PrintStatus(status model.Status) {
|
|||
p.print(builder.String())
|
||||
}
|
||||
|
||||
func (p Printer) PrintStatusList(list model.StatusList) {
|
||||
p.print(p.statusList(list))
|
||||
func (p Printer) PrintStatusList(list model.StatusList, userAccountID string) {
|
||||
p.print(p.statusList(list, userAccountID))
|
||||
}
|
||||
|
||||
func (p Printer) statusList(list model.StatusList) string {
|
||||
func (p Printer) statusList(list model.StatusList, userAccountID string) string {
|
||||
var builder strings.Builder
|
||||
|
||||
builder.WriteString(p.headerFormat(list.Name) + "\n")
|
||||
|
||||
for _, status := range list.Statuses {
|
||||
statusID := status.ID
|
||||
statusOwnerID := status.Account.ID
|
||||
createdAt := p.formatDateTime(status.CreatedAt)
|
||||
boostedAt := ""
|
||||
content := status.Content
|
||||
|
@ -100,6 +108,7 @@ func (p Printer) statusList(list model.StatusList) string {
|
|||
))
|
||||
|
||||
statusID = status.Reblog.ID
|
||||
statusOwnerID = status.Reblog.Account.ID
|
||||
createdAt = p.formatDateTime(status.Reblog.CreatedAt)
|
||||
boostedAt = p.formatDateTime(status.CreatedAt)
|
||||
content = status.Reblog.Content
|
||||
|
@ -120,7 +129,12 @@ func (p Printer) statusList(list model.StatusList) string {
|
|||
builder.WriteString("\n" + p.convertHTMLToText(content, true))
|
||||
|
||||
if poll != nil {
|
||||
builder.WriteString(p.pollOptions(*poll))
|
||||
pollOwner := false
|
||||
if statusOwnerID == userAccountID {
|
||||
pollOwner = true
|
||||
}
|
||||
|
||||
builder.WriteString(p.pollDetails(*poll, pollOwner))
|
||||
}
|
||||
|
||||
for _, media := range mediaAttachments {
|
||||
|
@ -172,3 +186,78 @@ func (p Printer) statusList(list model.StatusList) string {
|
|||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (p Printer) pollDetails(poll model.Poll, owner bool) string {
|
||||
var builder strings.Builder
|
||||
|
||||
for ind, option := range poll.Options {
|
||||
var (
|
||||
votage float64
|
||||
percentage int
|
||||
)
|
||||
|
||||
// Show the poll results under any of the following conditions:
|
||||
// - the user is the owner of the poll
|
||||
// - the poll has expired
|
||||
// - the user has voted in the poll
|
||||
if owner || poll.Expired || poll.Voted {
|
||||
if poll.VotesCount == 0 {
|
||||
percentage = 0
|
||||
} else {
|
||||
votage = float64(option.VotesCount) / float64(poll.VotesCount)
|
||||
percentage = int(math.Floor(100 * votage))
|
||||
}
|
||||
|
||||
optionTitle := "\n\n" + "[" + strconv.Itoa(ind) + "] " + option.Title
|
||||
|
||||
for _, vote := range poll.OwnVotes {
|
||||
if ind == vote {
|
||||
optionTitle += " " + symbolCheckMark
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString(optionTitle)
|
||||
builder.WriteString(p.pollMeter(votage))
|
||||
builder.WriteString("\n" + strconv.Itoa(option.VotesCount) + " votes " + "(" + strconv.Itoa(percentage) + "%)")
|
||||
} else {
|
||||
builder.WriteString("\n" + "[" + strconv.Itoa(ind) + "] " + option.Title)
|
||||
}
|
||||
}
|
||||
|
||||
pollStatusField := "Poll is open until: "
|
||||
if poll.Expired {
|
||||
pollStatusField = "Poll was closed on: "
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n" + p.fieldFormat(pollStatusField) + p.formatDateTime(poll.ExpiredAt))
|
||||
builder.WriteString("\n" + p.fieldFormat("Total votes: ") + strconv.Itoa(poll.VotesCount))
|
||||
builder.WriteString("\n" + p.fieldFormat("Multiple choices allowed: ") + strconv.FormatBool(poll.Multiple))
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (p Printer) pollMeter(votage float64) string {
|
||||
numVoteBlocks := int(math.Floor(float64(p.lineWrapCharacterLimit) * votage))
|
||||
numBackgroundBlocks := p.lineWrapCharacterLimit - numVoteBlocks
|
||||
|
||||
voteBlockColour := p.theme.boldgreen
|
||||
backgroundBlockColor := p.theme.grey
|
||||
|
||||
if p.noColor {
|
||||
voteBlockColour = p.theme.reset
|
||||
|
||||
if numVoteBlocks == 0 {
|
||||
numVoteBlocks = 1
|
||||
}
|
||||
}
|
||||
|
||||
meter := "\n" + voteBlockColour + strings.Repeat(symbolPollMeter, numVoteBlocks) + p.theme.reset
|
||||
|
||||
if !p.noColor {
|
||||
meter += backgroundBlockColor + strings.Repeat(symbolPollMeter, numBackgroundBlocks) + p.theme.reset
|
||||
}
|
||||
|
||||
return meter
|
||||
}
|
||||
|
|
|
@ -136,10 +136,6 @@
|
|||
"type": "bool",
|
||||
"description": "Set to true to hide the vote count until the poll is closed"
|
||||
},
|
||||
"poll-id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the poll"
|
||||
},
|
||||
"poll-option": {
|
||||
"type": "StringSliceValue",
|
||||
"description": "A poll option. Use this multiple times to set multiple options"
|
||||
|
@ -215,7 +211,6 @@
|
|||
{ "flag": "account-name", "fieldName": "accountNames" },
|
||||
{ "flag": "content", "default": "" },
|
||||
{ "flag": "list-id", "fieldName": "listID", "default": "" },
|
||||
{ "flag": "poll-id", "fieldName": "pollID", "default": "" },
|
||||
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
|
||||
{ "flag": "to", "fieldName": "toResourceType", "default": "" },
|
||||
{ "flag": "type", "fieldName": "resourceType", "default": "" },
|
||||
|
@ -367,7 +362,6 @@
|
|||
{ "flag": "only-media", "default": "false" },
|
||||
{ "flag": "only-pinned", "default": "false" },
|
||||
{ "flag": "only-public", "default": "false" },
|
||||
{ "flag": "poll-id", "fieldName": "pollID", "default": "" },
|
||||
{ "flag": "show-preferences", "fieldName": "showUserPreferences", "default": "false" },
|
||||
{ "flag": "show-statuses", "default": "false" },
|
||||
{ "flag": "skip-relationship", "fieldName": "skipAccountRelationship", "default": "false" },
|
||||
|
|
Loading…
Reference in a new issue