about summary refs log tree commit diff
path: root/app/controllers/activitypub/replies_controller.rb
blob: 8e0f9de2eeb78eef23b72d366cff662b491b24a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# frozen_string_literal: true

class ActivityPub::RepliesController < ActivityPub::BaseController
  include SignatureVerification
  include Authorization
  include AccountOwnedConcern

  DESCENDANTS_LIMIT = 60

  before_action :require_account_signature!, if: :authorized_fetch_mode?
  before_action :set_status
  before_action :set_cache_headers
  before_action :set_replies

  def index
    expires_in 0, public: public_fetch_mode?
    render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
  end

  private

  def pundit_user
    signed_request_account
  end

  def set_status
    @status = @account.statuses.find(params[:status_id])
    authorize @status, :show?
  rescue Mastodon::NotPermittedError
    not_found
  end

  def set_replies
    @replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses
    @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
    @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
  end

  def replies_collection_presenter
    page = ActivityPub::CollectionPresenter.new(
      id: account_status_replies_url(@account, @status, page_params),
      type: :unordered,
      part_of: account_status_replies_url(@account, @status),
      next: next_page,
      items: @replies.map { |status| status.local? ? status : status.uri }
    )

    return page if page_requested?

    ActivityPub::CollectionPresenter.new(
      id: account_status_replies_url(@account, @status),
      type: :unordered,
      first: page
    )
  end

  def page_requested?
    truthy_param?(:page)
  end

  def only_other_accounts?
    truthy_param?(:only_other_accounts)
  end

  def next_page
    if only_other_accounts?
      # Only consider remote accounts
      return nil if @replies.size < DESCENDANTS_LIMIT

      account_status_replies_url(
        @account,
        @status,
        page: true,
        min_id: @replies&.last&.id,
        only_other_accounts: true
      )
    else
      # For now, we're serving only self-replies, but next page might be other accounts
      next_only_other_accounts = @replies&.last&.account_id != @account.id || @replies.size < DESCENDANTS_LIMIT

      account_status_replies_url(
        @account,
        @status,
        page: true,
        min_id: next_only_other_accounts ? nil : @replies&.last&.id,
        only_other_accounts: next_only_other_accounts
      )
    end
  end

  def page_params
    params_slice(:only_other_accounts, :min_id).merge(page: true)
  end
end