mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-02 09:31:40 +08:00
When a post in a post-voting topic had both upvotes and downvotes,
clicking the vote count to view the voter list could display a "and -N
more users..." label with a negative number.
The root cause was that `calcRemainingCount` used the post's
`post_voting_vote_count` (the net score, i.e. upvotes − downvotes)
instead of the actual total number of voters. With 2 upvotes and 3
downvotes, net score is −1, minus 5 displayed voters = −6. Since −6 is
truthy in JS, the label would render.
The fix adds `total_voters_count` to the `/post_voting/voters` JSON
response so the client has a reliable total to subtract from. The
component now stores this value and uses it in `calcRemainingCount` with
a `Math.max(0, ...)` guard.
Also removes a no-op `{{#if whoVoted}}` template guard that referenced
the imported function (always truthy) instead of a result, and fixes the
voter popup z-index so it renders above subsequent posts.
Ref - t/181822
180 lines
5 KiB
Ruby
180 lines
5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module PostVoting
|
|
class VotesController < ::ApplicationController
|
|
requires_plugin PLUGIN_NAME
|
|
|
|
before_action :ensure_logged_in
|
|
before_action :find_vote_post, only: %i[create destroy voters]
|
|
before_action :ensure_can_see_post, only: %i[create destroy voters]
|
|
before_action :ensure_post_voting_enabled, only: %i[create destroy]
|
|
|
|
def create
|
|
ensure_can_vote(@post)
|
|
|
|
if PostVoting::VoteManager.vote(@post, current_user, direction: vote_params[:direction])
|
|
render json: success_json
|
|
else
|
|
render json: failed_json, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def create_comment_vote
|
|
comment = find_comment
|
|
ensure_can_see_comment!(comment)
|
|
ensure_can_vote(comment)
|
|
|
|
if PostVoting::VoteManager.vote(
|
|
comment,
|
|
current_user,
|
|
direction: PostVotingVote.directions[:up],
|
|
)
|
|
render json: success_json
|
|
else
|
|
render json: failed_json, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
if !Topic.post_voting_votes(@post.topic, current_user).exists?
|
|
raise Discourse::InvalidAccess.new(
|
|
nil,
|
|
nil,
|
|
custom_message: "vote.error.user_has_not_voted",
|
|
)
|
|
end
|
|
|
|
if !PostVoting::VoteManager.can_undo(@post, current_user)
|
|
msg =
|
|
I18n.t(
|
|
"vote.error.undo_vote_action_window",
|
|
count: SiteSetting.post_voting_undo_vote_action_window.to_i,
|
|
)
|
|
|
|
render_json_error(msg, status: 403)
|
|
|
|
return
|
|
end
|
|
|
|
if PostVoting::VoteManager.remove_vote(@post, current_user)
|
|
render json: success_json
|
|
else
|
|
render json: failed_json, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def destroy_comment_vote
|
|
comment = find_comment
|
|
ensure_can_see_comment!(comment)
|
|
|
|
if !PostVotingVote.exists?(votable: comment, user: current_user)
|
|
raise Discourse::InvalidAccess.new(
|
|
nil,
|
|
nil,
|
|
custom_message: "vote.error.user_has_not_voted",
|
|
)
|
|
end
|
|
|
|
if PostVoting::VoteManager.remove_vote(comment, current_user)
|
|
render json: success_json
|
|
else
|
|
render json: failed_json, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
VOTERS_LIMIT = 20
|
|
|
|
def voters
|
|
# TODO: Probably a site setting to hide/show voters
|
|
vote_scope = PostVotingVote.where(votable_id: @post.id, votable_type: "Post")
|
|
|
|
voters =
|
|
User
|
|
.joins(:post_voting_votes)
|
|
.merge(vote_scope)
|
|
.order("post_voting_votes.created_at DESC")
|
|
.select("users.*", "post_voting_votes.direction")
|
|
.limit(VOTERS_LIMIT)
|
|
|
|
render_json_dump(
|
|
voters: serialize_data(voters, BasicVoterSerializer),
|
|
total_voters_count: vote_scope.count,
|
|
)
|
|
end
|
|
|
|
private
|
|
|
|
def vote_params
|
|
params.permit(:post_id, :comment_id, :direction)
|
|
end
|
|
|
|
def find_vote_post
|
|
if params[:vote].present?
|
|
post_id = vote_params[:post_id]
|
|
else
|
|
params.require(:post_id)
|
|
post_id = params[:post_id]
|
|
end
|
|
|
|
@post = Post.find_by(id: post_id)
|
|
|
|
raise Discourse::NotFound unless @post
|
|
end
|
|
|
|
def ensure_can_see_post
|
|
@guardian.ensure_can_see!(@post)
|
|
end
|
|
|
|
def ensure_post_voting_enabled
|
|
raise Discourse::InvalidAccess.new unless @post.is_post_voting_topic?
|
|
end
|
|
|
|
def find_comment
|
|
comment = PostVotingComment.find_by(id: vote_params[:comment_id])
|
|
raise Discourse::NotFound if comment.blank?
|
|
comment
|
|
end
|
|
|
|
def ensure_can_see_comment!(comment)
|
|
@guardian.ensure_can_see!(comment.post)
|
|
end
|
|
|
|
def ensure_can_vote(votable)
|
|
if votable.user_id == current_user.id
|
|
raise_error("post.post_voting.errors.self_voting_not_permitted")
|
|
end
|
|
|
|
if votable.class.name == "Post"
|
|
raise_error("post.post_voting.errors.vote_archived_topic") if votable.topic.archived?
|
|
|
|
raise_error("post.post_voting.errors.vote_closed_topic") if votable.topic.closed?
|
|
|
|
direction = vote_params[:direction] || PostVotingVote.directions[:up]
|
|
if PostVotingVote.exists?(votable: votable, user_id: current_user.id, direction: direction)
|
|
raise_error("vote.error.one_vote_per_post")
|
|
elsif !PostVoting::VoteManager.can_undo(votable, current_user)
|
|
raise_error(
|
|
"vote.error.undo_vote_action_window",
|
|
{ count: SiteSetting.post_voting_undo_vote_action_window.to_i },
|
|
)
|
|
end
|
|
|
|
if votable.class.name == "PostVotingComment" &&
|
|
PostVotingVote.exists?(votable: votable, user: current_user)
|
|
raise_error("vote.error.one_vote_per_comment")
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def raise_error(error_message, error_message_params = nil)
|
|
raise Discourse::InvalidAccess.new(
|
|
nil,
|
|
nil,
|
|
custom_message: error_message,
|
|
custom_message_params: error_message_params,
|
|
)
|
|
end
|
|
end
|
|
end
|