mirror of
https://github.com/yingziwu/mastodon.git
synced 2026-02-19 16:53:16 +00:00
Implement FEP 7888: Part 1 - publish conversation context (#35959)
This commit is contained in:
parent
9463a31107
commit
65b4a0a6f1
15 changed files with 309 additions and 12 deletions
81
app/controllers/activitypub/contexts_controller.rb
Normal file
81
app/controllers/activitypub/contexts_controller.rb
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::ContextsController < ActivityPub::BaseController
|
||||
vary_by -> { 'Signature' if authorized_fetch_mode? }
|
||||
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_conversation
|
||||
before_action :set_items
|
||||
|
||||
DESCENDANTS_LIMIT = 60
|
||||
|
||||
def show
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
render_with_cache json: context_presenter, serializer: ActivityPub::ContextSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
def items
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
render_with_cache json: items_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_required?
|
||||
false
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = Conversation.local.find(params[:id])
|
||||
end
|
||||
|
||||
def set_items
|
||||
@items = @conversation.statuses.distributable_visibility.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
|
||||
end
|
||||
|
||||
def context_presenter
|
||||
first_page = ActivityPub::CollectionPresenter.new(
|
||||
id: items_context_url(@conversation, page_params),
|
||||
type: :unordered,
|
||||
part_of: items_context_url(@conversation),
|
||||
next: next_page,
|
||||
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
|
||||
)
|
||||
|
||||
ActivityPub::ContextPresenter.from_conversation(@conversation).tap do |presenter|
|
||||
presenter.first = first_page
|
||||
end
|
||||
end
|
||||
|
||||
def items_collection_presenter
|
||||
page = ActivityPub::CollectionPresenter.new(
|
||||
id: items_context_url(@conversation, page_params),
|
||||
type: :unordered,
|
||||
part_of: items_context_url(@conversation),
|
||||
next: next_page,
|
||||
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
|
||||
)
|
||||
|
||||
return page if page_requested?
|
||||
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: items_context_url(@conversation),
|
||||
type: :unordered,
|
||||
first: page
|
||||
)
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
truthy_param?(:page)
|
||||
end
|
||||
|
||||
def next_page
|
||||
return nil if @items.size < DESCENDANTS_LIMIT
|
||||
|
||||
items_context_url(@conversation, page: true, min_id: @items.last.id)
|
||||
end
|
||||
|
||||
def page_params
|
||||
params.permit(:page, :min_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -40,6 +40,8 @@ class ActivityPub::TagManager
|
|||
case target.object_type
|
||||
when :person
|
||||
target.instance_actor? ? instance_actor_url : account_url(target)
|
||||
when :conversation
|
||||
context_url(target)
|
||||
when :note, :comment, :activity
|
||||
return activity_account_status_url(target.account, target) if target.reblog?
|
||||
|
||||
|
|
@ -76,6 +78,12 @@ class ActivityPub::TagManager
|
|||
activity_account_status_url(target.account, target)
|
||||
end
|
||||
|
||||
def context_uri_for(target, page_params = nil)
|
||||
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
|
||||
|
||||
items_context_url(target.conversation, page_params)
|
||||
end
|
||||
|
||||
def replies_uri_for(target, page_params = nil)
|
||||
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
#
|
||||
# Table name: conversations
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# uri :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# id :bigint(8) not null, primary key
|
||||
# uri :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# parent_account_id :bigint(8)
|
||||
# parent_status_id :bigint(8)
|
||||
#
|
||||
|
||||
class Conversation < ApplicationRecord
|
||||
|
|
@ -15,7 +17,24 @@ class Conversation < ApplicationRecord
|
|||
|
||||
has_many :statuses, dependent: nil
|
||||
|
||||
belongs_to :parent_status, class_name: 'Status', optional: true, inverse_of: :conversation
|
||||
belongs_to :parent_account, class_name: 'Account', optional: true
|
||||
|
||||
scope :local, -> { where(uri: nil) }
|
||||
|
||||
before_validation :set_parent_account, on: :create
|
||||
|
||||
def local?
|
||||
uri.nil?
|
||||
end
|
||||
|
||||
def object_type
|
||||
:conversation
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_parent_account
|
||||
self.parent_account = parent_status.account if parent_status.present?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ class Status < ApplicationRecord
|
|||
belongs_to :reblog, foreign_key: 'reblog_of_id', inverse_of: :reblogs
|
||||
end
|
||||
|
||||
has_one :owned_conversation, class_name: 'Conversation', foreign_key: 'parent_status_id', inverse_of: :parent_status, dependent: nil
|
||||
|
||||
has_many :favourites, inverse_of: :status, dependent: :destroy
|
||||
has_many :bookmarks, inverse_of: :status, dependent: :destroy
|
||||
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
|
||||
|
|
@ -442,7 +444,8 @@ class Status < ApplicationRecord
|
|||
self.in_reply_to_account_id = carried_over_reply_to_account_id
|
||||
self.conversation_id = thread.conversation_id if conversation_id.nil?
|
||||
elsif conversation_id.nil?
|
||||
self.conversation = Conversation.new
|
||||
conversation = build_owned_conversation
|
||||
self.conversation = conversation
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
14
app/presenters/activitypub/context_presenter.rb
Normal file
14
app/presenters/activitypub/context_presenter.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::ContextPresenter < ActiveModelSerializers::Model
|
||||
attributes :id, :type, :attributed_to, :first, :object_type
|
||||
|
||||
class << self
|
||||
def from_conversation(conversation)
|
||||
new.tap do |presenter|
|
||||
presenter.id = ActivityPub::TagManager.instance.uri_for(conversation)
|
||||
presenter.attributed_to = ActivityPub::TagManager.instance.uri_for(conversation.parent_account)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
11
app/serializers/activitypub/context_serializer.rb
Normal file
11
app/serializers/activitypub/context_serializer.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::ContextSerializer < ActivityPub::Serializer
|
||||
include RoutingHelper
|
||||
|
||||
attributes :id, :type, :attributed_to, :first
|
||||
|
||||
def type
|
||||
'Collection'
|
||||
end
|
||||
end
|
||||
|
|
@ -9,7 +9,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
|||
:in_reply_to, :published, :url,
|
||||
:attributed_to, :to, :cc, :sensitive,
|
||||
:atom_uri, :in_reply_to_atom_uri,
|
||||
:conversation
|
||||
:conversation, :context
|
||||
|
||||
attribute :content
|
||||
attribute :content_map, if: :language?
|
||||
|
|
@ -163,6 +163,12 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
|||
end
|
||||
end
|
||||
|
||||
def context
|
||||
return if object.conversation.nil?
|
||||
|
||||
ActivityPub::TagManager.instance.uri_for(object.conversation)
|
||||
end
|
||||
|
||||
def local?
|
||||
object.account.local?
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue