Try hiding low-quality comments (#18513)

* Try hiding low-quality comments

* Message indicating when negative comments are being hidden

* Adjust comment show count for logged-out visitors

* Remove unusued comments_to_show_count from article decorator

* Remove unusued comments_to_show_count from podcast episode decorator

* Add test for hidden negative comment

* Update spec/system/articles/user_visits_an_article_spec.rb

Co-authored-by: Fernando Valverde <fernando@fdo.cr>

* Don't create twice; do test logged-in and -out

* Comment caching needs to account for signed-in status

Co-authored-by: Fernando Valverde <fernando@fdo.cr>
This commit is contained in:
Joshua Wehner 2022-10-11 14:15:40 +02:00 committed by GitHub
parent f97b5de96e
commit 201f4d998a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 56 additions and 42 deletions

View file

@ -268,6 +268,7 @@ class StoriesController < ApplicationController
end
@comments_to_show_count = @article.cached_tag_list_array.include?("discuss") ? 50 : 30
@comments_to_show_count = 15 unless user_signed_in?
set_article_json_ld
assign_co_authors
@comment = Comment.new(body_markdown: @article&.comment_template)

View file

@ -33,10 +33,6 @@ class ArticleDecorator < ApplicationDecorator
end
end
def comments_to_show_count
cached_tag_list_array.include?("discuss") ? 75 : 25
end
def cached_tag_list_array
(cached_tag_list || "").split(", ")
end

View file

@ -1,8 +1,4 @@
class PodcastEpisodeDecorator < ApplicationDecorator
def comments_to_show_count
cached_tag_list_array.include?("discuss") ? 75 : 25
end
# this method exists because podcast episodes are "commentables"
# and in some parts of the code we assume they have this method,
# but podcast episodes don't have a cached_tag_list like articles do

View file

@ -2,6 +2,34 @@ module CommentsHelper
MAX_COMMENTS_TO_RENDER = 250
MIN_COMMENTS_TO_RENDER = 8
def any_negative_comments?(commentable)
commentable.comments.where("score < 0").any?
end
def any_hidden_negative_comments?(commentable)
!user_signed_in? && any_negative_comments?(commentable)
end
def all_comments_visible?(commentable)
!(commentable.any_comments_hidden || any_hidden_negative_comments?(commentable))
end
def article_comment_tree(article, count, order)
@article_comment_tree ||= begin
collection = Comment.tree_for(article, count, order)
collection.reject! { |comment| comment.score.negative? } unless user_signed_in?
collection
end
end
def podcast_comment_tree(episode)
@podcast_comment_tree ||= begin
collection = Comment.tree_for(episode, 12)
collection.reject! { |comment| comment.score.negative? } unless user_signed_in?
collection
end
end
def comment_class(comment, is_view_root: false)
if comment.root? || is_view_root
"root"

View file

@ -7,9 +7,10 @@
</div>
<% end %>
<% if @article.any_comments_hidden %>
<% unless all_comments_visible?(@article) %>
<p class="fs-s color-base-60 mb-4">
<%= t("views.comments.hidden.text_html", info: link_to(t("views.comments.hidden.info"), "/faq")) %>
<%= t("views.comments.visible_signed_in.text_html", sign_in: link_to(t("views.comments.visible_signed_in.sign_in"), "/enter")) if any_hidden_negative_comments?(@article) %>
<%= t("views.comments.hidden.text_html", info: link_to(t("views.comments.hidden.info"), "/faq")) if @article.any_comments_hidden %>
</p>
<% end %>

View file

@ -55,7 +55,10 @@
<div class="comments" id="comment-trees-container">
<% if @article.comments_count > 0 %>
<%= render partial: "articles/comment_tree", collection: Comment.tree_for(@article, @comments_to_show_count, @comments_order), as: :comment_node, cached: proc { |comment, _sub_comments| [comment, @comments_order] } %>
<%= render partial: "articles/comment_tree",
collection: article_comment_tree(@article, @comments_to_show_count, @comments_order),
as: :comment_node,
cached: proc { |comment, _sub_comments| [comment, @comments_order, user_signed_in] } %>
<% end %>
</div>
</div>

View file

@ -103,7 +103,7 @@
commentable: @episode,
commentable_type: "PodcastEpisode" %>
<div class="comment-trees" id="comment-trees-container">
<% Comment.tree_for(@episode, 12).each do |comment, sub_comments| %>
<% podcast_comment_tree(@episode).each do |comment, sub_comments| %>
<% cache ["comment_root_#{user_signed_in?}", comment] do %>
<%= tree_for(comment, sub_comments, @episode) %>
<% end %>

View file

@ -35,6 +35,9 @@ en:
text_html: "&bull; Edited %{on}"
on_html: on %{date}
expand: Expand
visible_signed_in:
text_html: "Some comments may only be visible to logged-in visitors. %{sign_in} to view all comments."
sign_in: "Sign in"
hidden:
text_html: Some comments have been hidden by the post's author - %{info}
info: find out more

View file

@ -35,6 +35,9 @@ fr:
text_html: "&bull; Edited %{on}"
on_html: on %{date}
expand: Expand
visible_signed_in:
text_html: "Some comments may only be visible to logged-in visitors. %{sign_in} to view all comments."
sign_in: "Sign in"
hidden:
text_html: Some comments have been hidden by the post's author - %{info}
info: find out more

View file

@ -83,18 +83,6 @@ RSpec.describe ArticleDecorator, type: :decorator do
end
end
describe "#comments_to_show_count" do
it "returns 25 if does not have a discuss tag" do
article.cached_tag_list = ""
expect(article.decorate.comments_to_show_count).to eq(25)
end
it "returns 75 if it does have a discuss tag" do
article.cached_tag_list = "discuss, python"
expect(article.decorate.comments_to_show_count).to eq(75)
end
end
describe "#cached_tag_list_array" do
it "returns no tags if the cached tag list is empty" do
article.cached_tag_list = ""

View file

@ -6,9 +6,9 @@ RSpec.describe PodcastEpisodeDecorator, type: :decorator do
it "serializes both the decorated object IDs and decorated methods" do
expected_result = {
"id" => podcast_episode.id, "comments_to_show_count" => podcast_episode.comments_to_show_count
"id" => podcast_episode.id, "published_timestamp" => podcast_episode.published_timestamp
}
expect(podcast_episode.as_json(only: [:id], methods: [:comments_to_show_count])).to eq(expected_result)
expect(podcast_episode.as_json(only: [:id], methods: [:published_timestamp])).to eq(expected_result)
end
it "serializes collections of decorated objects" do
@ -16,21 +16,9 @@ RSpec.describe PodcastEpisodeDecorator, type: :decorator do
decorated_collection = PodcastEpisode.decorate
expected_result = [
{ "id" => podcast_episode.id, "comments_to_show_count" => podcast_episode.comments_to_show_count },
{ "id" => podcast_episode.id, "published_timestamp" => podcast_episode.published_timestamp },
]
expect(decorated_collection.as_json(only: [:id], methods: [:comments_to_show_count])).to eq(expected_result)
end
end
describe "#comments_to_show_count" do
it "returns 25 if does not have a discuss tag" do
pe = build(:podcast_episode)
expect(pe.decorate.comments_to_show_count).to eq(25)
end
it "returns 75 if it does have a discuss tag" do
pe = build(:podcast_episode, tag_list: ["discuss"])
expect(pe.decorate.comments_to_show_count).to eq(75)
expect(decorated_collection.as_json(only: [:id], methods: [:published_timestamp])).to eq(expected_result)
end
end

View file

@ -15,9 +15,16 @@ RSpec.describe "Views an article", type: :system do
expect(page).to have_content(article.title)
end
it "shows comments", js: true do
create_list(:comment, 3, commentable: article)
it "shows non-negative comments", js: true do
comments = create_list(:comment, 4, commentable: article)
admin = create(:user, :admin)
create(:thumbsdown_reaction, reactable: comments.last, user: admin)
sidekiq_perform_enqueued_jobs
visit article.path
expect(page).to have_selector(".single-comment-node", visible: :visible, count: 4)
sign_out user
visit article.path
expect(page).to have_selector(".single-comment-node", visible: :visible, count: 3)
end