mirror of
https://github.com/mastodon/mastodon
synced 2024-12-01 00:08:21 +09:00
f17fc5742e
When a poll is edited, we reset the poll and remove all previous votes. However, prior to this commit, the voter count on the poll was not reset. This leads to incorrect percentages being shown in poll results. Fixes #21696
123 lines
3.2 KiB
Ruby
123 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
# == Schema Information
|
|
#
|
|
# Table name: polls
|
|
#
|
|
# id :bigint(8) not null, primary key
|
|
# account_id :bigint(8)
|
|
# status_id :bigint(8)
|
|
# expires_at :datetime
|
|
# options :string default([]), not null, is an Array
|
|
# cached_tallies :bigint(8) default([]), not null, is an Array
|
|
# multiple :boolean default(FALSE), not null
|
|
# hide_totals :boolean default(FALSE), not null
|
|
# votes_count :bigint(8) default(0), not null
|
|
# last_fetched_at :datetime
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# lock_version :integer default(0), not null
|
|
# voters_count :bigint(8)
|
|
#
|
|
|
|
class Poll < ApplicationRecord
|
|
include Expireable
|
|
|
|
belongs_to :account
|
|
belongs_to :status
|
|
|
|
has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all
|
|
has_many :voters, -> { group('accounts.id') }, through: :votes, class_name: 'Account', source: :account
|
|
|
|
has_many :notifications, as: :activity, dependent: :destroy
|
|
|
|
validates :options, presence: true
|
|
validates :expires_at, presence: true, if: :local?
|
|
validates_with PollValidator, on: :create, if: :local?
|
|
|
|
scope :attached, -> { where.not(status_id: nil) }
|
|
scope :unattached, -> { where(status_id: nil) }
|
|
|
|
before_validation :prepare_options, if: :local?
|
|
before_validation :prepare_votes_count
|
|
before_validation :prepare_cached_tallies
|
|
|
|
after_commit :reset_parent_cache, on: :update
|
|
|
|
def loaded_options
|
|
options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? (cached_tallies[key] || 0) : nil) }
|
|
end
|
|
|
|
def possibly_stale?
|
|
remote? && last_fetched_before_expiration? && time_passed_since_last_fetch?
|
|
end
|
|
|
|
def voted?(account)
|
|
account.id == account_id || votes.where(account: account).exists?
|
|
end
|
|
|
|
def own_votes(account)
|
|
votes.where(account: account).pluck(:choice)
|
|
end
|
|
|
|
delegate :local?, to: :account
|
|
|
|
def remote?
|
|
!local?
|
|
end
|
|
|
|
def emojis
|
|
@emojis ||= CustomEmoji.from_text(options.join(' '), account.domain)
|
|
end
|
|
|
|
class Option < ActiveModelSerializers::Model
|
|
attributes :id, :title, :votes_count, :poll
|
|
|
|
def initialize(poll, id, title, votes_count)
|
|
super(
|
|
poll: poll,
|
|
id: id,
|
|
title: title,
|
|
votes_count: votes_count,
|
|
)
|
|
end
|
|
end
|
|
|
|
def reset_votes!
|
|
self.cached_tallies = options.map { 0 }
|
|
self.votes_count = 0
|
|
self.voters_count = 0
|
|
votes.delete_all unless new_record?
|
|
end
|
|
|
|
private
|
|
|
|
def prepare_cached_tallies
|
|
self.cached_tallies = options.map { 0 } if cached_tallies.empty?
|
|
end
|
|
|
|
def prepare_votes_count
|
|
self.votes_count = cached_tallies.sum unless cached_tallies.empty?
|
|
end
|
|
|
|
def prepare_options
|
|
self.options = options.map(&:strip).reject(&:blank?)
|
|
end
|
|
|
|
def reset_parent_cache
|
|
return if status_id.nil?
|
|
Rails.cache.delete("statuses/#{status_id}")
|
|
end
|
|
|
|
def last_fetched_before_expiration?
|
|
last_fetched_at.nil? || expires_at.nil? || last_fetched_at < expires_at
|
|
end
|
|
|
|
def time_passed_since_last_fetch?
|
|
last_fetched_at.nil? || last_fetched_at < 1.minute.ago
|
|
end
|
|
|
|
def show_totals_now?
|
|
expired? || !hide_totals?
|
|
end
|
|
end
|