Skip to content

Commit 92dd2e1

Browse files
ClearlyClairehiyuki2578
authored andcommitted
Give the replies collection an identifier and enable pagination (mastodon#10128)
1 parent 401ec89 commit 92dd2e1

4 files changed

Lines changed: 67 additions & 3 deletions

File tree

app/controllers/statuses_controller.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class StatusesController < ApplicationController
1818
before_action :redirect_to_original, only: [:show]
1919
before_action :set_referrer_policy_header, only: [:show]
2020
before_action :set_cache_headers
21+
before_action :set_replies, only: [:replies]
2122

2223
content_security_policy only: :embed do |p|
2324
p.frame_ancestors(false)
@@ -63,8 +64,37 @@ def embed
6364
render 'stream_entries/embed', layout: 'embedded'
6465
end
6566

67+
def replies
68+
skip_session!
69+
70+
render json: replies_collection_presenter,
71+
serializer: ActivityPub::CollectionSerializer,
72+
adapter: ActivityPub::Adapter,
73+
content_type: 'application/activity+json',
74+
skip_activities: true
75+
end
76+
6677
private
6778

79+
def replies_collection_presenter
80+
page = ActivityPub::CollectionPresenter.new(
81+
id: replies_account_status_url(@account, @status, page_params),
82+
type: :unordered,
83+
part_of: replies_account_status_url(@account, @status),
84+
next: next_page,
85+
items: @replies.map { |status| status.local ? status : status.id }
86+
)
87+
if page_requested?
88+
page
89+
else
90+
ActivityPub::CollectionPresenter.new(
91+
id: replies_account_status_url(@account, @status),
92+
type: :unordered,
93+
first: page
94+
)
95+
end
96+
end
97+
6898
def create_descendant_thread(starting_depth, statuses)
6999
depth = starting_depth + statuses.size
70100
if depth < DESCENDANTS_DEPTH_LIMIT
@@ -174,4 +204,27 @@ def set_referrer_policy_header
174204
return if @status.public_visibility? || @status.unlisted_visibility?
175205
response.headers['Referrer-Policy'] = 'origin'
176206
end
207+
208+
def page_requested?
209+
params[:page] == 'true'
210+
end
211+
212+
def set_replies
213+
@replies = page_params[:other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
214+
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
215+
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
216+
end
217+
218+
def next_page
219+
last_reply = @replies.last
220+
return if last_reply.nil?
221+
same_account = last_reply.account_id == @account.id
222+
return unless same_account || @replies.size == DESCENDANTS_LIMIT
223+
same_account = false unless @replies.size == DESCENDANTS_LIMIT
224+
replies_account_status_url(@account, @status, page: true, min_id: last_reply.id, other_accounts: !same_account)
225+
end
226+
227+
def page_params
228+
{ page: true, other_accounts: params[:other_accounts], min_id: params[:min_id] }.compact
229+
end
177230
end

app/lib/activitypub/tag_manager.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ def activity_uri_for(target)
4848
activity_account_status_url(target.account, target)
4949
end
5050

51+
def replies_uri_for(target, page_params = nil)
52+
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
53+
54+
replies_account_status_url(target.account, target, page_params)
55+
end
56+
5157
# Primary audience of a status
5258
# Public statuses go out to primarily the public collection
5359
# Unlisted and private statuses go out primarily to the followers collection

app/serializers/activitypub/note_serializer.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
1313
has_many :media_attachments, key: :attachment
1414
has_many :virtual_tags, key: :tag
1515

16-
has_one :replies, serializer: ActivityPub::CollectionSerializer
16+
has_one :replies, serializer: ActivityPub::CollectionSerializer, if: :local?
1717

1818
def id
1919
ActivityPub::TagManager.instance.uri_for(object)
@@ -36,12 +36,16 @@ def content_map
3636
end
3737

3838
def replies
39+
replies = object.self_replies(5).pluck(:id, :uri)
40+
last_id = replies.last&.first
3941
ActivityPub::CollectionPresenter.new(
4042
type: :unordered,
43+
id: ActivityPub::TagManager.instance.replies_uri_for(object),
4144
first: ActivityPub::CollectionPresenter.new(
4245
type: :unordered,
43-
page: true,
44-
items: object.self_replies(5).pluck(:uri)
46+
part_of: ActivityPub::TagManager.instance.replies_uri_for(object),
47+
items: replies.map(&:second),
48+
next: last_id ? ActivityPub::TagManager.instance.replies_uri_for(object, page: true, min_id: last_id) : nil
4549
)
4650
)
4751
end

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
member do
5757
get :activity
5858
get :embed
59+
get :replies
5960
end
6061
end
6162

0 commit comments

Comments
 (0)