From eb016b96e93ab0563825264dca6473d012220347 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Wed, 14 Aug 2024 11:18:38 +0100 Subject: [PATCH] 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 --- docs/manual.md | 40 ++++++------- internal/client/poll.go | 46 +++++++-------- internal/executor/add.go | 52 ++++++++--------- internal/executor/create.go | 3 +- internal/executor/errors.go | 18 ++++++ internal/executor/executors.go | 4 -- internal/executor/flags.go | 2 - internal/executor/show.go | 58 +++++++++++-------- internal/printer/account.go | 3 +- internal/printer/poll.go | 91 ----------------------------- internal/printer/printer.go | 6 +- internal/printer/status.go | 101 +++++++++++++++++++++++++++++++-- schema/enbas_cli_schema.json | 6 -- 13 files changed, 215 insertions(+), 215 deletions(-) delete mode 100644 internal/printer/poll.go diff --git a/docs/manual.md b/docs/manual.md index 48332f6..95ebaf3 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -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.
Here this should be `vote`. | | +| `to` | string | true | The resource you want to add the vote to.
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.
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.
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.
Here this should be `vote`. | | -| `to` | string | true | The resource you want to add the vote to.
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.
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 diff --git a/internal/client/poll.go b/internal/client/poll.go index 5d6baa5..783167a 100644 --- a/internal/client/poll.go +++ b/internal/client/poll.go @@ -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 { diff --git a/internal/executor/add.go b/internal/executor/add.go index ab89ebd..a66bda1 100644 --- a/internal/executor/add.go +++ b/internal/executor/add.go @@ -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) } diff --git a/internal/executor/create.go b/internal/executor/create.go index 5e0e409..194400a 100644 --- a/internal/executor/create.go +++ b/internal/executor/create.go @@ -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 } diff --git a/internal/executor/errors.go b/internal/executor/errors.go index f32927c..62e49c3 100644 --- a/internal/executor/errors.go +++ b/internal/executor/errors.go @@ -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 } diff --git a/internal/executor/executors.go b/internal/executor/executors.go index 7b53ed2..4ce5cfc 100644 --- a/internal/executor/executors.go +++ b/internal/executor/executors.go @@ -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") diff --git a/internal/executor/flags.go b/internal/executor/flags.go index 37eb7a0..992ccb5 100644 --- a/internal/executor/flags.go +++ b/internal/executor/flags.go @@ -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" ) diff --git a/internal/executor/show.go b/internal/executor/show.go index beecf8b..665386a 100644 --- a/internal/executor/show.go +++ b/internal/executor/show.go @@ -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 { diff --git a/internal/printer/account.go b/internal/printer/account.go index cc101ad..004c649 100644 --- a/internal/printer/account.go +++ b/internal/printer/account.go @@ -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") diff --git a/internal/printer/poll.go b/internal/printer/poll.go deleted file mode 100644 index 16c99fe..0000000 --- a/internal/printer/poll.go +++ /dev/null @@ -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 -} diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 6ac2325..0ec5951 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -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") diff --git a/internal/printer/status.go b/internal/printer/status.go index 38ae7a7..9b847aa 100644 --- a/internal/printer/status.go +++ b/internal/printer/status.go @@ -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 +} diff --git a/schema/enbas_cli_schema.json b/schema/enbas_cli_schema.json index b441830..bbed96d 100644 --- a/schema/enbas_cli_schema.json +++ b/schema/enbas_cli_schema.json @@ -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" },