diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index e673faca04..65db807d18 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -29,7 +29,7 @@ class StatusesController < ApplicationController end format.json do - expires_in 3.minutes, public: true if @status.distributable? && public_fetch_mode? + expires_in @status.quote&.pending? ? 5.seconds : 3.minutes, public: true if @status.distributable? && public_fetch_mode? render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter end end diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 2a8c9bfb2d..aac58b31f4 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -145,6 +145,7 @@ class Status extends ImmutablePureComponent { 'hidden', 'unread', 'pictureInPicture', + 'onQuoteCancel', ]; state = { diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 0def7ab50f..28fb454620 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -379,6 +379,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def conversation_from_uri(uri) return nil if uri.nil? return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri) + return ActivityPub::TagManager.instance.uri_to_resource(uri, Conversation) if ActivityPub::TagManager.instance.local_uri?(uri) begin Conversation.find_or_create_by!(uri: uri) diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 3174d1792e..40adb57378 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -241,12 +241,6 @@ class ActivityPub::TagManager !host.nil? && (::TagManager.instance.local_domain?(host) || ::TagManager.instance.web_domain?(host)) end - def uri_to_local_id(uri, param = :id) - path_params = Rails.application.routes.recognize_path(uri) - path_params[:username] = Rails.configuration.x.local_domain if path_params[:controller] == 'instance_actors' - path_params[param] - end - def uris_to_local_accounts(uris) usernames = [] ids = [] @@ -264,6 +258,14 @@ class ActivityPub::TagManager uri_to_resource(uri, Account) end + def uri_to_local_conversation(uri) + path_params = Rails.application.routes.recognize_path(uri) + return unless path_params[:controller] == 'activitypub/contexts' + + account_id, conversation_id = path_params[:id].split('-') + Conversation.find_by(parent_account_id: account_id, id: conversation_id) + end + def uri_to_resource(uri, klass) return if uri.nil? @@ -271,6 +273,8 @@ class ActivityPub::TagManager case klass.name when 'Account' uris_to_local_accounts([uri]).first + when 'Conversation' + uri_to_local_conversation(uri) else StatusFinder.new(uri).status end diff --git a/app/lib/connection_pool/shared_timed_stack.rb b/app/lib/connection_pool/shared_timed_stack.rb index 14a5285c45..8a13f4473b 100644 --- a/app/lib/connection_pool/shared_timed_stack.rb +++ b/app/lib/connection_pool/shared_timed_stack.rb @@ -70,6 +70,7 @@ class ConnectionPool::SharedTimedStack if @created == @max && !@queue.empty? throw_away_connection = @queue.pop @tagged_queue[throw_away_connection.site].delete(throw_away_connection) + throw_away_connection.close @create_block.call(preferred_tag) elsif @created != @max connection = @create_block.call(preferred_tag) diff --git a/app/lib/ostatus/tag_manager.rb b/app/lib/ostatus/tag_manager.rb index cb0c9f8966..7d0f23c4dc 100644 --- a/app/lib/ostatus/tag_manager.rb +++ b/app/lib/ostatus/tag_manager.rb @@ -11,16 +11,12 @@ class OStatus::TagManager def unique_tag_to_local_id(tag, expected_type) return nil unless local_id?(tag) - if ActivityPub::TagManager.instance.local_uri?(tag) - ActivityPub::TagManager.instance.uri_to_local_id(tag) - else - matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) - matches[1] unless matches.nil? - end + matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) + matches[1] unless matches.nil? end def local_id?(id) - id.start_with?("tag:#{Rails.configuration.x.local_domain}") || ActivityPub::TagManager.instance.local_uri?(id) + id.start_with?("tag:#{Rails.configuration.x.local_domain}") end def uri_for(target) diff --git a/app/models/quote.rb b/app/models/quote.rb index 4ad393e3a5..e4f3b823fe 100644 --- a/app/models/quote.rb +++ b/app/models/quote.rb @@ -47,6 +47,8 @@ class Quote < ApplicationRecord def accept! update!(state: :accepted) + + reset_parent_cache! if attribute_previously_changed?(:state) end def reject! @@ -75,6 +77,15 @@ class Quote < ApplicationRecord private + def reset_parent_cache! + return if status_id.nil? + + Rails.cache.delete("v3:statuses/#{status_id}") + + # This clears the web cache for the ActivityPub representation + Rails.cache.delete("statuses/show:v3:statuses/#{status_id}") + end + def set_accounts self.account = status.account self.quoted_account = quoted_status&.account diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 1cdf0b4830..c45454fc7f 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -204,7 +204,11 @@ class ActivityPub::ProcessStatusUpdateService < BaseService def update_tags! previous_tags = @status.tags.to_a - current_tags = @status.tags = Tag.find_or_create_by_names(@raw_tags) + current_tags = @status.tags = @raw_tags.flat_map do |tag| + Tag.find_or_create_by_names([tag]).filter(&:valid?) + rescue ActiveRecord::RecordInvalid + [] + end return unless @status.distributable? diff --git a/app/workers/feed_insert_worker.rb b/app/workers/feed_insert_worker.rb index 7b9ae5eada..fbde4f115f 100644 --- a/app/workers/feed_insert_worker.rb +++ b/app/workers/feed_insert_worker.rb @@ -57,7 +57,7 @@ class FeedInsertWorker def notify?(filter_result) return false if @type != :home || @status.reblog? || (@status.reply? && @status.in_reply_to_account_id != @status.account_id) || - filter_result == :filter + update? || filter_result == :filter Follow.find_by(account: @follower, target_account: @status.account)&.notify? end diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb index 1a5745a86a..d0103dc664 100644 --- a/app/workers/move_worker.rb +++ b/app/workers/move_worker.rb @@ -64,6 +64,16 @@ class MoveWorker .in_batches do |follows| ListAccount.where(follow: follows).in_batches.update_all(account_id: @target_account.id) num_moved += follows.update_all(target_account_id: @target_account.id) + + # Clear any relationship cache, since callbacks are not called + Rails.cache.delete_multi(follows.flat_map do |follow| + [ + ['relationship', follow.account_id, follow.target_account_id], + ['relationship', follow.target_account_id, follow.account_id], + ['relationship', follow.account_id, @target_account.id], + ['relationship', @target_account.id, follow.account_id], + ] + end) end num_moved diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 8639dadaf5..e522478b04 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -521,7 +521,7 @@ RSpec.describe ActivityPub::Activity::Create do end end - context 'with a reply' do + context 'with a reply without explicitly setting a conversation' do let(:original_status) { Fabricate(:status) } let(:object_json) do @@ -543,6 +543,30 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'with a reply explicitly setting a conversation' do + let(:original_status) { Fabricate(:status) } + + let(:object_json) do + build_object( + inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status), + conversation: ActivityPub::TagManager.instance.uri_for(original_status.conversation), + context: ActivityPub::TagManager.instance.uri_for(original_status.conversation) + ) + end + + it 'creates status' do + expect { subject.perform }.to change(sender.statuses, :count).by(1) + + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.thread).to eq original_status + expect(status.reply?).to be true + expect(status.in_reply_to_account).to eq original_status.account + expect(status.conversation).to eq original_status.conversation + end + end + context 'with mentions' do let(:recipient) { Fabricate(:account) } diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 6cbb58055e..7571d03f04 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -612,14 +612,6 @@ RSpec.describe ActivityPub::TagManager do end end - describe '#uri_to_local_id' do - let(:account) { Fabricate(:account, id_scheme: :username_ap_id) } - - it 'returns the local ID' do - expect(subject.uri_to_local_id(subject.uri_for(account), :username)).to eq account.username - end - end - describe '#uris_to_local_accounts' do it 'returns the expected local accounts' do account = Fabricate(:account) diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 9d63c5f1fe..9dc1ece33e 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -258,6 +258,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do tag: [ { type: 'Hashtag', name: 'foo' }, { type: 'Hashtag', name: 'bar' }, + { type: 'Hashtag', name: '#2024' }, ], } end