Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix blocking someone not clearing up list feeds #16205

Merged
merged 1 commit into from
May 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions app/lib/feed_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,36 @@ def clear_from_home(account, target_account)
end
end

# Clear all statuses from or mentioning target_account from a list feed
# @param [List] list
# @param [Account] target_account
# @return [void]
def clear_from_list(list, target_account)
timeline_key = key(:list, list.id)
timeline_status_ids = redis.zrange(timeline_key, 0, -1)
statuses = Status.where(id: timeline_status_ids).select(:id, :reblog_of_id, :account_id).to_a
reblogged_ids = Status.where(id: statuses.map(&:reblog_of_id).compact, account: target_account).pluck(:id)
with_mentions_ids = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact, account: target_account).pluck(:status_id)

target_statuses = statuses.select do |status|
status.account_id == target_account.id || reblogged_ids.include?(status.reblog_of_id) || with_mentions_ids.include?(status.id) || with_mentions_ids.include?(status.reblog_of_id)
end

target_statuses.each do |status|
unpush_from_list(list, status)
end
end

# Clear all statuses from or mentioning target_account from an account's lists
# @param [Account] account
# @param [Account] target_account
# @return [void]
def clear_from_lists(account, target_account)
List.where(account: account).each do |list|
clear_from_list(list, target_account)
end
end

# Populate home feed of account from scratch
# @param [Account] account
# @return [void]
Expand Down
5 changes: 5 additions & 0 deletions app/services/after_block_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def call(account, target_account)
@target_account = target_account

clear_home_feed!
clear_list_feeds!
clear_notifications!
clear_conversations!
end
Expand All @@ -16,6 +17,10 @@ def clear_home_feed!
FeedManager.instance.clear_from_home(@account, @target_account)
end

def clear_list_feeds!
FeedManager.instance.clear_from_lists(@account, @target_account)
end

def clear_conversations!
AccountConversation.where(account: @account).where('? = ANY(participant_account_ids)', @target_account.id).in_batches.destroy_all
end
Expand Down
32 changes: 27 additions & 5 deletions spec/services/after_block_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
-> { described_class.new.call(account, target_account) }
end

let(:account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) }
let(:account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: target_account) }
let(:other_status) { Fabricate(:status, account: target_account) }
let(:other_account_status) { Fabricate(:status) }
let(:other_account_reblog) { Fabricate(:status, reblog_of_id: other_status.id) }

describe 'home timeline' do
let(:status) { Fabricate(:status, account: target_account) }
let(:other_account_status) { Fabricate(:status) }
let(:home_timeline_key) { FeedManager.instance.key(:home, account.id) }

before do
Expand All @@ -20,10 +22,30 @@
it "clears account's statuses" do
FeedManager.instance.push_to_home(account, status)
FeedManager.instance.push_to_home(account, other_account_status)
FeedManager.instance.push_to_home(account, other_account_reblog)

is_expected.to change {
Redis.current.zrange(home_timeline_key, 0, -1)
}.from([status.id.to_s, other_account_status.id.to_s]).to([other_account_status.id.to_s])
}.from([status.id.to_s, other_account_status.id.to_s, other_account_reblog.id.to_s]).to([other_account_status.id.to_s])
end
end

describe 'lists' do
let(:list) { Fabricate(:list, account: account) }
let(:list_timeline_key) { FeedManager.instance.key(:list, list.id) }

before do
Redis.current.del(list_timeline_key)
end

it "clears account's statuses" do
FeedManager.instance.push_to_list(list, status)
FeedManager.instance.push_to_list(list, other_account_status)
FeedManager.instance.push_to_list(list, other_account_reblog)

is_expected.to change {
Redis.current.zrange(list_timeline_key, 0, -1)
}.from([status.id.to_s, other_account_status.id.to_s, other_account_reblog.id.to_s]).to([other_account_status.id.to_s])
end
end
end