Skip to content

Commit a2a7293

Browse files
authored
Merge pull request #1705 from codidact/0valt/725/comments
User pings no longer temporarily become displayed as "unpingable" after comment edits
2 parents 9d3670c + 07cb550 commit a2a7293

8 files changed

Lines changed: 60 additions & 44 deletions

File tree

app/controllers/comments_controller.rb

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def create_thread
2929
@comment_thread = CommentThread.new(title: title, post: @post)
3030
@comment = Comment.new(post: @post, content: body, user: current_user, comment_thread: @comment_thread)
3131

32-
pings = check_for_pings @comment_thread, body
32+
pings = check_for_pings(@comment_thread, body)
3333

3434
success = ActiveRecord::Base.transaction do
3535
thread_success = @comment_thread.save
@@ -56,7 +56,7 @@ def create_thread
5656
ThreadFollower.create(user: tf.user, comment_thread: @comment_thread)
5757
end
5858

59-
apply_pings pings
59+
apply_pings(pings)
6060
else
6161
flash[:danger] = "Could not create comment thread: #{(@comment_thread.errors.full_messages \
6262
+ @comment.errors.full_messages).join(', ')}"
@@ -66,15 +66,15 @@ def create_thread
6666

6767
def create
6868
body = params[:content]
69-
pings = check_for_pings @comment_thread, body
69+
pings = check_for_pings(@comment_thread, body)
7070

7171
@comment = Comment.new(post: @post, content: body, user: current_user,
7272
comment_thread: @comment_thread, has_reference: false)
7373

7474
status = @comment.save
7575

7676
if status
77-
apply_pings pings
77+
apply_pings(pings)
7878
@comment_thread.thread_follower.each do |follower|
7979
next if follower.user_id == current_user.id
8080
next if pings.include? follower.user_id
@@ -105,17 +105,18 @@ def update
105105
@post = @comment.post
106106
@comment_thread = @comment.comment_thread
107107
before = @comment.content
108-
before_pings = check_for_pings @comment_thread, before
108+
before_pings = check_for_pings(@comment_thread, before)
109109
if @comment.update comment_params
110110
unless current_user.id == @comment.user_id
111111
audit('comment_update', @comment, "from <<#{before}>>\nto <<#{@comment.content}>>")
112112
end
113113

114-
after_pings = check_for_pings @comment_thread, @comment.content
114+
after_pings = check_for_pings(@comment_thread, @comment.content)
115115
apply_pings(after_pings - before_pings - @comment_thread.thread_follower.to_a)
116116

117117
render json: { status: 'success',
118-
comment: render_to_string(partial: 'comments/comment', locals: { comment: @comment }) }
118+
comment: render_to_string(partial: 'comments/comment',
119+
locals: { comment: @comment, pingable: after_pings }) }
119120
else
120121
render json: { status: 'failed',
121122
message: "Comment failed to save (#{@comment.errors.full_messages.join(', ')})" },
@@ -279,8 +280,7 @@ def post_follow
279280

280281
def pingable
281282
thread = params[:id] == '-1' ? CommentThread.new(post_id: params[:post]) : CommentThread.find(params[:id])
282-
ids = helpers.get_pingable(thread)
283-
users = User.where(id: ids)
283+
users = User.where(id: thread.pingable)
284284
render json: users.to_h { |u| [u.username, u.id] }
285285
end
286286

@@ -379,12 +379,16 @@ def check_if_target_post_locked
379379
check_if_locked(Post.find(params[:post_id]))
380380
end
381381

382+
# @param thread [CommentThread] thread to extract pings for
383+
# @param content [String] content to extract pings from
384+
# @return [Array<Integer>] list of pinged user ids
382385
def check_for_pings(thread, content)
383-
pingable = helpers.get_pingable(thread)
386+
pingable = thread.pingable
384387
matches = content.scan(/@#(\d+)/)
385388
matches.flatten.select { |m| pingable.include?(m.to_i) }.map(&:to_i)
386389
end
387390

391+
# @param pings [Array<Integer>] list of pinged user ids
388392
def apply_pings(pings)
389393
pings.each do |p|
390394
user = User.where(id: p).first

app/helpers/comments_helper.rb

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def render_pings(content, pingable: nil)
5858
was_pung = pingable.present? && pingable.include?(user.id)
5959
classes = "ping #{'me' if user.same_as?(current_user)} #{'unpingable' unless was_pung}"
6060
user_link user, class: classes, dir: 'ltr',
61-
title: was_pung ? '' : 'This user was not notified because they have not participated in this thread.'
61+
title: was_pung ? '' : I18n.t('comments.warnings.unrelated_user_not_pinged')
6262
end
6363
end.html_safe
6464
end
@@ -149,34 +149,6 @@ def rate_limited_error_msg(user, post)
149149
I18n.t('comments.errors.rate_limited', count: comments_count)
150150
end
151151

152-
##
153-
# Get a list of user IDs who should be pingable in a specified comment thread. This combines the post author, answer
154-
# authors, recent history event authors, recent comment authors on the post (in any thread), and all thread followers.
155-
# @param thread [CommentThread]
156-
# @return [Array<Integer>]
157-
def get_pingable(thread)
158-
post = thread.post
159-
160-
# post author +
161-
# answer authors +
162-
# last 500 history event users +
163-
# last 500 comment authors +
164-
# all thread followers
165-
query = <<~END_SQL
166-
SELECT posts.user_id FROM posts WHERE posts.id = #{post.id}
167-
UNION DISTINCT
168-
SELECT DISTINCT posts.user_id FROM posts WHERE posts.parent_id = #{post.id}
169-
UNION DISTINCT
170-
SELECT DISTINCT ph.user_id FROM post_histories ph WHERE ph.post_id = #{post.id}
171-
UNION DISTINCT
172-
SELECT DISTINCT comments.user_id FROM comments WHERE comments.post_id = #{post.id}
173-
UNION DISTINCT
174-
SELECT DISTINCT tf.user_id FROM thread_followers tf WHERE tf.comment_thread_id = #{thread.id || '-1'}
175-
END_SQL
176-
177-
ActiveRecord::Base.connection.execute(query).to_a.flatten
178-
end
179-
180152
##
181153
# Is the specified user comment rate limited for the specified post?
182154
# @param user [User] The user to check.

app/models/comment.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ def content_length
3333
end
3434
end
3535

36+
def pings
37+
pingable = thread.pingable
38+
matches = content.scan(/@#(\d+)/)
39+
matches.flatten.select { |m| pingable.include?(m.to_i) }.map(&:to_i)
40+
end
41+
3642
private
3743

3844
def create_follower

app/models/comment_thread.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class CommentThread < ApplicationRecord
1313

1414
after_create :create_follower
1515

16+
def self.post_followed?(post, user)
17+
ThreadFollower.where(post: post, user: user).any?
18+
end
19+
1620
def read_only?
1721
locked? || archived? || deleted?
1822
end
@@ -26,8 +30,27 @@ def can_access?(user)
2630
post.can_access?(user)
2731
end
2832

29-
def self.post_followed?(post, user)
30-
ThreadFollower.where(post: post, user: user).any?
33+
# Gets a list of user IDs who should be pingable in the thread.
34+
# @return [Array<Integer>]
35+
def pingable
36+
# post author +
37+
# answer authors +
38+
# last 500 history event users +
39+
# last 500 comment authors +
40+
# all thread followers
41+
query = <<~END_SQL
42+
SELECT posts.user_id FROM posts WHERE posts.id = #{post.id}
43+
UNION DISTINCT
44+
SELECT DISTINCT posts.user_id FROM posts WHERE posts.parent_id = #{post.id}
45+
UNION DISTINCT
46+
SELECT DISTINCT ph.user_id FROM post_histories ph WHERE ph.post_id = #{post.id}
47+
UNION DISTINCT
48+
SELECT DISTINCT comments.user_id FROM comments WHERE comments.post_id = #{post.id}
49+
UNION DISTINCT
50+
SELECT DISTINCT tf.user_id FROM thread_followers tf WHERE tf.comment_thread_id = #{id || '-1'}
51+
END_SQL
52+
53+
ActiveRecord::Base.connection.execute(query).to_a.flatten
3154
end
3255

3356
private

app/views/comment_threads/_expanded.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<% max_shown_comments = 5 %>
1212
<% comment_id ||= defined?(comment_id) ? comment_id : nil %>
13-
<% pingable = get_pingable(thread) %>
13+
<% pingable = thread.pingable %>
1414
<% shown_comments_count = [max_shown_comments, thread.reply_count].min %>
1515

1616
<%

app/views/flags/_flag.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<%= render 'posts/type_agnostic', show_type_tag: true, show_category_tag: true, post: flag.post %>
1717
</div>
1818
<% elsif flag.post_type == 'Comment' %>
19-
<%= render 'comments/comment', comment: flag.post, with_post_link: true %>
19+
<%= render 'comments/comment', comment: flag.post, with_post_link: true, pingable: flag.post.comment_thread.pingable %>
2020
<% end %>
2121
<div class="widget--body">
2222
<p>

app/views/moderator/recent_comments.html.erb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@
99
communities; consider using activity logs on user profiles instead.
1010
</p>
1111

12+
<%
13+
thread_pingables = @comments.map(&:comment_thread).to_set.to_h do |thread|
14+
[thread, thread.pingable]
15+
end
16+
%>
17+
1218
<% @comments.each do |comment| %>
13-
<%= render 'comments/comment', comment: comment, with_post_link: true %>
19+
<%= render 'comments/comment', comment: comment,
20+
pingable: thread_pingables[comment.comment_thread],
21+
with_post_link: true %>
1422
<% end %>
1523

1624
<div class="has-padding-top-4">

config/locales/strings/en.comments.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ en:
3434
Start new comment thread
3535
reply_to_thread: >
3636
Reply to this thread
37+
warnings:
38+
unrelated_user_not_pinged: >
39+
This user was not notified because they have not participated in this thread.

0 commit comments

Comments
 (0)