From 88aed3c11a5951a6d7a2af041ef3ca7d9dcf3c6a Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 4 Dec 2025 15:10:48 +0100 Subject: [PATCH] Fix streamed quoted polls not being hydrated correctly (#37118) --- app/lib/status_cache_hydrator.rb | 37 +++++++++++++------------- spec/lib/status_cache_hydrator_spec.rb | 22 +++++++++++++++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/app/lib/status_cache_hydrator.rb b/app/lib/status_cache_hydrator.rb index be425e1291..b830e509bf 100644 --- a/app/lib/status_cache_hydrator.rb +++ b/app/lib/status_cache_hydrator.rb @@ -26,12 +26,7 @@ class StatusCacheHydrator def hydrate_non_reblog_payload(empty_payload, account_id, nested: false) empty_payload.tap do |payload| - fill_status_payload(payload, @status, account_id, nested:) - - if payload[:poll] - payload[:poll][:voted] = @status.account_id == account_id - payload[:poll][:own_votes] = [] - end + fill_status_payload(payload, @status, account_id, fresh: !nested, nested:) end end @@ -45,18 +40,7 @@ class StatusCacheHydrator # used to create the status, we need to hydrate it here too payload[:reblog][:application] = payload_reblog_application if payload[:reblog][:application].nil? && @status.reblog.account_id == account_id - fill_status_payload(payload[:reblog], @status.reblog, account_id, nested:) - - if payload[:reblog][:poll] - if @status.reblog.account_id == account_id - payload[:reblog][:poll][:voted] = true - payload[:reblog][:poll][:own_votes] = [] - else - own_votes = PollVote.where(poll_id: @status.reblog.poll_id, account_id: account_id).pluck(:choice) - payload[:reblog][:poll][:voted] = !own_votes.empty? - payload[:reblog][:poll][:own_votes] = own_votes - end - end + fill_status_payload(payload[:reblog], @status.reblog, account_id, fresh: false, nested:) payload[:filtered] = payload[:reblog][:filtered] payload[:favourited] = payload[:reblog][:favourited] @@ -65,7 +49,7 @@ class StatusCacheHydrator end end - def fill_status_payload(payload, status, account_id, nested: false) + def fill_status_payload(payload, status, account_id, nested: false, fresh: true) payload[:favourited] = Favourite.exists?(account_id: account_id, status_id: status.id) payload[:reblogged] = Status.exists?(account_id: account_id, reblog_of_id: status.id) payload[:muted] = ConversationMute.exists?(account_id: account_id, conversation_id: status.conversation_id) @@ -76,6 +60,21 @@ class StatusCacheHydrator payload[:quote_approval][:current_user] = status.quote_policy_for_account(Account.find_by(id: account_id)) if payload[:quote_approval] payload[:quote] = hydrate_quote_payload(payload[:quote], status.quote, account_id, nested:) if payload[:quote] + if payload[:poll] + if fresh + # If the status is brand new, we don't need to look up votes in database + payload[:poll][:voted] = status.account_id == account_id + payload[:poll][:own_votes] = [] + elsif status.account_id == account_id + payload[:poll][:voted] = true + payload[:poll][:own_votes] = [] + else + own_votes = PollVote.where(poll_id: status.poll_id, account_id: account_id).pluck(:choice) + payload[:poll][:voted] = !own_votes.empty? + payload[:poll][:own_votes] = own_votes + end + end + # Nested statuses are more likely to have a stale cache fill_status_stats(payload, status) if nested end diff --git a/spec/lib/status_cache_hydrator_spec.rb b/spec/lib/status_cache_hydrator_spec.rb index 085866ef1d..8ad4e5614e 100644 --- a/spec/lib/status_cache_hydrator_spec.rb +++ b/spec/lib/status_cache_hydrator_spec.rb @@ -176,6 +176,28 @@ RSpec.describe StatusCacheHydrator do end end + context 'when the quoted post has a poll authored by the user' do + let(:poll) { Fabricate(:poll, account: account) } + let(:quoted_status) { Fabricate(:status, poll: poll, account: account) } + + it 'renders the same attributes as a full render' do + expect(subject).to eql(compare_to_hash) + end + end + + context 'when the quoted post has been voted in' do + let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) } + let(:quoted_status) { Fabricate(:status, poll: poll) } + + before do + VoteService.new.call(account, poll, [0]) + end + + it 'renders the same attributes as a full render' do + expect(subject).to eql(compare_to_hash) + end + end + context 'when the quoted post matches account filters' do let(:quoted_status) { Fabricate(:status, text: 'this toot is about that banned word') }