diff options
author | 2023-11-08 14:32:17 +0000 | |
---|---|---|
committer | 2023-11-08 14:32:17 +0000 | |
commit | e9e5dc5a40926e5320cb131b035c46b1e1b0bd59 (patch) | |
tree | 52edc9fa5742f28e1e5223f51cda628ec1c35a24 /internal/gtsmodel/poll.go | |
parent | [chore]: Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#2338) (diff) | |
download | gotosocial-e9e5dc5a40926e5320cb131b035c46b1e1b0bd59.tar.xz |
[feature] add support for polls + receiving federated status edits (#2330)
Diffstat (limited to 'internal/gtsmodel/poll.go')
-rw-r--r-- | internal/gtsmodel/poll.go | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/internal/gtsmodel/poll.go b/internal/gtsmodel/poll.go new file mode 100644 index 000000000..7e131ebba --- /dev/null +++ b/internal/gtsmodel/poll.go @@ -0,0 +1,121 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package gtsmodel + +import ( + "strings" + "time" +) + +// Poll represents an attached (to) Status poll, i.e. a questionaire. Can be remote / local. +type Poll struct { + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // Unique identity string. + Multiple *bool `bun:",nullzero,notnull,default:false"` // Is this a multiple choice poll? i.e. can you vote on multiple options. + HideCounts *bool `bun:",nullzero,notnull,default:false"` // Hides vote counts until poll ends. + Options []string `bun:",nullzero,notnull"` // The available options for this poll. + Votes []int `bun:",nullzero,notnull"` // Vote counts per choice. + Voters *int `bun:",nullzero,notnull"` // Total no. voters count. + StatusID string `bun:"type:CHAR(26),nullzero,notnull,unique"` // Status ID of which this Poll is attached to. + Status *Status `bun:"-"` // The related Status for StatusID (not always set). + ExpiresAt time.Time `bun:"type:timestamptz,nullzero,notnull"` // The expiry date of this Poll. + ClosedAt time.Time `bun:"type:timestamptz,nullzero"` // The closure date of this poll, will be zerotime until set. + Closing bool `bun:"-"` // An ephemeral field only set on Polls in the middle of closing. + // no creation date, use attached Status.CreatedAt. +} + +// GetChoice returns the option index with name. +func (p *Poll) GetChoice(name string) int { + for i, option := range p.Options { + if strings.EqualFold(option, name) { + return i + } + } + return -1 +} + +// Expired returns whether the Poll is expired (i.e. date is BEFORE now). +func (p *Poll) Expired() bool { + return !p.ExpiresAt.IsZero() && + time.Now().After(p.ExpiresAt) +} + +// Closed returns whether the Poll is closed (i.e. date is set and BEFORE now). +func (p *Poll) Closed() bool { + return !p.ClosedAt.IsZero() && + time.Now().After(p.ClosedAt) +} + +// IncrementVotes increments Poll vote and voter counts for given choices. +func (p *Poll) IncrementVotes(choices []int) { + if len(choices) == 0 { + return + } + p.CheckVotes() + for _, choice := range choices { + p.Votes[choice]++ + } + (*p.Voters)++ +} + +// DecrementVotes decrements Poll vote and voter counts for given choices. +func (p *Poll) DecrementVotes(choices []int) { + if len(choices) == 0 { + return + } + p.CheckVotes() + for _, choice := range choices { + if p.Votes[choice] != 0 { + p.Votes[choice]-- + } + } + if (*p.Voters) != 0 { + (*p.Voters)-- + } +} + +// ResetVotes resets all stored vote counts. +func (p *Poll) ResetVotes() { + p.Votes = make([]int, len(p.Options)) + p.Voters = new(int) +} + +// CheckVotes ensures that the Poll.Votes slice is not nil, +// else initializing an int slice len+cap equal to Poll.Options. +// Note this should not be needed anywhere other than the +// database and the processor. +func (p *Poll) CheckVotes() { + if p.Votes == nil { + p.Votes = make([]int, len(p.Options)) + } + if p.Voters == nil { + p.Voters = new(int) + } +} + +// PollVote represents a single instance of vote(s) in a Poll by an account. +// If the Poll is single-choice, len(.Choices) = 1, if multiple-choice then +// len(.Choices) >= 1. Can be remote or local. +type PollVote struct { + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // Unique identity string. + Choices []int `bun:",nullzero,notnull"` // The Poll's option indices of which these are votes for. + AccountID string `bun:"type:CHAR(26),nullzero,notnull,unique:in_poll_by_account"` // Account ID from which this vote originated. + Account *Account `bun:"-"` // The related Account for AccountID (not always set). + PollID string `bun:"type:CHAR(26),nullzero,notnull,unique:in_poll_by_account"` // Poll ID of which this is a vote in. + Poll *Poll `bun:"-"` // The related Poll for PollID (not always set). + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // The creation date of this PollVote. +} |