mirror of
https://codeberg.org/superseriousbusiness/gotosocial.git
synced 2024-12-25 10:28:18 +03:00
[feature] unending polls (#3592)
* adds support for unending polls to be created locally * remove unused argument
This commit is contained in:
parent
79f2e85f51
commit
3e18d97a6e
3 changed files with 53 additions and 30 deletions
|
@ -88,12 +88,15 @@ func (p *pollDB) getPoll(ctx context.Context, lookup string, dbQuery func(*gtsmo
|
||||||
func (p *pollDB) GetOpenPolls(ctx context.Context) ([]*gtsmodel.Poll, error) {
|
func (p *pollDB) GetOpenPolls(ctx context.Context) ([]*gtsmodel.Poll, error) {
|
||||||
var pollIDs []string
|
var pollIDs []string
|
||||||
|
|
||||||
// Select all polls with unset `closed_at` time.
|
// Select all polls with:
|
||||||
|
// - UNSET `closed_at`
|
||||||
|
// - SET `expires_at`
|
||||||
if err := p.db.NewSelect().
|
if err := p.db.NewSelect().
|
||||||
Table("polls").
|
Table("polls").
|
||||||
Column("polls.id").
|
Column("polls.id").
|
||||||
Join("JOIN ? ON ? = ?", bun.Ident("statuses"), bun.Ident("polls.id"), bun.Ident("statuses.poll_id")).
|
Join("JOIN ? ON ? = ?", bun.Ident("statuses"), bun.Ident("polls.id"), bun.Ident("statuses.poll_id")).
|
||||||
Where("? = true", bun.Ident("statuses.local")).
|
Where("? = true", bun.Ident("statuses.local")).
|
||||||
|
Where("? IS NOT NULL", bun.Ident("polls.expires_at")).
|
||||||
Where("? IS NULL", bun.Ident("polls.closed_at")).
|
Where("? IS NULL", bun.Ident("polls.closed_at")).
|
||||||
Scan(ctx, &pollIDs); err != nil {
|
Scan(ctx, &pollIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -85,25 +85,8 @@ func (p *Processor) Create(
|
||||||
PendingApproval: util.Ptr(false),
|
PendingApproval: util.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Poll != nil {
|
// Process any attached poll.
|
||||||
// Update the status AS type to "Question".
|
p.processPoll(status, form.Poll)
|
||||||
status.ActivityStreamsType = ap.ActivityQuestion
|
|
||||||
|
|
||||||
// Create new poll for status from form.
|
|
||||||
secs := time.Duration(form.Poll.ExpiresIn)
|
|
||||||
status.Poll = >smodel.Poll{
|
|
||||||
ID: id.NewULID(),
|
|
||||||
Multiple: &form.Poll.Multiple,
|
|
||||||
HideCounts: &form.Poll.HideTotals,
|
|
||||||
Options: form.Poll.Options,
|
|
||||||
StatusID: statusID,
|
|
||||||
Status: status,
|
|
||||||
ExpiresAt: now.Add(secs * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set poll ID on the status.
|
|
||||||
status.PollID = status.Poll.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check + attach in-reply-to status.
|
// Check + attach in-reply-to status.
|
||||||
if errWithCode := p.processInReplyTo(ctx,
|
if errWithCode := p.processInReplyTo(ctx,
|
||||||
|
@ -153,6 +136,14 @@ func (p *Processor) Create(
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if status.Poll != nil && !status.Poll.ExpiresAt.IsZero() {
|
||||||
|
// Now that the status is inserted, and side effects queued,
|
||||||
|
// attempt to schedule an expiry handler for the status poll.
|
||||||
|
if err := p.polls.ScheduleExpiry(ctx, status.Poll); err != nil {
|
||||||
|
log.Errorf(ctx, "error scheduling poll expiry: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// send it back to the client API worker for async side-effects.
|
// send it back to the client API worker for async side-effects.
|
||||||
p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
|
p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{
|
||||||
APObjectType: ap.ObjectNote,
|
APObjectType: ap.ObjectNote,
|
||||||
|
@ -161,14 +152,6 @@ func (p *Processor) Create(
|
||||||
Origin: requester,
|
Origin: requester,
|
||||||
})
|
})
|
||||||
|
|
||||||
if status.Poll != nil {
|
|
||||||
// Now that the status is inserted, and side effects queued,
|
|
||||||
// attempt to schedule an expiry handler for the status poll.
|
|
||||||
if err := p.polls.ScheduleExpiry(ctx, status.Poll); err != nil {
|
|
||||||
log.Errorf(ctx, "error scheduling poll expiry: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the new status replies to a status that
|
// If the new status replies to a status that
|
||||||
// replies to us, use our reply as an implicit
|
// replies to us, use our reply as an implicit
|
||||||
// accept of any pending interaction.
|
// accept of any pending interaction.
|
||||||
|
@ -189,6 +172,43 @@ func (p *Processor) Create(
|
||||||
return p.c.GetAPIStatus(ctx, requester, status)
|
return p.c.GetAPIStatus(ctx, requester, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Processor) processPoll(status *gtsmodel.Status, poll *apimodel.PollRequest) {
|
||||||
|
if poll == nil {
|
||||||
|
// No poll set.
|
||||||
|
// Nothing to do.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var expiresAt time.Time
|
||||||
|
|
||||||
|
// Now will have been set
|
||||||
|
// as the status creation.
|
||||||
|
now := status.CreatedAt
|
||||||
|
|
||||||
|
// Update the status AS type to "Question".
|
||||||
|
status.ActivityStreamsType = ap.ActivityQuestion
|
||||||
|
|
||||||
|
// Set an expiry time if one given.
|
||||||
|
if in := poll.ExpiresIn; in > 0 {
|
||||||
|
expiresIn := time.Duration(in)
|
||||||
|
expiresAt = now.Add(expiresIn * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new poll for status.
|
||||||
|
status.Poll = >smodel.Poll{
|
||||||
|
ID: id.NewULID(),
|
||||||
|
Multiple: &poll.Multiple,
|
||||||
|
HideCounts: &poll.HideTotals,
|
||||||
|
Options: poll.Options,
|
||||||
|
StatusID: status.ID,
|
||||||
|
Status: status,
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set poll ID on the status.
|
||||||
|
status.PollID = status.Poll.ID
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Processor) processInReplyTo(ctx context.Context, requester *gtsmodel.Account, status *gtsmodel.Status, inReplyToID string) gtserror.WithCode {
|
func (p *Processor) processInReplyTo(ctx context.Context, requester *gtsmodel.Account, status *gtsmodel.Status, inReplyToID string) gtserror.WithCode {
|
||||||
if inReplyToID == "" {
|
if inReplyToID == "" {
|
||||||
// Not a reply.
|
// Not a reply.
|
||||||
|
|
|
@ -444,7 +444,7 @@ func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (ap.Stat
|
||||||
poll := streams.NewActivityStreamsQuestion()
|
poll := streams.NewActivityStreamsQuestion()
|
||||||
|
|
||||||
// Add required status poll data to AS Question.
|
// Add required status poll data to AS Question.
|
||||||
if err := c.addPollToAS(ctx, s.Poll, poll); err != nil {
|
if err := c.addPollToAS(s.Poll, poll); err != nil {
|
||||||
return nil, gtserror.Newf("error converting poll: %w", err)
|
return nil, gtserror.Newf("error converting poll: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,7 +708,7 @@ func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (ap.Stat
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Converter) addPollToAS(ctx context.Context, poll *gtsmodel.Poll, dst ap.Pollable) error {
|
func (c *Converter) addPollToAS(poll *gtsmodel.Poll, dst ap.Pollable) error {
|
||||||
var optionsProp interface {
|
var optionsProp interface {
|
||||||
// the minimum interface for appending AS Notes
|
// the minimum interface for appending AS Notes
|
||||||
// to an AS type options property of some kind.
|
// to an AS type options property of some kind.
|
||||||
|
|
Loading…
Reference in a new issue