posts: make post votes work the same way as comment votes.

Change post votes to work the same way as comment votes:

* Make the upvote arrow blue if you've upvoted the post, or grey if you
  haven't. Likewise for the downvote arrow.
* Make it so you can click the upvote or downvote arrows to undo the vote.
* Don't show any notices when you vote on a post.

Also fix it so that votes work the same way on the posts page, the
comments page, and in the modqueue. Before it wasn't possible to undo
votes on the comments page or in the modqueue.
This commit is contained in:
evazion
2021-01-28 17:34:42 -06:00
parent 0a8fe506b4
commit ffdd5e6128
13 changed files with 135 additions and 42 deletions

View File

@@ -1,4 +1,8 @@
class ApplicationComponent < ViewComponent::Base class ApplicationComponent < ViewComponent::Base
delegate :link_to_user, :time_ago_in_words_tagged, :format_text, :edit_icon,
:delete_icon, :undelete_icon, :flag_icon, :upvote_icon, :downvote_icon,
:link_icon, to: :helpers
def policy(subject) def policy(subject)
Pundit.policy!(current_user, subject) Pundit.policy!(current_user, subject)
end end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
# This component represents the post score and upvote/downvote buttons ("🠉 5 🠋")
class PostVotesComponent < ApplicationComponent
attr_reader :post, :current_user
def initialize(post:, current_user:)
@post = post
@current_user = current_user
end
def can_vote?
policy(PostVote).create?
end
def current_vote
post.votes.find_by(user: current_user)
end
def upvoted?
can_vote? && current_vote&.is_positive?
end
def downvoted?
can_vote? && current_vote&.is_negative?
end
end

View File

@@ -0,0 +1,19 @@
<span class="post-votes" data-id="<%= post.id %>">
<% if can_vote? %>
<% if upvoted? %>
<%= link_to upvote_icon, post_post_votes_path(post_id: post.id), class: "post-upvote-link post-unvote-link active-link", method: :delete, remote: true %>
<% else %>
<%= link_to upvote_icon, post_post_votes_path(post_id: post.id, score: "up"), class: "post-upvote-link inactive-link", method: :post, remote: true %>
<% end %>
<% end %>
<span class="post-score"><%= post.score %></span>
<% if can_vote? %>
<% if downvoted? %>
<%= link_to downvote_icon, post_post_votes_path(post_id: post.id), class: "post-downvote-link post-unvote-link active-link", method: :delete, remote: true %>
<% else %>
<%= link_to downvote_icon, post_post_votes_path(post_id: post.id, score: "down"), class: "post-downvote-link inactive-link", method: :post, remote: true %>
<% end %>
<% end %>
</span>

View File

@@ -0,0 +1,10 @@
.post-votes {
// Fix it so that the vote buttons don't move when the score changes width.
// XXX duplicated from app/components/comment_component/comment_component.scss
.post-score {
display: inline-block;
text-align: center;
min-width: 1.25em;
white-space: nowrap;
}
}

View File

@@ -14,4 +14,8 @@ module ComponentsHelper
def render_comment_section(post, **options) def render_comment_section(post, **options)
render CommentSectionComponent.new(post: post, **options) render CommentSectionComponent.new(post: post, **options)
end end
def render_post_votes(post, **options)
render PostVotesComponent.new(post: post, **options)
end
end end

View File

@@ -33,6 +33,14 @@ class PostVote < ApplicationRecord
end end
end end
def is_positive?
score > 0
end
def is_negative?
score < 0
end
def update_post_on_create def update_post_on_create
if score > 0 if score > 0
Post.where(:id => post_id).update_all("score = score + #{score}, up_score = up_score + #{score}") Post.where(:id => post_id).update_all("score = score + #{score}, up_score = up_score + #{score}")

View File

@@ -14,12 +14,7 @@
</span> </span>
<span class="info"> <span class="info">
<strong>Score</strong> <strong>Score</strong>
<span> <%= render_post_votes post, current_user: CurrentUser.user %>
<span id="score-for-post-<%= post.id %>"><%= post.score %></span>
<% if policy(PostVote).create? %>
(vote <%= link_to upvote_icon, post_post_votes_path(score: "up", post_id: post.id), remote: true, method: :post %>/<%= link_to downvote_icon, post_post_votes_path(score: "down", post_id: post.id), remote: true, method: :post %>)
<% end %>
</span>
</span> </span>
</div> </div>
<div class="row list-of-tags"> <div class="row list-of-tags">

View File

@@ -1,17 +1,9 @@
$("#add-to-favorites, #add-fav-button, #remove-from-favorites, #remove-fav-button").toggle(); $("#add-to-favorites, #add-fav-button, #remove-from-favorites, #remove-fav-button").toggle();
$("#remove-fav-button").addClass("animate"); $("#remove-fav-button").addClass("animate");
$("#score-for-post-<%= @post.id %>").text(<%= @post.score %>); $("span.post-votes[data-id=<%= @post.id %>]").replaceWith("<%= j render_post_votes @post, current_user: CurrentUser.user %>");
$("#favcount-for-post-<%= @post.id %>").text(<%= @post.fav_count %>); $("#favcount-for-post-<%= @post.id %>").text(<%= @post.fav_count %>);
$(".fav-buttons").toggleClass("fav-buttons-false").toggleClass("fav-buttons-true"); $(".fav-buttons").toggleClass("fav-buttons-false").toggleClass("fav-buttons-true");
<% if @post.can_be_voted_by?(CurrentUser.user) %>
$("#vote-links-for-post-<%= @post.id %>").show();
$("#unvote-link-for-post-<%= @post.id %>").hide();
<% else %>
$("#vote-links-for-post-<%= @post.id %>").hide();
$("#unvote-link-for-post-<%= @post.id %>").show();
<% end %>
<% if policy(@post).can_view_favlist? %> <% if policy(@post).can_view_favlist? %>
var fav_count = <%= @post.fav_count %>; var fav_count = <%= @post.fav_count %>;
$("#favlist").html("<%= j render "posts/partials/show/favorite_list", post: @post %>"); $("#favlist").html("<%= j render "posts/partials/show/favorite_list", post: @post %>");

View File

@@ -28,12 +28,7 @@
<span class="info"> <span class="info">
<strong>Score</strong> <strong>Score</strong>
<span> <%= render_post_votes post, current_user: CurrentUser.user %>
<span id="score-for-post-<%= post.id %>"><%= post.score %></span>
<% if policy(PostVote).create? %>
(vote <%= link_to upvote_icon, post_post_votes_path(score: "up", post_id: post.id), remote: true, method: :post %>/<%= link_to downvote_icon, post_post_votes_path(score: "down", post_id: post.id), remote: true, method: :post %>)
<% end %>
</span>
</span> </span>
</div> </div>

View File

@@ -1,5 +1 @@
Danbooru.Utility.notice("Vote saved"); $("span.post-votes[data-id=<%= @post.id %>]").replaceWith("<%= j render_post_votes @post, current_user: CurrentUser.user %>");
$("#score-for-post-<%= @post.id %>").html(<%= @post.score %>);
$("#vote-links-for-post-<%= @post.id %>").hide();
$("#unvote-link-for-post-<%= @post.id %>").show();

View File

@@ -1,5 +0,0 @@
Danbooru.Utility.notice("Unvoted successfully");
$("#score-for-post-<%= @post.id %>").html(<%= @post.score %>);
$("#vote-links-for-post-<%= @post.id %>").show();
$("#unvote-link-for-post-<%= @post.id %>").hide();

View File

@@ -0,0 +1 @@
create.js.erb

View File

@@ -21,17 +21,8 @@
</li> </li>
<li id="post-info-source">Source: <%= post_source_tag(post.source, post.normalized_source) %></li> <li id="post-info-source">Source: <%= post_source_tag(post.source, post.normalized_source) %></li>
<li id="post-info-rating">Rating: <%= post.pretty_rating %></li> <li id="post-info-rating">Rating: <%= post.pretty_rating %></li>
<li id="post-info-score">Score: <span id="score-for-post-<%= post.id %>"><%= post.score %></span> <li id="post-info-score">
<% if policy(PostVote).create? %> Score: <%= render_post_votes post, current_user: CurrentUser.user %>
<%= tag.span id: "vote-links-for-post-#{post.id}", style: ("display: none;" if !@post.can_be_voted_by?(CurrentUser.user)) do %>
(vote
<%= link_to upvote_icon, post_post_votes_path(post_id: post.id, score: "up"), remote: true, method: :post %>
<%= link_to downvote_icon, post_post_votes_path(post_id: post.id, score: "down"), remote: true, method: :post %>)
<% end %>
<%= tag.span id: "unvote-link-for-post-#{post.id}", style: ("display: none;" if @post.can_be_voted_by?(CurrentUser.user)) do %>
(<%= link_to "undo vote", post_post_votes_path(post), remote: true, method: :delete, class: "unvote-post-link" %>)
<% end %>
<% end %>
</li> </li>
<li id="post-info-favorites">Favorites: <span id="favcount-for-post-<%= post.id %>"><%= post.fav_count %></span> <li id="post-info-favorites">Favorites: <span id="favcount-for-post-<%= post.id %>"><%= post.fav_count %></span>
<% if policy(post).can_view_favlist? %> <% if policy(post).can_view_favlist? %>

View File

@@ -0,0 +1,56 @@
require "test_helper"
class PostVotesComponentTest < ViewComponent::TestCase
def render_post_votes(post, current_user:)
render_inline(PostVotesComponent.new(post: post, current_user: current_user))
end
context "The PostVotesComponent" do
setup do
@post = as(create(:user)) { create(:post) }
end
context "for a user who can't vote" do
should "not show the vote buttons" do
render_post_votes(@post, current_user: User.anonymous)
assert_css(".post-score")
assert_no_css(".post-upvote-link")
assert_no_css(".post-downvote-link")
end
end
context "for a user who can vote" do
setup do
@user = create(:gold_user)
end
should "show the vote buttons" do
render_post_votes(@post, current_user: @user)
assert_css(".post-upvote-link.inactive-link")
assert_css(".post-downvote-link.inactive-link")
end
context "for a downvoted post" do
should "highlight the downvote button as active" do
@post.vote!("down", @user)
render_post_votes(@post, current_user: @user)
assert_css(".post-upvote-link.inactive-link")
assert_css(".post-downvote-link.active-link")
end
end
context "for an upvoted post" do
should "highlight the upvote button as active" do
@post.vote!("up", @user)
render_post_votes(@post, current_user: @user)
assert_css(".post-upvote-link.active-link")
assert_css(".post-downvote-link.inactive-link")
end
end
end
end
end