Remove unfollowed hashtag posts from home feed (#26028)
This commit is contained in:
		
							parent
							
								
									f18618d7f9
								
							
						
					
					
						commit
						943f27f437
					
				
					 4 changed files with 81 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -19,6 +19,7 @@ class Api::V1::TagsController < Api::BaseController
 | 
			
		|||
 | 
			
		||||
  def unfollow
 | 
			
		||||
    TagFollow.find_by(account: current_account, tag: @tag)&.destroy!
 | 
			
		||||
    TagUnmergeWorker.perform_async(@tag.id, current_account.id)
 | 
			
		||||
    render json: @tag, serializer: REST::TagSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,6 +180,26 @@ class FeedManager
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Remove a tag's statuses from a home feed
 | 
			
		||||
  # @param [Tag] from_tag
 | 
			
		||||
  # @param [Account] into_account
 | 
			
		||||
  # @return [void]
 | 
			
		||||
  def unmerge_tag_from_home(from_tag, into_account)
 | 
			
		||||
    timeline_key        = key(:home, into_account.id)
 | 
			
		||||
    timeline_status_ids = redis.zrange(timeline_key, 0, -1)
 | 
			
		||||
 | 
			
		||||
    # This is a bit tricky because we need posts tagged with this hashtag that are not
 | 
			
		||||
    # also tagged with another followed hashtag or from a followed user
 | 
			
		||||
    scope = from_tag.statuses
 | 
			
		||||
                    .where(id: timeline_status_ids)
 | 
			
		||||
                    .where.not(account: into_account.following)
 | 
			
		||||
                    .tagged_with_none(TagFollow.where(account: into_account).pluck(:tag_id))
 | 
			
		||||
 | 
			
		||||
    scope.select('id, reblog_of_id').reorder(nil).find_each do |status|
 | 
			
		||||
      remove_from_feed(:home, into_account.id, status, aggregate_reblogs: into_account.user&.aggregates_reblogs?)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Clear all statuses from or mentioning target_account from a home feed
 | 
			
		||||
  # @param [Account] account
 | 
			
		||||
  # @param [Account] target_account
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								app/workers/tag_unmerge_worker.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/workers/tag_unmerge_worker.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class TagUnmergeWorker
 | 
			
		||||
  include Sidekiq::Worker
 | 
			
		||||
  include DatabaseHelper
 | 
			
		||||
 | 
			
		||||
  sidekiq_options queue: 'pull'
 | 
			
		||||
 | 
			
		||||
  def perform(from_tag_id, into_account_id)
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @from_tag     = Tag.find(from_tag_id)
 | 
			
		||||
      @into_account = Account.find(into_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.unmerge_tag_from_home(@from_tag, @into_account)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										39
									
								
								spec/workers/tag_unmerge_worker_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								spec/workers/tag_unmerge_worker_spec.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe TagUnmergeWorker do
 | 
			
		||||
  subject { described_class.new }
 | 
			
		||||
 | 
			
		||||
  describe 'perform' do
 | 
			
		||||
    let(:follower)                { Fabricate(:account) }
 | 
			
		||||
    let(:followed)                { Fabricate(:account) }
 | 
			
		||||
    let(:followed_tag)            { Fabricate(:tag) }
 | 
			
		||||
    let(:unchanged_followed_tag)  { Fabricate(:tag) }
 | 
			
		||||
    let(:status_from_followed)    { Fabricate(:status, created_at: 2.hours.ago, account: followed) }
 | 
			
		||||
    let(:tagged_status)           { Fabricate(:status, created_at: 1.hour.ago) }
 | 
			
		||||
    let(:unchanged_tagged_status) { Fabricate(:status) }
 | 
			
		||||
 | 
			
		||||
    before do
 | 
			
		||||
      tagged_status.tags << followed_tag
 | 
			
		||||
      unchanged_tagged_status.tags << followed_tag
 | 
			
		||||
      unchanged_tagged_status.tags << unchanged_followed_tag
 | 
			
		||||
 | 
			
		||||
      tag_follow = TagFollow.create_with(rate_limit: false).find_or_create_by!(tag: followed_tag, account: follower)
 | 
			
		||||
      TagFollow.create_with(rate_limit: false).find_or_create_by!(tag: unchanged_followed_tag, account: follower)
 | 
			
		||||
 | 
			
		||||
      FeedManager.instance.push_to_home(follower, status_from_followed, update: false)
 | 
			
		||||
      FeedManager.instance.push_to_home(follower, tagged_status, update: false)
 | 
			
		||||
      FeedManager.instance.push_to_home(follower, unchanged_tagged_status, update: false)
 | 
			
		||||
 | 
			
		||||
      tag_follow.destroy!
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'removes the expected status from the feed' do
 | 
			
		||||
      expect { subject.perform(followed_tag.id, follower.id) }
 | 
			
		||||
        .to change { HomeFeed.new(follower).get(10).pluck(:id) }
 | 
			
		||||
        .from([unchanged_tagged_status.id, tagged_status.id, status_from_followed.id])
 | 
			
		||||
        .to([unchanged_tagged_status.id, status_from_followed.id])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
		Reference in a new issue