docbrown/app/models/reaction.rb
Ben Halpern 749d61a5c1
Remove fetch polyfill and other misc small fixes (#629)
* Remove fetch polyfill and other misc small fixes

* Remove fetch polyfill and other misc small fixes
2018-07-27 10:13:38 -07:00

143 lines
4.3 KiB
Ruby

class Reaction < ApplicationRecord
belongs_to :reactable, polymorphic: true
counter_culture :reactable,
column_name: proc { |model|
model.points.positive? ? "positive_reactions_count" : "reactions_count"
}
counter_culture :user
belongs_to :user
validates :category, inclusion: { in: %w(like thinking hands unicorn thumbsdown vomit readinglist) }
validates :reactable_type, inclusion: { in: %w(Comment Article) }
validates :user_id, uniqueness: {:scope => [:reactable_id, :reactable_type, :category]}
validate :permissions
before_save :assign_points
after_save :update_reactable
before_destroy :update_reactable_without_delay
after_save :touch_user
after_save :async_bust
before_destroy :clean_up_before_destroy
include StreamRails::Activity
as_activity
def self.count_for_reactable(reactable)
Rails.cache.fetch("count_for_reactable-#{reactable.class.name}-#{reactable.id}", expires_in: 1.hour) do
[{category:"like",count:Reaction.where(reactable_id:reactable.id, reactable_type: reactable.class.name, category: "like").size},
{category:"readinglist",count:Reaction.where(reactable_id:reactable.id, reactable_type: reactable.class.name, category: "readinglist").size},
{category:"unicorn",count:Reaction.where(reactable_id:reactable.id, reactable_type: reactable.class.name, category: "unicorn").size}]
end
end
def self.for_display(user)
self.includes(:reactable).
where(reactable_type: "Article", user_id: user.id).
where("created_at > ?", 5.days.ago).
select("distinct on (reactable_id) *").
take(15)
end
# notifications
def activity_object
self
end
def activity_target
"#{reactable_type}_#{reactable_id}"
end
def activity_notify
return if user_id == reactable.user_id
return if points.negative?
[StreamNotifier.new(reactable.user.id).notify]
end
def remove_from_feed
super
User.find_by(id: reactable.user.id)&.touch(:last_notification_activity)
end
def self.cached_any_reactions_for?(reactable, user, category)
Rails.cache.fetch("any_reactions_for-#{reactable.class.name}-#{reactable.id}-#{user.updated_at}-#{category}", expires_in: 24.hours) do
Reaction.
where(reactable_id: reactable.id, reactable_type: reactable.class.name, user_id: user.id, category: category).
any?
end
end
private
def update_reactable
cache_buster = CacheBuster.new
if reactable_type == "Article"
reactable.async_score_calc
reactable.index!
cache_buster.bust "/reactions?article_id=#{reactable_id}"
elsif reactable_type == "Comment"
reactable.save
cache_buster.bust "/reactions?commentable_id=#{reactable.commentable_id}&commentable_type=#{reactable.commentable_type}"
end
cache_buster.bust user.path
occasionally_sync_reaction_counts
end
handle_asynchronously :update_reactable
def touch_user
user.touch
end
handle_asynchronously :touch_user
def async_bust
featured_articles = Article.where(featured: true).order('hotness_score DESC').limit(3).pluck(:id)
if featured_articles.include?(reactable.id)
reactable.touch
cache_buster = CacheBuster.new
cache_buster.bust "/"
cache_buster.bust "/"
cache_buster.bust "/?i=i"
cache_buster.bust "?i=i"
end
end
handle_asynchronously :async_bust
def clean_up_before_destroy
reactable.index! if reactable_type == "Article"
end
BASE_POINTS = {
"vomit" => -50.0,
"thumbsdown" => -10.0
}.freeze
def assign_points
base_points = BASE_POINTS.fetch(category, 1.0)
self.points = user ? (base_points * user.reputation_modifier) : -5
end
def permissions
if negative_reaction_from_untrusted_user?
errors.add(:category, "is not valid.")
end
if reactable_type == "Article" && !reactable.published
errors.add(:reactable_id, "is not valid.")
end
end
def occasionally_sync_reaction_counts
# Fixes any out-of-sync positive_reactions_count
if rand(6) == 1 || reactable.positive_reactions_count.negative?
reactable.update_column(:positive_reactions_count, reactable.reactions.where("points > ?", 0).size)
end
end
def negative_reaction_from_untrusted_user?
negative? && !user.trusted
end
def negative?
category == "vomit" || category == "thumbsdown"
end
end