diff --git a/app/controllers/activitypub/emoji_reactions_controller.rb b/app/controllers/activitypub/emoji_reactions_controller.rb new file mode 100644 index 000000000..29dc5ef4f --- /dev/null +++ b/app/controllers/activitypub/emoji_reactions_controller.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +class ActivityPub::EmojiReactionsController < ActivityPub::BaseController + include SignatureVerification + include Authorization + include AccountOwnedConcern + + EMOJI_REACTIONS_LIMIT = 60 + + before_action :require_signature!, if: :authorized_fetch_mode? + before_action :set_status + before_action :set_cache_headers + before_action :set_emoji_reactions + + def index + expires_in 0, public: public_fetch_mode? + render json: emoji_reactions_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_emoji_reactions + @emoji_reactions = @status.emoji_reactions + @emoji_reactions = @emoji_reactions.paginate_by_min_id(EMOJI_REACTIONS_LIMIT, params[:min_id]) + end + + def records_continue? + @emoji_reactions.size == EMOJI_REACTIONS_LIMIT + end + + def emoji_reactions_collection_presenter + page = ActivityPub::CollectionPresenter.new( + id: ActivityPub::TagManager.instance.emoji_reactions_uri_for(@status, page_params), + type: :unordered, + part_of: ActivityPub::TagManager.instance.emoji_reactions_uri_for(@status), + next: next_page, + items: @emoji_reactions.map { |emoji_reaction| emoji_reaction.uri.blank? ? emoji_reaction : emoji_reaction.uri } + ) + + return page if page_requested? + + ActivityPub::CollectionPresenter.new( + id: ActivityPub::TagManager.instance.emoji_reactions_uri_for(@status), + type: :unordered, + first: page + ) + end + + def page_requested? + truthy_param?(:page) + end + + def next_page + if records_continue? + ActivityPub::TagManager.instance.emoji_reactions_uri_for( + @status, + page: true, + min_id: @emoji_reactions&.last&.id, + ) + end + end + + def page_params + params_slice(:min_id).merge(page: true) + end +end diff --git a/app/controllers/emoji_reactions_controller.rb b/app/controllers/emoji_reactions_controller.rb new file mode 100644 index 000000000..a1d2dccbf --- /dev/null +++ b/app/controllers/emoji_reactions_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class EmojiReactionsController < ApplicationController + before_action :set_emoji_reaction + before_action :set_cache_headers + + def show + respond_to do |format| + format.json do + expires_in 3.minutes, public: true + render_with_cache json: @emoji_reaction, content_type: 'application/activity+json', serializer: ActivityPub::EmojiReactionSerializer, adapter: ActivityPub::Adapter + end + end + end + + private + + def set_emoji_reaction + @emoji_reaction = EmojiReaction.local.find(params[:id]) + end +end diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index 05d0810bd..9a28d6212 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -28,6 +28,7 @@ module ContextHelper expiry: { 'fedibird' => 'http://fedibird.com/ns#', 'expiry' => 'fedibird:expiry' }, other_setting: { 'fedibird' => 'http://fedibird.com/ns#', 'otherSetting' => 'fedibird:otherSetting' }, references: { 'fedibird' => 'http://fedibird.com/ns#', 'references' => { '@id' => "fedibird:references", '@type' => '@id' } }, + emoji_reactions: { 'fedibird' => 'http://fedibird.com/ns#', 'emojiReactions' => { '@id' => "fedibird:emojiReactions", '@type' => '@id' } }, is_cat: { 'misskey' => 'https://misskey-hub.net/ns#', 'isCat' => 'misskey:isCat' }, vcard: { 'vcard' => 'http://www.w3.org/2006/vcard/ns#' }, }.freeze diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 156ff06c4..fce7f8dca 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -47,6 +47,8 @@ class ActivityPub::TagManager end when :emoji emoji_url(target) + when :emoji_reaction + emoji_reaction_url(target) when :conversation context_url(target) end @@ -78,6 +80,12 @@ class ActivityPub::TagManager account_status_references_url(target.account, target, page_params) end + def emoji_reactions_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? + + account_status_emoji_reactions_url(target.account, target, page_params) + end + # Primary audience of a status # Public statuses go out to primarily the public collection # Unlisted and private statuses go out primarily to the followers collection diff --git a/app/models/emoji_reaction.rb b/app/models/emoji_reaction.rb index 869c8cc98..4003087e8 100644 --- a/app/models/emoji_reaction.rb +++ b/app/models/emoji_reaction.rb @@ -26,6 +26,9 @@ class EmojiReaction < ApplicationRecord has_one :notification, as: :activity, dependent: :destroy + scope :local, -> { where(uri: nil) } + scope :remote, -> { where.not(uri: nil) } + validates :name, presence: true validates_with EmojiReactionValidator @@ -37,6 +40,10 @@ class EmojiReaction < ApplicationRecord true end + def object_type + :emoji_reaction + end + private def queue_publish diff --git a/app/serializers/activitypub/emoji_reaction_serializer.rb b/app/serializers/activitypub/emoji_reaction_serializer.rb index 59ec8d6e6..cce397b11 100644 --- a/app/serializers/activitypub/emoji_reaction_serializer.rb +++ b/app/serializers/activitypub/emoji_reaction_serializer.rb @@ -7,7 +7,7 @@ class ActivityPub::EmojiReactionSerializer < ActivityPub::Serializer has_one :custom_emoji ,key: :tag, serializer: ActivityPub::EmojiSerializer, unless: -> { object.custom_emoji.nil? } def id - [ActivityPub::TagManager.instance.uri_for(object.account), '#emoji_reactions/', object.id].join + ActivityPub::TagManager.instance.uri_for(object) end def type diff --git a/app/serializers/activitypub/emoji_serializer.rb b/app/serializers/activitypub/emoji_serializer.rb index 4dc38f3ea..78df3c337 100644 --- a/app/serializers/activitypub/emoji_serializer.rb +++ b/app/serializers/activitypub/emoji_serializer.rb @@ -7,8 +7,25 @@ class ActivityPub::EmojiSerializer < ActivityPub::Serializer attributes :id, :type, :name, :updated - has_one :icon, serializer: ActivityPub::ImageSerializer + has_one :icon + class RemoteImageSerializer < ActivityPub::ImageSerializer + def url + object.instance.image_remote_url + end + end + + def self.serializer_for(model, options) + case model.class.name + when 'Paperclip::Attachment' + if model.instance.local? + ActivityPub::ImageSerializer + else + RemoteImageSerializer + end + end + end + def id ActivityPub::TagManager.instance.uri_for(object) end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index c75c8856e..6657f9b22 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ActivityPub::NoteSerializer < ActivityPub::Serializer - context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :quote_uri, :expiry, :references + context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :quote_uri, :expiry, :references, :emoji_reactions attributes :id, :type, :summary, :in_reply_to, :published, :url, @@ -22,6 +22,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer has_one :replies, serializer: ActivityPub::CollectionSerializer, if: :local? has_one :references, serializer: ActivityPub::CollectionSerializer, if: :local? + has_one :emoji_reactions, if: -> { object.local? && object.emoji_reaction? } has_many :poll_options, key: :one_of, if: :poll_and_not_multiple? has_many :poll_options, key: :any_of, if: :poll_and_multiple? @@ -85,6 +86,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer ) end + def emoji_reactions + ActivityPub::TagManager.instance.emoji_reactions_uri_for(object) + end + def language? object.language.present? end diff --git a/config/routes.rb b/config/routes.rb index 8eb26384e..c7bc0df61 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,6 +71,7 @@ Rails.application.routes.draw do resources :replies, only: [:index], module: :activitypub resources :references, only: [:index], module: :activitypub + resources :emoji_reactions, only: [:index], module: :activitypub end resources :followers, only: [:index], controller: :follower_accounts @@ -183,6 +184,7 @@ Rails.application.routes.draw do resources :tags, only: [:show] resources :emojis, only: [:show] + resources :emoji_reactions, only: [:show] resources :invites, only: [:index, :create, :destroy] resources :filters, except: [:show] resource :relationships, only: [:show, :update]