comments: rework thresholded comments.

Previously thresholded comments were hidden completely. You had to click
the "Show X hidden comments" button to unhide all hidden comments in a
thread. Now it works like this:

* When a comment is below your threshold, the comment text is hidden and
  replaced by a `[hidden]` link, which you can click to unhide the comment.

* When a comment is at half your threshold (for example, your threshold
  is -8 but the comment is at -4), then the comment is greyed out.

This means that comments aren't completely hidden, they're just
collapsed, so you can see the commenter and the score without unhiding
the comment. It also means you don't have to scroll back up to unhide a
comment, and threads aren't disrupted by comments being secretly
hidden (which is confusing when people are replying to hidden comments,
which forces you to go back up and unhide to find).
This commit is contained in:
evazion
2021-01-18 05:03:35 -06:00
parent b6008b02b4
commit 07bdc6eab0
22 changed files with 196 additions and 201 deletions

View File

@@ -1,2 +1,5 @@
class ApplicationComponent < ViewComponent::Base
def policy(subject)
Pundit.policy!(current_user, subject)
end
end

View File

@@ -2,17 +2,8 @@
class CommentComponent < ApplicationComponent
attr_reader :comment, :context, :dtext_data, :show_deleted, :current_user
delegate :link_to_user, :time_ago_in_words_tagged, :format_text, :policy, to: :helpers
delegate :link_to_user, :time_ago_in_words_tagged, :format_text, to: :helpers
def self.with_collection(comments, current_user:, **options)
dtext_data = DText.preprocess(comments.map(&:body))
# XXX
#comments = comments.includes(:moderation_reports) if Pundit.policy!(current_user, ModerationReport).show?
super(comments, current_user: current_user, dtext_data: dtext_data, **options)
end
# XXX calls to pundit policy don't respect current_user.
def initialize(comment:, current_user:, context: nil, dtext_data: nil, show_deleted: false)
@comment = comment
@context = context
@@ -25,6 +16,14 @@ class CommentComponent < ApplicationComponent
!comment.is_deleted? || show_deleted || current_user.is_moderator?
end
def dimmed?
!comment.is_sticky? && comment.score < current_user.comment_threshold/2.0
end
def thresholded?
!comment.is_sticky? && comment.score < current_user.comment_threshold
end
def has_moderation_reports?
policy(ModerationReport).show? && comment.moderation_reports.present?
end

View File

@@ -8,7 +8,8 @@
data-do-not-bump-post="<%= comment.do_not_bump_post? %>"
data-is-deleted="<%= comment.is_deleted? %>"
data-is-sticky="<%= comment.is_sticky? %>"
data-below-threshold="<%= comment.score < current_user.comment_threshold %>"
data-is-dimmed="<%= dimmed? %>"
data-is-thresholded="<%= thresholded? %>"
data-is-reported="<%= has_moderation_reports? %>"
data-is-voted="<%= comment.voted_by?(current_user) %>">
<div class="author">
@@ -21,10 +22,14 @@
<%= link_to time_ago_in_words_tagged(comment.created_at), post_path(comment.post, anchor: "comment_#{comment.id}"), class: "message-timestamp" %>
</div>
<div class="content">
<div class="body prose">
<% if thresholded? %>
<%= link_to "[hidden]", "javascript:void(0)", class: "unhide-comment-link" %>
<% end %>
<%= tag.div class: "body prose", style: ("display: none;" if thresholded?) do %>
<%= format_text(comment.body, data: dtext_data) %>
</div>
<%= render "application/update_notice", record: comment %>
<%= render "application/update_notice", record: comment %>
<% end %>
<% if policy(comment).create? %>
<menu>

View File

@@ -21,7 +21,7 @@ article.comment {
}
}
&[data-below-threshold="true"][data-is-sticky="false"] {
&[data-is-dimmed="true"] {
opacity: 0.3;
&:hover {
@@ -29,6 +29,11 @@ article.comment {
}
}
.unhide-comment-link {
margin-bottom: 1em;
display: block;
}
.moderation-report-notice {
font-weight: bold;
color: var(--moderation-report-text-color);

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: true
class CommentSectionComponent < ApplicationComponent
attr_reader :post, :comments, :current_user, :limit, :dtext_data
def initialize(post:, current_user:, limit: nil)
@post = post
@current_user = current_user
@limit = limit
@comments = @post.comments.order(id: :asc)
@comments = @comments.includes(:creator)
@comments = @comments.includes(:votes) if !current_user.is_anonymous?
@comments = @comments.includes(:moderation_reports) if policy(ModerationReport).show?
@comments = @comments.last(limit) if limit.present?
@dtext_data = DText.preprocess(@comments.map(&:body))
end
def has_unloaded_comments?
unloaded_comment_count > 0
end
def unloaded_comment_count
return 0 if limit.nil?
[post.comments.size - limit, 0].max
end
end

View File

@@ -0,0 +1,20 @@
<div class="comments-for-post" data-post-id="<%= post.id %>">
<% if has_unloaded_comments? %>
<%= link_to "Show #{pluralize unloaded_comment_count, "more comment"}", comments_path(post_id: post.id), class: "show-all-comments-link", remote: true %>
<% end %>
<div class="list-of-comments list-of-messages">
<% if comments.present? %>
<%= render CommentComponent.with_collection(comments, current_user: current_user, context: :index_by_post, dtext_data: dtext_data) %>
<% else %>
<p>There are no comments.</p>
<% end %>
</div>
<% if policy(Comment).create? %>
<div class="new-comment">
<p><%= link_to "Post comment", new_comment_path(comment: { post_id: post.id }), class: "expand-comment-response" %></p>
<%= render "comments/form", comment: post.comments.new, hidden: true %>
</div>
<% end %>
</div>

View File

@@ -23,11 +23,6 @@ class PostsController < ApplicationController
@post = authorize Post.find(params[:id])
if request.format.html?
@comments = @post.comments
@comments = @comments.includes(:creator)
@comments = @comments.includes(:votes) if CurrentUser.is_member?
@comments = @comments.unhidden(CurrentUser.user)
include_deleted = @post.is_deleted? || (@post.parent_id.present? && @post.parent.is_deleted?) || CurrentUser.user.show_deleted_children?
@sibling_posts = @post.parent.present? ? @post.parent.children : Post.none
@sibling_posts = @sibling_posts.undeleted unless include_deleted

View File

@@ -1,9 +0,0 @@
module CommentsHelper
def render_comment(comment, **options)
render CommentComponent.new(comment: comment, **options)
end
def render_comment_list(comments, **options)
render CommentComponent.with_collection(comments, current_user: CurrentUser.user, **options)
end
end

View File

@@ -0,0 +1,17 @@
module ComponentsHelper
def post_preview(post, **options)
render PostPreviewComponent.new(post: post, **options)
end
def post_previews_html(posts, **options)
render PostPreviewComponent.with_collection(posts, **options)
end
def render_comment(comment, **options)
render CommentComponent.new(comment: comment, **options)
end
def render_comment_section(post, **options)
render CommentSectionComponent.new(post: post, **options)
end
end

View File

@@ -1,12 +1,4 @@
module PostsHelper
def post_preview(post, **options)
render PostPreviewComponent.new(post: post, **options)
end
def post_previews_html(posts, **options)
render PostPreviewComponent.with_collection(posts, **options)
end
def reportbooru_enabled?
Danbooru.config.reportbooru_server.present? && Danbooru.config.reportbooru_key.present?
end

View File

@@ -4,6 +4,7 @@ Comment.initialize_all = function() {
if ($("#c-posts").length || $("#c-comments").length) {
$(document).on("click.danbooru.comment", ".edit_comment_link", Comment.show_edit_form);
$(document).on("click.danbooru.comment", ".expand-comment-response", Comment.show_new_comment_form);
$(document).on("click.danbooru.comment", ".unhide-comment-link", Comment.unhide_comment);
}
}
@@ -20,6 +21,13 @@ Comment.show_edit_form = function(e) {
e.preventDefault();
}
Comment.unhide_comment = function(e) {
let $comment = $(this).closest(".comment");
$comment.find(".unhide-comment-link").hide();
$comment.find(".body").show();
e.preventDefault();
}
$(document).ready(function() {
Comment.initialize_all();
});

View File

@@ -1,20 +1,10 @@
@import "../base/000_vars.scss";
div.comments-for-post {
div.moderation-comments-notice {
margin: 1em 0;
font-weight: bold;
color: var(--moderation-report-text-color);
}
div.hidden-comments-notice {
margin: 1em 0;
}
}
div#c-comments {
div#a-index, div#a-show {
div#a-index {
div.header {
margin-bottom: 1em;
span.info {
margin-right: 1.5em;
}
@@ -58,7 +48,7 @@ div#c-comments {
text-align: center;
}
div.comments-for-post {
div.comment-section {
flex: 1;
}
}
@@ -89,12 +79,6 @@ form.edit_comment div.input.boolean {
width: auto;
height: auto;
}
div.comments-for-post {
div.header div.row span.info {
display: block;
}
}
}
}
}

View File

@@ -118,21 +118,6 @@ class Comment < ApplicationRecord
user.id.in?(votes.map(&:user_id))
end
def visibility(user)
return :invisible if is_deleted? && !user.is_moderator?
return :hidden if is_deleted? && user.is_moderator?
return :hidden if score < user.comment_threshold && !is_sticky?
return :visible
end
def self.hidden(user)
select { |comment| comment.visibility(user) == :hidden }
end
def self.unhidden(user)
select { |comment| comment.visibility(user) == :visible }
end
def quoted_response
DText.quote(body, creator.name)
end

View File

@@ -49,7 +49,7 @@ class Post < ApplicationRecord
has_many :appeals, :class_name => "PostAppeal", :dependent => :destroy
has_many :votes, :class_name => "PostVote", :dependent => :destroy
has_many :notes, :dependent => :destroy
has_many :comments, -> {order("comments.id")}, :dependent => :destroy
has_many :comments, :dependent => :destroy
has_many :children, -> {order("posts.id")}, :class_name => "Post", :foreign_key => "parent_id"
has_many :approvals, :class_name => "PostApproval", :dependent => :destroy
has_many :disapprovals, :class_name => "PostDisapproval", :dependent => :destroy

View File

@@ -4,13 +4,15 @@
<% end %>
<% @posts.select(&:visible?).each do |post| %>
<% if post.comments.unhidden(CurrentUser.user).any? || post.comments.hidden(CurrentUser.user).any? %>
<%= tag.div id: "post_#{post.id}", **PostPreviewComponent.new(post: post).article_attrs("post") do %>
<div class="preview">
<%= link_to(image_tag(post.preview_file_url), post_path(post)) %>
</div>
<%= render "comments/partials/index/list", post: post, comments: post.comments.unhidden(CurrentUser.user).last(6), page: :comments %>
<% end %>
<%= tag.div id: "post_#{post.id}", **PostPreviewComponent.new(post: post).article_attrs("post") do %>
<div class="preview">
<%= link_to(image_tag(post.preview_file_url), post_path(post)) %>
</div>
<div class="comment-section">
<%= render "comments/partials/index/header", post: post %>
<%= render_comment_section(post, limit: 6, current_user: CurrentUser.user) %>
</div>
<% end %>
<% end %>
</div>

View File

@@ -1,5 +1,3 @@
$("#threshold-comments-notice-for-<%= @post.id %>").hide();
var current_comment_section = $("div.comments-for-post[data-post-id=<%= @post.id %>] div.list-of-comments");
current_comment_section.html("<%= j render_comment_list(@comments, context: :index_by_post) %>");
var current_comment_section = $("div.comments-for-post[data-post-id=<%= @post.id %>]");
current_comment_section.replaceWith("<%= j render_comment_section(@post, limit: nil, current_user: CurrentUser.user) %>");
$(window).trigger("danbooru:index_for_post", [<%= @post.id %>]);

View File

@@ -1,30 +0,0 @@
<div class="comments-for-post" data-post-id="<%= post.id %>">
<% if page == :comments %>
<%= render "comments/partials/index/header", :post => post %>
<% end %>
<% if post.comments.hidden(CurrentUser.user).any? || (page == :comments && post.comments.size > 6) %>
<div class="row hidden-comments-notice">
<span class="info" id="threshold-comments-notice-for-<%= post.id %>">
<%= link_to "Show #{pluralize post.comments.hidden(CurrentUser.user).size, "hidden comment"}", comments_path(post_id: post.id), id: "show-all-comments-link", remote: true %>
</span>
</div>
<% end %>
<div class="list-of-comments list-of-messages">
<% if comments.present? %>
<%= render_comment_list(comments, context: :index_by_post) %>
<% elsif post.last_commented_at.present? %>
<p>There are no visible comments.</p>
<% else %>
<p>There are no comments.</p>
<% end %>
</div>
<% if policy(Comment).create? %>
<div class="new-comment">
<p><%= link_to "Post comment", new_comment_path(comment: { post_id: post.id }), :class => "expand-comment-response" %></p>
<%= render "comments/form", comment: post.comments.new, hidden: true %>
</div>
<% end %>
</div>

View File

@@ -105,7 +105,7 @@
<% end %>
<section id="comments">
<%= render "comments/partials/index/list", comments: @comments, post: @post, page: :post %>
<%= render_comment_section(@post, current_user: CurrentUser.user) %>
</section>
<section id="notes" style="display: none;">