diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 8bda045017..e416287a54 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -2020,14 +2020,21 @@ definitions: type: array x-go-name: Options own_votes: - description: When called with a user token, which options has the authorized user chosen? Contains an array of index values for options. + description: |- + When called with a user token, which options has the authorized + user chosen? Contains an array of index values for options. + + Omitted when no user token provided. items: format: int64 type: integer type: array x-go-name: OwnVotes voted: - description: When called with a user token, has the authorized user voted? + description: |- + When called with a user token, has the authorized user voted? + + Omitted when no user token provided. type: boolean x-go-name: Voted voters_count: @@ -2059,6 +2066,36 @@ definitions: type: object x-go-name: PollOption x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model + pollRequest: + properties: + expires_in: + description: |- + Duration the poll should be open, in seconds. + If provided, media_ids cannot be used, and poll[options] must be provided. + format: int64 + type: integer + x-go-name: ExpiresIn + hide_totals: + description: Hide vote counts until the poll ends. + type: boolean + x-go-name: HideTotals + multiple: + description: Allow multiple choices on this poll. + type: boolean + x-go-name: Multiple + options: + description: |- + Array of possible answers. + If provided, media_ids cannot be used, and poll[expires_in] must be provided. + name: poll[options] + items: + type: string + type: array + x-go-name: Options + title: PollRequest models a request to create a poll. + type: object + x-go-name: PollRequest + x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model report: properties: action_taken: @@ -2346,6 +2383,8 @@ definitions: type: string type: array x-go-name: MediaIDs + poll: + $ref: '#/definitions/pollRequest' scheduled_at: description: |- ISO 8601 Datetime at which to schedule a status. @@ -6028,13 +6067,20 @@ paths: - polls /api/v1/polls/{id}/vote: post: - operationId: poll + operationId: pollVote parameters: - description: Target poll ID. in: path name: id required: true type: string + - description: Poll choice indices on which to vote. + in: formData + items: + type: integer + name: choices + required: true + type: array produces: - application/json responses: @@ -6309,6 +6355,11 @@ paths: name: media_ids type: array x-go-name: MediaIDs + - $ref: '#/definitions/pollRequest' + description: Poll to include with this status. + in: formData + name: poll + x-go-name: Poll - description: ID of the status being replied to, if status is a reply. in: formData name: in_reply_to_id diff --git a/internal/api/client/polls/polls_vote.go b/internal/api/client/polls/polls_vote.go index 8773d06060..824ea08ef6 100644 --- a/internal/api/client/polls/polls_vote.go +++ b/internal/api/client/polls/polls_vote.go @@ -27,7 +27,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) -// PollVotePOSTHandler swagger:operation POST /api/v1/polls/{id}/vote poll +// PollVotePOSTHandler swagger:operation POST /api/v1/polls/{id}/vote pollVote // // Vote with choices in the given poll. // @@ -45,6 +45,14 @@ import ( // description: Target poll ID. // in: path // required: true +// - +// name: choices +// type: array +// items: +// type: integer +// description: Poll choice indices on which to vote. +// in: formData +// required: true // // security: // - OAuth2 Bearer: diff --git a/internal/api/model/poll.go b/internal/api/model/poll.go index ca479d1177..c1d2ca89e9 100644 --- a/internal/api/model/poll.go +++ b/internal/api/model/poll.go @@ -41,10 +41,15 @@ type Poll struct { VotersCount int `json:"voters_count"` // When called with a user token, has the authorized user voted? - Voted bool `json:"voted,omitempty"` + // + // Omitted when no user token provided. + Voted *bool `json:"voted,omitempty"` - // When called with a user token, which options has the authorized user chosen? Contains an array of index values for options. - OwnVotes []int `json:"own_votes,omitempty"` + // When called with a user token, which options has the authorized + // user chosen? Contains an array of index values for options. + // + // Omitted when no user token provided. + OwnVotes *[]int `json:"own_votes,omitempty"` // Possible answers for the poll. Options []PollOption `json:"options"` @@ -66,7 +71,7 @@ type PollOption struct { // PollRequest models a request to create a poll. // -// swagger:parameters createStatus +// swagger:model pollRequest type PollRequest struct { // Array of possible answers. // If provided, media_ids cannot be used, and poll[expires_in] must be provided. @@ -86,7 +91,7 @@ type PollRequest struct { // PollVoteRequest models a request to vote in a poll. // -// swagger:parameters pollVote +// swagger:ignore type PollVoteRequest struct { // Choices contains poll vote choice indices. Note that form // uses a different key than the JSON, i.e. the '[]' suffix. diff --git a/internal/api/model/status.go b/internal/api/model/status.go index 87db77e67a..a6c7f43a48 100644 --- a/internal/api/model/status.go +++ b/internal/api/model/status.go @@ -155,7 +155,7 @@ type StatusCreateRequest struct { // in: formData MediaIDs []string `form:"media_ids[]" json:"media_ids" xml:"media_ids"` // Poll to include with this status. - // swagger:ignore + // in: formData Poll *PollRequest `form:"poll" json:"poll" xml:"poll"` // ID of the status being replied to, if status is a reply. // in: formData diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 0a79defc73..d5a1dee327 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -1313,8 +1313,10 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou options []apimodel.PollOption totalVotes int totalVoters int - ownChoices []int + voted *bool + ownChoices *[]int isAuthor bool + emojis []apimodel.Emoji ) // Preallocate a slice of frontend model poll choices. @@ -1337,19 +1339,26 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou if vote != nil { // Set choices by requester. - ownChoices = vote.Choices + ownChoices = &vote.Choices // Update default totals in the // case that counts are hidden. totalVotes = len(vote.Choices) totalVoters = 1 - for _, choice := range ownChoices { + for _, choice := range *ownChoices { options[choice].VotesCount++ } + } else { + // Requester is defined but hasn't made + // a choice. Init slice to serialize as `[]`. + ownChoices = util.Ptr(make([]int, 0)) } // Check if requester is author of source status. isAuthor = (requester.ID == poll.Status.AccountID) + + // Requester is defined so voted should be defined too. + voted = util.Ptr((isAuthor || len(*ownChoices) > 0)) } if isAuthor || !*poll.HideCounts { @@ -1368,6 +1377,11 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou } } + // TODO: emojis used in poll options. + // For now init to empty slice to serialize as `[]`. + // In future inherit from parent status. + emojis = make([]apimodel.Emoji, 0) + return &apimodel.Poll{ ID: poll.ID, ExpiresAt: util.FormatISO8601(poll.ExpiresAt), @@ -1375,9 +1389,10 @@ func (c *Converter) PollToAPIPoll(ctx context.Context, requester *gtsmodel.Accou Multiple: (*poll.Multiple), VotesCount: totalVotes, VotersCount: totalVoters, - Voted: (isAuthor || len(ownChoices) > 0), + Voted: voted, OwnVotes: ownChoices, Options: options, + Emojis: emojis, }, nil }