From 4b61b8706ee61e04a8b72d728f71b152a7c16c62 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 8 Apr 2025 11:14:59 +0200 Subject: [PATCH] [WiP] Record quote posts in post history --- .../concerns/status/snapshot_concern.rb | 1 + app/models/status_edit.rb | 1 + .../rest/status_edit_serializer.rb | 6 +++++ .../process_status_update_service.rb | 23 +++++++++++-------- ...50411095859_add_quote_id_to_status_edit.rb | 7 ++++++ db/schema.rb | 3 ++- .../process_status_update_service_spec.rb | 14 +++++++---- 7 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20250411095859_add_quote_id_to_status_edit.rb diff --git a/app/models/concerns/status/snapshot_concern.rb b/app/models/concerns/status/snapshot_concern.rb index 02897109044..269545ce8b9 100644 --- a/app/models/concerns/status/snapshot_concern.rb +++ b/app/models/concerns/status/snapshot_concern.rb @@ -25,6 +25,7 @@ module Status::SnapshotConcern poll_options: preloadable_poll&.options&.dup, account_id: account_id || self.account_id, created_at: at_time || edited_at, + quote_id: quote&.id, rate_limit: rate_limit ) end diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 6e25a6f3bbd..a64ef349054 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -15,6 +15,7 @@ # media_descriptions :text is an Array # poll_options :string is an Array # sensitive :boolean +# quote_id :bigint(8) # class StatusEdit < ApplicationRecord diff --git a/app/serializers/rest/status_edit_serializer.rb b/app/serializers/rest/status_edit_serializer.rb index f7a48797d1a..30e318a6aa2 100644 --- a/app/serializers/rest/status_edit_serializer.rb +++ b/app/serializers/rest/status_edit_serializer.rb @@ -10,6 +10,8 @@ class REST::StatusEditSerializer < ActiveModel::Serializer has_many :ordered_media_attachments, key: :media_attachments, serializer: REST::MediaAttachmentSerializer has_many :emojis, serializer: REST::CustomEmojiSerializer + has_one :quote, serializer: REST::QuoteSerializer, if: -> { object.quote_id.present? } + attribute :poll, if: -> { object.poll_options.present? } def content @@ -19,4 +21,8 @@ class REST::StatusEditSerializer < ActiveModel::Serializer def poll { options: object.poll_options.map { |title| { title: title } } } end + + def quote + object.quote_id == status.quote&.id ? status.quote : Quote.new(state: :pending) + end end diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index d7cc16cfb0d..85b9f4ca023 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -16,6 +16,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @account = status.account @media_attachments_changed = false @poll_changed = false + @quote_changed = false @request_id = request_id # Only native types can be updated at the moment @@ -164,7 +165,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @status.sensitive = @account.sensitized? || @status_parser.sensitive || false @status.language = @status_parser.language - @significant_changes = text_significantly_changed? || @status.spoiler_text_changed? || @media_attachments_changed || @poll_changed + @significant_changes = text_significantly_changed? || @status.spoiler_text_changed? || @media_attachments_changed || @poll_changed || @quote_changed @status.edited_at = @status_parser.edited_at if significant_changes? @@ -282,19 +283,24 @@ class ActivityPub::ProcessStatusUpdateService < BaseService quote_uri = tag['href'] if @status.quote.present? - quote = @status.quote - - quote.update(quoted_status: nil, state: :pending) if quote.quoted_status.present? && ActivityPub::TagManager.instance.uri_for(quote.quoted_status) != quote_uri - quote.update(approval_uri: unsupported_uri_scheme?(tag['approvedBy']) ? nil : tag['approvedBy'], state: :pending) if quote.approval_uri != tag['approvedBy'] + # If the quoted post has changed, discard the old object and create a new one + if @status.quote.quoted_status.present? && ActivityPub::TagManager.instance.uri_for(@status.quote.quoted_status) != quote_uri + @status.quote.destroy + quote = Quote.create(status: @status, approval_uri: unsupported_uri_scheme?(tag['approvedBy']) ? nil : tag['approvedBy']) + @quote_changed = true + else + quote = @status.quote + quote.update(approval_uri: unsupported_uri_scheme?(tag['approvedBy']) ? nil : tag['approvedBy'], state: :pending) if quote.approval_uri != tag['approvedBy'] + end else - quote = Quote.new(status: @status, approval_uri: unsupported_uri_scheme?(tag['approvedBy']) ? nil : tag['approvedBy']) + quote = Quote.create(status: @status, approval_uri: unsupported_uri_scheme?(tag['approvedBy']) ? nil : tag['approvedBy']) + @quote_changed = true end break end if quote.present? - quote.save! begin ActivityPub::VerifyQuoteService.new.call(quote, fetchable_quoted_uri: quote_uri) quote.save! @@ -303,9 +309,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService end elsif @status.quote.present? @status.quote.destroy! + @quote_changed = true end - - # TODO: quote in snapshot? end def update_counts! diff --git a/db/migrate/20250411095859_add_quote_id_to_status_edit.rb b/db/migrate/20250411095859_add_quote_id_to_status_edit.rb new file mode 100644 index 00000000000..f5bb2f812a5 --- /dev/null +++ b/db/migrate/20250411095859_add_quote_id_to_status_edit.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddQuoteIdToStatusEdit < ActiveRecord::Migration[8.0] + def change + add_column :status_edits, :quote_id, :bigint, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 760ff99fd95..6102bd9035c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_04_11_095029) do +ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -1025,6 +1025,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_11_095029) do t.text "media_descriptions", array: true t.string "poll_options", array: true t.boolean "sensitive" + t.bigint "quote_id" t.index ["account_id"], name: "index_status_edits_on_account_id" t.index ["status_id"], name: "index_status_edits_on_status_id" end diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 7a9c36d2c02..afa6afc9367 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -733,8 +733,10 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do it 'updates the URI and unverifies the quote' do expect { subject.call(status, json, json) } - .to change(quote, :quoted_status).from(quoted_status).to(second_quoted_status) - .and change(quote, :state).from('accepted') + .to change { status.quote.quoted_status }.from(quoted_status).to(second_quoted_status) + .and change { status.quote.state }.from('accepted') + + expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound) end end @@ -799,9 +801,11 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do it 'updates the URI and unverifies the quote' do expect { subject.call(status, json, json) } - .to change(quote, :quoted_status).from(quoted_status).to(second_quoted_status) - .and change(quote, :approval_uri).from(approval_uri).to(second_approval_uri) - .and not_change(quote, :state) + .to change { status.quote.quoted_status }.from(quoted_status).to(second_quoted_status) + .and change { status.quote.approval_uri }.from(approval_uri).to(second_approval_uri) + .and(not_change { status.quote.state }) + + expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound) end end