Skip to content

Commit

Permalink
引用APIを作成、ついでにブロック状況を引用APIに反映
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Oct 1, 2023
1 parent 65d3a38 commit cbd4b77
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def destroy
end

render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(
[@status], current_account.id, emoji_reactions_map: { @status.id => false }
[@status], current_account.id
)
rescue Mastodon::NotPermittedError
not_found
Expand Down
10 changes: 5 additions & 5 deletions app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def reblog?
end

def quote
reference_objects.where(attribute_type: 'QT').first&.target_status
@quote ||= reference_objects.where(quote: true).first&.target_status
end

def within_realtime_window?
Expand Down Expand Up @@ -480,12 +480,12 @@ def mutes_map(conversation_ids, account_id)
ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).each_with_object({}) { |m, h| h[m.conversation_id] = true }
end

def pins_map(status_ids, account_id)
StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
def blocks_map(account_ids, account_id)
Block.where(account_id: account_id, target_account_id: account_ids).each_with_object({}) { |b, h| h[b.target_account_id] = true }
end

def emoji_reactions_map(status_ids, account_id)
EmojiReaction.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |e, h| h[e.status_id] = true }
def pins_map(status_ids, account_id)
StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
end

def emoji_reaction_allows_map(status_ids, account_id)
Expand Down
1 change: 1 addition & 0 deletions app/models/status_reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# attribute_type :string
# quote :boolean default(FALSE), not null
#

class StatusReference < ApplicationRecord
Expand Down
4 changes: 4 additions & 0 deletions app/policies/status_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ def emoji_reaction?
show? && !blocking_author?
end

def quote?
%i(public public_unlisted unlisted).include?(record.visibility.to_sym) && show? && !blocking_author?
end

def destroy?
owned?
end
Expand Down
7 changes: 3 additions & 4 deletions app/presenters/status_relationships_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
class StatusRelationshipsPresenter
PINNABLE_VISIBILITIES = %w(public public_unlisted unlisted login private).freeze

attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map,
:bookmarks_map, :filters_map, :emoji_reactions_map, :attributes_map, :emoji_reaction_allows_map
attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map, :blocks_map,
:bookmarks_map, :filters_map, :attributes_map, :emoji_reaction_allows_map

def initialize(statuses, current_account_id = nil, **options)
@current_account_id = current_account_id
Expand All @@ -16,7 +16,6 @@ def initialize(statuses, current_account_id = nil, **options)
@mutes_map = {}
@pins_map = {}
@filters_map = {}
@emoji_reactions_map = {}
@emoji_reaction_allows_map = nil
else
statuses = statuses.compact
Expand All @@ -29,8 +28,8 @@ def initialize(statuses, current_account_id = nil, **options)
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(options[:favourites_map] || {})
@bookmarks_map = Status.bookmarks_map(status_ids, current_account_id).merge(options[:bookmarks_map] || {})
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {})
@blocks_map = Status.blocks_map(conversation_ids, current_account_id).merge(options[:blocks_map] || {})
@pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {})
@emoji_reactions_map = Status.emoji_reactions_map(status_ids, current_account_id).merge(options[:emoji_reactions_map] || {})
@emoji_reaction_allows_map = Status.emoji_reaction_allows_map(status_ids, current_account_id).merge(options[:emoji_reaction_allows_map] || {})
@attributes_map = options[:attributes_map] || {}
end
Expand Down
23 changes: 22 additions & 1 deletion app/serializers/rest/status_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class REST::StatusSerializer < ActiveModel::Serializer

attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
:sensitive, :spoiler_text, :visibility, :visibility_ex, :limited_scope, :language,
:uri, :url, :replies_count, :reblogs_count, :searchability, :markdown,
:uri, :url, :replies_count, :reblogs_count, :searchability, :markdown, :quote_id,
:status_reference_ids, :status_references_count, :status_referred_by_count,
:favourites_count, :emoji_reactions, :emoji_reactions_count, :reactions, :edited_at

Expand Down Expand Up @@ -33,6 +33,23 @@ class REST::StatusSerializer < ActiveModel::Serializer
has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer

class QuotedStatusSerializer < REST::StatusSerializer
attribute :blocked, if: :current_user?

def quote
nil
end

def blocked
if relationships
relationships.blocks_map[object.account_id] || false
else
current_user.account.blocking?(object.account_id)
end
end
end
belongs_to :quote, serializer: QuotedStatusSerializer

def id
object.id.to_s
end
Expand Down Expand Up @@ -159,6 +176,10 @@ def reactions
end
end

def quote_id
object.quote&.id
end

def reblogged
if relationships
relationships.reblogs_map[object.id] || false
Expand Down
39 changes: 28 additions & 11 deletions app/services/process_references_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ class ProcessReferencesService < BaseService
REFURL_EXP = /(RT|QT|BT|RN|RE)((:|;)?\s+|:|;)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
MAX_REFERENCES = 5

def call(status, reference_parameters, urls: nil, fetch_remote: true, no_fetch_urls: nil)
def call(status, reference_parameters, urls: nil, fetch_remote: true, no_fetch_urls: nil, quote_urls: nil)
@status = status
@reference_parameters = reference_parameters || []
@urls = urls || []
@quote_urls = quote_urls || []
@urls = (urls - @quote_urls) || []
@no_fetch_urls = no_fetch_urls || []
@fetch_remote = fetch_remote
@again = false

@attributes = {}

with_redis_lock("process_status_refs:#{@status.id}") do
@references_count = old_references.size

Expand All @@ -28,7 +31,7 @@ def call(status, reference_parameters, urls: nil, fetch_remote: true, no_fetch_u

@status.save!
end

quote_ur
create_notifications!
end

Expand All @@ -42,23 +45,23 @@ def self.need_process?(status, reference_parameters, urls)
reference_parameters.any? || (urls || []).any? || FormattingHelper.extract_status_plain_text(status).scan(REFURL_EXP).pluck(3).uniq.any?
end

def self.perform_worker_async(status, reference_parameters, urls)
def self.perform_worker_async(status, reference_parameters, urls, quote_urls)
return unless need_process?(status, reference_parameters, urls)

Rails.cache.write("status_reference:#{status.id}", true, expires_in: 10.minutes)
ProcessReferencesWorker.perform_async(status.id, reference_parameters, urls, [])
ProcessReferencesWorker.perform_async(status.id, reference_parameters, urls, [], quote_urls || [])
end

def self.call_service(status, reference_parameters, urls)
def self.call_service(status, reference_parameters, urls, quote_urls)
return unless need_process?(status, reference_parameters, urls)

ProcessReferencesService.new.call(status, reference_parameters || [], urls: urls || [], fetch_remote: false)
ProcessReferencesService.new.call(status, reference_parameters || [], urls: urls || [], fetch_remote: false, quote_urls: quote_urls)
end

private

def references
@references ||= @reference_parameters + scan_text!
@references ||= @reference_parameters + scan_text! + quote_status_ids
end

def old_references
Expand Down Expand Up @@ -88,20 +91,34 @@ def fetch_statuses!(urls)
target_urls = urls + @urls

target_urls.map do |url|
status = ResolveURLService.new.call(url, on_behalf_of: @status.account, fetch_remote: @fetch_remote && @no_fetch_urls.exclude?(url))
status = url_to_status(url)
@no_fetch_urls << url if !@fetch_remote && status.present? && status.local?
status
end
end

def url_to_status(url)
ResolveURLService.new.call(url, on_behalf_of: @status.account, fetch_remote: @fetch_remote && @no_fetch_urls.exclude?(url))
end

def quote_status_ids
@quote_status_ids ||= @quote_urls.filter_map { |url| url_to_status(url) }.map(&:id)
end

def quotable?(target_status)
StatusPolicy.new(@status.account, target_status).quote?
end

def add_references
return if added_references.empty?

@added_objects = []

statuses = Status.where(id: added_references)
statuses.each do |status|
@added_objects << @status.reference_objects.new(target_status: status, attribute_type: @attributes[status.id])
attribute_type = quote_status_ids.include?(status.id) ? 'QT' : @attributes[status.id]
attribute_type = 'BT' unless quotable?(status)
@added_objects << @status.reference_objects.new(target_status: status, attribute_type: attribute_type, quote: attribute_type.casecmp('QT').zero?)
status.increment_count!(:status_referred_by_count)
@references_count += 1

Expand Down Expand Up @@ -133,6 +150,6 @@ def remove_old_references
end

def launch_worker
ProcessReferencesWorker.perform_async(@status.id, @reference_parameters, @urls, @no_fetch_urls)
ProcessReferencesWorker.perform_async(@status.id, @reference_parameters, @urls, @no_fetch_urls, @quote_urls)
end
end
4 changes: 2 additions & 2 deletions app/workers/process_references_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ class ProcessReferencesWorker

sidekiq_options queue: 'pull', retry: 3

def perform(status_id, ids, urls, no_fetch_urls = nil)
ProcessReferencesService.new.call(Status.find(status_id), ids || [], urls: urls || [], no_fetch_urls: no_fetch_urls)
def perform(status_id, ids, urls, no_fetch_urls = nil, quote_urls = nil)
ProcessReferencesService.new.call(Status.find(status_id), ids || [], urls: urls || [], no_fetch_urls: no_fetch_urls, quote_urls: quote_urls || [])
rescue ActiveRecord::RecordNotFound
true
end
Expand Down
24 changes: 24 additions & 0 deletions db/migrate/20230930233930_add_quote_to_status_references.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require Rails.root.join('lib', 'mastodon', 'migration_helpers')

class AddQuoteToStatusReferences < ActiveRecord::Migration[7.0]
include Mastodon::MigrationHelpers

disable_ddl_transaction!

class StatusReference < ApplicationRecord; end

def up
safety_assured do
add_column_with_default :status_references, :quote, :boolean, default: false, allow_null: false
StatusReference.where(attribute_type: 'QT').update_all(quote: true) # rubocop:disable Rails/SkipsModelValidations
end
end

def down
safety_assured do
remove_column :status_references, :quote
end
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do
ActiveRecord::Schema[7.0].define(version: 2023_09_30_233930) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -1143,6 +1143,7 @@
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.string "attribute_type"
t.boolean "quote", default: false, null: false
t.index ["status_id"], name: "index_status_references_on_status_id"
t.index ["target_status_id"], name: "index_status_references_on_target_status_id"
end
Expand Down
8 changes: 8 additions & 0 deletions spec/fabricators/status_reference_fabricator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

Fabricator(:status_reference) do
status { Fabricate.build(:status) }
target_status { Fabricate.build(:status) }
attribute_type 'BT'
quote false
end
49 changes: 49 additions & 0 deletions spec/models/status_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,39 @@
end
end

describe '#quote' do
let(:target_status) { Fabricate(:status) }
let(:quote) { true }

before do
Fabricate(:status_reference, status: subject, target_status: target_status, quote: quote)
end

context 'when quoting single' do
it 'get quote' do
expect(subject.quote).to_not be_nil
expect(subject.quote.id).to eq target_status.id
end
end

context 'when multiple quotes' do
it 'get quote' do
target2 = Fabricate(:status)
Fabricate(:status_reference, status: subject, quote: quote)
expect(subject.quote).to_not be_nil
expect([target_status.id, target2.id].include?(subject.quote.id)).to be true
end
end

context 'when no quote but reference' do
let(:quote) { false }

it 'get quote' do
expect(subject.quote).to be_nil
end
end
end

describe '#content' do
it 'returns the text of the status if it is not a reblog' do
expect(subject.content).to eql subject.text
Expand Down Expand Up @@ -324,6 +357,22 @@
end
end

describe '.blocks_map' do
subject { described_class.blocks_map([status.account.id], account) }

let(:status) { Fabricate(:status) }
let(:account) { Fabricate(:account) }

it 'returns a hash' do
expect(subject).to be_a Hash
end

it 'contains true value' do
account.block!(status.account)
expect(subject[status.account.id]).to be true
end
end

describe '.favourites_map' do
subject { described_class.favourites_map([status], account) }

Expand Down
Loading

0 comments on commit cbd4b77

Please sign in to comment.