Fix articles counts based on display rules (#20669)

* Comment counts for signed in users

* Adjust comments count for articles

* Fixed counts for signed out users, added specs for Comments::Count

* Fixed counts for signed out and added specs for Comments::Count

* Removed debug info from the view

* Removed separate count for signed out users

* Remove irrelevant specs

* Remove unused scope
This commit is contained in:
Anna Buianova 2024-03-01 15:54:15 +03:00 committed by GitHub
parent fe213a81fa
commit 288c9ed31f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 95 additions and 2 deletions

View file

@ -274,6 +274,9 @@ class StoriesController < ApplicationController
@user = @article.user
@organization = @article.organization
@comments_order = fetch_sort_order
@comments_count = Comments::Count.new(@article).call
if @article.collection
@collection = @article.collection

View file

@ -0,0 +1,25 @@
# find comments count for an article based on our display rules for signed in users
# the count includes both comments displayed as usual (text), comments displayed as "deleted" or "hidden by post author"
# the count doesn't include comments not displayed at all (childless comments with score below HIDE_THRESHOLD)
module Comments
class Count
attr_reader :article
def initialize(article)
@article = article
end
def call
# comments that are not displayed at all (not even a "comment deleted" message):
# with the score below hiding threshold and w/o children
count_sql = "SELECT COUNT(id) FROM comments c1 WHERE score < ? AND commentable_id = ? " \
"AND commentable_type = ? AND NOT EXISTS " \
"(SELECT 1 FROM comments c2 WHERE c2.ancestry LIKE CONCAT('%/', c1.id::varchar(255)) " \
"OR c2.ancestry = c1.id::varchar(255))"
san_count_sql = Comment.sanitize_sql([count_sql, Comment::HIDE_THRESHOLD, @article.id, "Article"])
hidden_comments_cnt = Comment.count_by_sql(san_count_sql)
article.comments_count - hidden_comments_cnt
end
end
end

View file

@ -5,7 +5,7 @@
<div class="flex items-center">
<h2 class="crayons-subtitle-1">
<%= t("views.articles.comments.subtitle.#{@comments_order}_html",
num: tag.span(t("views.articles.comments.num", num: @article.comments_count), class: "js-comments-count", data: { comments_count: @article.comments_count })) %>
num: tag.span(t("views.articles.comments.num", num: @comments_count), class: "js-comments-count", data: { comments_count: @comments_count })) %>
</h2>
<% if user_signed_in? %>
<button class="c-btn c-btn--ghost crayons-btn--icon-left" id="toggle-comments-sort-dropdown" aria-controls="comments-sort-dropdown-container" aria-expanded="false" aria-label="<%= t("views.articles.comments.sort_button.aria_label") %>" aria-haspopup="true">

View file

@ -44,7 +44,7 @@
<span class="crayons-reaction__icon crayons-reaction__icon--borderless crayons-reaction__icon--inactive">
<%= crayons_icon_tag("comment.svg", aria_hidden: true) %>
</span>
<span class="crayons-reaction__count" id="reaction-number-comment" data-count="<%= @article.comments_count %>">
<span class="crayons-reaction__count" id="reaction-number-comment" data-count="<%= @comments_count %>">
<span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span>
</span>

View file

@ -0,0 +1,54 @@
require "rails_helper"
RSpec.describe Comments::Count do
let(:article) { create(:article) }
let!(:comment) { create(:comment, commentable: article) }
let!(:comment2) { create(:comment, commentable: article) }
it "returns correct number with regular comments" do
article.reload
count = described_class.new(article).call
expect(count).to eq(2)
end
it "returns correct number with children" do
create(:comment, commentable: article, parent: comment2, score: 20)
article.reload
count = described_class.new(article).call
expect(count).to eq(3)
end
it "doesn't include childless children" do
create(:comment, commentable: article, parent: comment, score: -450)
article.reload
count = described_class.new(article).call
expect(count).to eq(2)
end
it "includes ok children of a low-score comment (but not low-score children)" do
comment.update_column(:score, -500)
create(:comment, commentable: article, parent: comment, score: 10)
create(:comment, commentable: article, parent: comment, score: -490)
create(:comment, commentable: article, parent: comment2, score: 0)
article.reload
count = described_class.new(article).call
expect(count).to eq(4)
end
it "includes children of a low-score comment" do
child = create(:comment, commentable: article, parent: comment, score: -401)
create(:comment, commentable: article, parent: child, score: 10)
article.reload
count = described_class.new(article).call
expect(count).to eq(4)
end
it "includes a comment with low-score ancestors" do
comment.update_column(:score, -500)
child = create(:comment, commentable: article, parent: comment, score: -401)
create(:comment, commentable: article, parent: child, score: 10)
article.reload
count = described_class.new(article).call
expect(count).to eq(4)
end
end

View file

@ -217,6 +217,17 @@ RSpec.describe "ArticlesShow" do
expect(response.body).to include("Child comment")
expect(response.body).to include("Comment deleted") # instead of the low quality one
end
it "displays comments count w/o including super low-quality ones" do
get article.path
expect(response.body).to include("<span class=\"js-comments-count\" data-comments-count=\"3\">(3)</span>")
end
it "displays includes spam comments in comments count if they have children" do
create(:comment, score: 0, commentable: article, parent: spam_comment, body_markdown: "Child comment")
get article.path
expect(response.body).to include("<span class=\"js-comments-count\" data-comments-count=\"5\">(5)</span>")
end
end
context "when user not signed in" do