Change lists to reflect added and removed users retroactively (#32930)
This commit is contained in:
		@@ -15,17 +15,12 @@ class Api::V1::Lists::AccountsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    ApplicationRecord.transaction do
 | 
			
		||||
      list_accounts.each do |account|
 | 
			
		||||
        @list.accounts << account
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    AddAccountsToListService.new.call(@list, Account.find(account_ids))
 | 
			
		||||
    render_empty
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def destroy
 | 
			
		||||
    ListAccount.where(list: @list, account_id: account_ids).destroy_all
 | 
			
		||||
    RemoveAccountsFromListService.new.call(@list, Account.where(id: account_ids))
 | 
			
		||||
    render_empty
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -43,10 +38,6 @@ class Api::V1::Lists::AccountsController < Api::BaseController
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def list_accounts
 | 
			
		||||
    Account.find(account_ids)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def account_ids
 | 
			
		||||
    Array(resource_params[:account_ids])
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,15 @@ class FollowRequest < ApplicationRecord
 | 
			
		||||
 | 
			
		||||
  def authorize!
 | 
			
		||||
    follow = account.follow!(target_account, reblogs: show_reblogs, notify: notify, languages: languages, uri: uri, bypass_limit: true)
 | 
			
		||||
    ListAccount.where(follow_request: self).update_all(follow_request_id: nil, follow_id: follow.id)
 | 
			
		||||
    MergeWorker.perform_async(target_account.id, account.id) if account.local?
 | 
			
		||||
 | 
			
		||||
    if account.local?
 | 
			
		||||
      ListAccount.where(follow_request: self).update_all(follow_request_id: nil, follow_id: follow.id)
 | 
			
		||||
      MergeWorker.perform_async(target_account.id, account.id, 'home')
 | 
			
		||||
      MergeWorker.push_bulk(List.where(account: account).joins(:list_accounts).where(list_accounts: { account_id: target_account.id }).pluck(:id)) do |list_id|
 | 
			
		||||
        [target_account.id, list_id, 'list']
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    destroy!
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								app/services/add_accounts_to_list_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/services/add_accounts_to_list_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class AddAccountsToListService < BaseService
 | 
			
		||||
  def call(list, accounts)
 | 
			
		||||
    @list = list
 | 
			
		||||
    @accounts = accounts
 | 
			
		||||
 | 
			
		||||
    return if @accounts.empty?
 | 
			
		||||
 | 
			
		||||
    update_list!
 | 
			
		||||
    merge_into_list!
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def update_list!
 | 
			
		||||
    ApplicationRecord.transaction do
 | 
			
		||||
      @accounts.each do |account|
 | 
			
		||||
        @list.accounts << account
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def merge_into_list!
 | 
			
		||||
    MergeWorker.push_bulk(merge_account_ids) do |account_id|
 | 
			
		||||
      [account_id, @list.id, 'list']
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def merge_account_ids
 | 
			
		||||
    ListAccount.where(list: @list, account: @accounts).where.not(follow_id: nil).pluck(:account_id)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -81,7 +81,10 @@ class FollowService < BaseService
 | 
			
		||||
    follow = @source_account.follow!(@target_account, **follow_options.merge(rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit]))
 | 
			
		||||
 | 
			
		||||
    LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, 'follow')
 | 
			
		||||
    MergeWorker.perform_async(@target_account.id, @source_account.id)
 | 
			
		||||
    MergeWorker.perform_async(@target_account.id, @source_account.id, 'home')
 | 
			
		||||
    MergeWorker.push_bulk(List.where(account: @source_account).joins(:list_accounts).where(list_accounts: { account_id: @target_account.id }).pluck(:id)) do |list_id|
 | 
			
		||||
      [@target_account.id, list_id, 'list']
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    follow
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								app/services/remove_accounts_from_list_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/services/remove_accounts_from_list_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class RemoveAccountsFromListService < BaseService
 | 
			
		||||
  def call(list, accounts)
 | 
			
		||||
    @list = list
 | 
			
		||||
    @accounts = accounts
 | 
			
		||||
 | 
			
		||||
    return if @accounts.empty?
 | 
			
		||||
 | 
			
		||||
    unmerge_from_list!
 | 
			
		||||
    update_list!
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def update_list!
 | 
			
		||||
    ListAccount.where(list: @list, account: @accounts).destroy_all
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unmerge_from_list!
 | 
			
		||||
    UnmergeWorker.push_bulk(unmerge_account_ids) do |account_id|
 | 
			
		||||
      [account_id, @list.id, 'list']
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unmerge_account_ids
 | 
			
		||||
    ListAccount.where(list: @list, account: @accounts).where.not(follow_id: nil).pluck(:account_id)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -31,7 +31,13 @@ class UnfollowService < BaseService
 | 
			
		||||
 | 
			
		||||
    create_notification(follow) if !@target_account.local? && @target_account.activitypub?
 | 
			
		||||
    create_reject_notification(follow) if @target_account.local? && !@source_account.local? && @source_account.activitypub?
 | 
			
		||||
    UnmergeWorker.perform_async(@target_account.id, @source_account.id) unless @options[:skip_unmerge]
 | 
			
		||||
 | 
			
		||||
    unless @options[:skip_unmerge]
 | 
			
		||||
      UnmergeWorker.perform_async(@target_account.id, @source_account.id, 'home')
 | 
			
		||||
      UnmergeWorker.push_bulk(List.where(account: @source_account).joins(:list_accounts).where(list_accounts: { account_id: @target_account.id }).pluck(:list_id)) do |list_id|
 | 
			
		||||
        [@target_account.id, list_id, 'list']
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    follow
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,12 @@ class UnmuteService < BaseService
 | 
			
		||||
 | 
			
		||||
    account.unmute!(target_account)
 | 
			
		||||
 | 
			
		||||
    MergeWorker.perform_async(target_account.id, account.id) if account.following?(target_account)
 | 
			
		||||
    if account.following?(target_account)
 | 
			
		||||
      MergeWorker.perform_async(target_account.id, account.id, 'home')
 | 
			
		||||
 | 
			
		||||
      MergeWorker.push_bulk(List.where(account: account).joins(:list_accounts).where(list_accounts: { account_id: target_account.id }).pluck(:id)) do |list_id|
 | 
			
		||||
        [target_account.id, list_id, 'list']
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,42 @@ class MergeWorker
 | 
			
		||||
  include Redisable
 | 
			
		||||
  include DatabaseHelper
 | 
			
		||||
 | 
			
		||||
  def perform(from_account_id, into_account_id)
 | 
			
		||||
  def perform(from_account_id, into_id, type = 'home')
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @from_account = Account.find(from_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    case type
 | 
			
		||||
    when 'home'
 | 
			
		||||
      merge_into_home!(into_id)
 | 
			
		||||
    when 'list'
 | 
			
		||||
      merge_into_list!(into_id)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def merge_into_home!(into_account_id)
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @into_account = Account.find(into_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.merge_into_home(@from_account, @into_account)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  ensure
 | 
			
		||||
    redis.del("account:#{into_account_id}:regeneration")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def merge_into_list!(into_list_id)
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @into_list = List.find(into_list_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.merge_into_list(@from_account, @into_list)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,18 @@
 | 
			
		||||
 | 
			
		||||
class MuteWorker
 | 
			
		||||
  include Sidekiq::Worker
 | 
			
		||||
  include DatabaseHelper
 | 
			
		||||
 | 
			
		||||
  def perform(account_id, target_account_id)
 | 
			
		||||
    FeedManager.instance.clear_from_home(Account.find(account_id), Account.find(target_account_id))
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @account = Account.find(account_id)
 | 
			
		||||
      @target_account = Account.find(target_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.clear_from_home(@account, @target_account)
 | 
			
		||||
      FeedManager.instance.clear_from_lists(@account, @target_account)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -6,16 +6,40 @@ class UnmergeWorker
 | 
			
		||||
 | 
			
		||||
  sidekiq_options queue: 'pull'
 | 
			
		||||
 | 
			
		||||
  def perform(from_account_id, into_account_id)
 | 
			
		||||
  def perform(from_account_id, into_id, type = 'home')
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @from_account = Account.find(from_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    case type
 | 
			
		||||
    when 'home'
 | 
			
		||||
      unmerge_from_home!(into_id)
 | 
			
		||||
    when 'list'
 | 
			
		||||
      unmerge_from_list!(into_id)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def unmerge_from_home!(into_account_id)
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @into_account = Account.find(into_account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.unmerge_from_home(@from_account, @into_account)
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unmerge_from_list!(into_list_id)
 | 
			
		||||
    with_primary do
 | 
			
		||||
      @into_list = List.find(into_list_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    with_read_replica do
 | 
			
		||||
      FeedManager.instance.unmerge_from_list(@from_account, @into_list)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ RSpec.describe FollowRequest do
 | 
			
		||||
      follow_request.authorize!
 | 
			
		||||
 | 
			
		||||
      expect(account).to have_received(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri, languages: nil, bypass_limit: true)
 | 
			
		||||
      expect(MergeWorker).to have_received(:perform_async).with(target_account.id, account.id)
 | 
			
		||||
      expect(MergeWorker).to have_received(:perform_async).with(target_account.id, account.id, 'home')
 | 
			
		||||
      expect(follow_request).to have_received(:destroy!)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ RSpec.describe UnmuteService do
 | 
			
		||||
        it 'removes the account mute and sets up a merge' do
 | 
			
		||||
          expect { subject.call(account, target_account) }
 | 
			
		||||
            .to remove_account_mute
 | 
			
		||||
          expect(MergeWorker).to have_enqueued_sidekiq_job(target_account.id, account.id)
 | 
			
		||||
          expect(MergeWorker).to have_enqueued_sidekiq_job(target_account.id, account.id, 'home')
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user