mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 13:08:40 +08:00
Previously, small actions in nested replies' Activity Log modal could not be edited, deleted, or recovered, and refreshes collapsed the modal. This change renders the activity log as proper small action posts with all the actions you expect from flat view.
118 lines
3.8 KiB
Ruby
Vendored
118 lines
3.8 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module NestedReplies
|
|
class PostTreeSerializer
|
|
def initialize(topic:, topic_view:, guardian:)
|
|
@topic = topic
|
|
@topic_view = topic_view
|
|
@guardian = guardian
|
|
@ignored_user_ids = IgnoredUser.ignored_ids_for(guardian.user)
|
|
end
|
|
|
|
SUGGESTED_AND_RELATED_KEYS = %i[
|
|
suggested_topics
|
|
suggested_group_name
|
|
related_topics
|
|
related_messages
|
|
].freeze
|
|
|
|
def serialize_topic
|
|
topic_view_json.merge(has_activity_log: activity_log_present?).except(
|
|
:post_stream,
|
|
:timeline_lookup,
|
|
:user_badges,
|
|
*SUGGESTED_AND_RELATED_KEYS,
|
|
)
|
|
end
|
|
|
|
# Produces the suggested/related payload we piggyback on whichever
|
|
# response has has_more_roots=false — mirroring how the flat view
|
|
# ships suggested_topics on the final /t/:id/posts.json chunk.
|
|
def serialize_suggested_and_related
|
|
topic_view_json.slice(*SUGGESTED_AND_RELATED_KEYS)
|
|
end
|
|
|
|
def serialize_post(post, reply_counts, descendant_counts = {})
|
|
# Assign the already-loaded topic to avoid an N+1 query per post
|
|
# in PostSerializer#topic, which reads object.topic.
|
|
post.topic = @topic
|
|
serializer = PostSerializer.new(post, scope: @guardian, root: false)
|
|
serializer.topic_view = @topic_view
|
|
serializer.notice_created_by_users = post_notice_created_by_users if @guardian.is_staff?
|
|
json = serializer.as_json
|
|
|
|
json[:direct_reply_count] = reply_counts[post.post_number] || 0
|
|
json[:total_descendant_count] = descendant_counts[post.id] || 0
|
|
|
|
if post.deleted_at.present?
|
|
json[:deleted_post_placeholder] = true
|
|
unless @guardian.is_staff?
|
|
json[:cooked] = ""
|
|
json[:raw] = nil
|
|
json[:actions_summary] = []
|
|
json =
|
|
json.slice(
|
|
:id,
|
|
:post_number,
|
|
:reply_to_post_number,
|
|
:deleted_post_placeholder,
|
|
:cooked,
|
|
:raw,
|
|
:actions_summary,
|
|
:direct_reply_count,
|
|
:total_descendant_count,
|
|
)
|
|
end
|
|
elsif post.post_number != 1 && @ignored_user_ids.include?(post.user_id)
|
|
json[:ignored_post_placeholder] = true
|
|
json[:cooked] = ""
|
|
json[:raw] = nil
|
|
json[:actions_summary] = []
|
|
end
|
|
|
|
json
|
|
end
|
|
|
|
def serialize_tree(post, children_map, reply_counts, descendant_counts = {})
|
|
node = serialize_post(post, reply_counts, descendant_counts)
|
|
children = children_map[post.post_number] || []
|
|
node[:children] = children.map do |child|
|
|
serialize_tree(child, children_map, reply_counts, descendant_counts)
|
|
end
|
|
node
|
|
end
|
|
|
|
private
|
|
|
|
def topic_view_json
|
|
@topic_view_json ||=
|
|
TopicViewSerializer.new(@topic_view, scope: @guardian, root: false).as_json
|
|
end
|
|
|
|
def post_notice_created_by_users
|
|
@post_notice_created_by_users ||=
|
|
begin
|
|
user_ids =
|
|
(@topic_view.post_custom_fields || {})
|
|
.filter_map do |_, custom_fields|
|
|
custom_fields.dig(Post::NOTICE, "created_by_user_id")
|
|
end
|
|
.uniq
|
|
|
|
User.real.where(id: user_ids).to_a
|
|
end
|
|
end
|
|
|
|
# Filters mirror Guardian#can_see_post? so the boolean does not leak
|
|
# the existence of small_actions/whispers the user cannot see.
|
|
def activity_log_present?
|
|
post_types = [Post.types[:small_action]]
|
|
post_types << Post.types[:whisper] if @guardian.user&.whisperer?
|
|
|
|
scope = @topic.posts
|
|
scope = scope.with_deleted if @guardian.can_see_deleted_posts?(@topic.category)
|
|
scope = scope.where(post_type: post_types).where.not(action_code: [nil, ""])
|
|
@guardian.filter_hidden_posts(scope, category: @topic.category).exists?
|
|
end
|
|
end
|
|
end
|