Change lists to be able to include accounts with pending follow requests (#19727)

This commit is contained in:
Claire 2023-05-02 14:40:36 +02:00 committed by GitHub
parent 598e63dad2
commit 6693a4fe7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 19 deletions

View File

@ -272,6 +272,7 @@ module AccountInteractions
def lists_for_local_distribution def lists_for_local_distribution
lists.joins(account: :user) lists.joins(account: :user)
.where.not(list_accounts: { follow_id: nil })
.where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago) .where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
end end

View File

@ -32,7 +32,8 @@ class FollowRequest < ApplicationRecord
validates :languages, language: true validates :languages, language: true
def authorize! def authorize!
account.follow!(target_account, reblogs: show_reblogs, notify: notify, languages: languages, uri: uri, bypass_limit: true) 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) # rubocop:disable Rails/SkipsModelValidations
MergeWorker.perform_async(target_account.id, account.id) if account.local? MergeWorker.perform_async(target_account.id, account.id) if account.local?
destroy! destroy!
end end

View File

@ -4,16 +4,18 @@
# #
# Table name: list_accounts # Table name: list_accounts
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# list_id :bigint(8) not null # list_id :bigint(8) not null
# account_id :bigint(8) not null # account_id :bigint(8) not null
# follow_id :bigint(8) # follow_id :bigint(8)
# follow_request_id :bigint(8)
# #
class ListAccount < ApplicationRecord class ListAccount < ApplicationRecord
belongs_to :list belongs_to :list
belongs_to :account belongs_to :account
belongs_to :follow, optional: true belongs_to :follow, optional: true
belongs_to :follow_request, optional: true
validates :account_id, uniqueness: { scope: :list_id } validates :account_id, uniqueness: { scope: :list_id }
@ -22,6 +24,10 @@ class ListAccount < ApplicationRecord
private private
def set_follow def set_follow
self.follow = Follow.find_by!(account_id: list.account_id, target_account_id: account.id) unless list.account_id == account.id return if list.account_id == account.id
self.follow = Follow.find_by!(account_id: list.account_id, target_account_id: account.id)
rescue ActiveRecord::RecordNotFound
self.follow_request = FollowRequest.find_by!(account_id: list.account_id, target_account_id: account.id)
end end
end end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddFollowRequestIdToListAccounts < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def change
safety_assured { add_reference :list_accounts, :follow_request, foreign_key: { on_delete: :cascade }, index: false }
add_index :list_accounts, :follow_request_id, algorithm: :concurrently, where: 'follow_request_id IS NOT NULL'
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_03_30_140036) do ActiveRecord::Schema.define(version: 2023_03_30_155710) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -554,8 +554,10 @@ ActiveRecord::Schema.define(version: 2023_03_30_140036) do
t.bigint "list_id", null: false t.bigint "list_id", null: false
t.bigint "account_id", null: false t.bigint "account_id", null: false
t.bigint "follow_id" t.bigint "follow_id"
t.bigint "follow_request_id"
t.index ["account_id", "list_id"], name: "index_list_accounts_on_account_id_and_list_id", unique: true t.index ["account_id", "list_id"], name: "index_list_accounts_on_account_id_and_list_id", unique: true
t.index ["follow_id"], name: "index_list_accounts_on_follow_id", where: "(follow_id IS NOT NULL)" t.index ["follow_id"], name: "index_list_accounts_on_follow_id", where: "(follow_id IS NOT NULL)"
t.index ["follow_request_id"], name: "index_list_accounts_on_follow_request_id", where: "(follow_request_id IS NOT NULL)"
t.index ["list_id", "account_id"], name: "index_list_accounts_on_list_id_and_account_id" t.index ["list_id", "account_id"], name: "index_list_accounts_on_list_id_and_account_id"
end end
@ -1198,6 +1200,7 @@ ActiveRecord::Schema.define(version: 2023_03_30_140036) do
add_foreign_key "imports", "accounts", name: "fk_6db1b6e408", on_delete: :cascade add_foreign_key "imports", "accounts", name: "fk_6db1b6e408", on_delete: :cascade
add_foreign_key "invites", "users", on_delete: :cascade add_foreign_key "invites", "users", on_delete: :cascade
add_foreign_key "list_accounts", "accounts", on_delete: :cascade add_foreign_key "list_accounts", "accounts", on_delete: :cascade
add_foreign_key "list_accounts", "follow_requests", on_delete: :cascade
add_foreign_key "list_accounts", "follows", on_delete: :cascade add_foreign_key "list_accounts", "follows", on_delete: :cascade
add_foreign_key "list_accounts", "lists", on_delete: :cascade add_foreign_key "list_accounts", "lists", on_delete: :cascade
add_foreign_key "lists", "accounts", on_delete: :cascade add_foreign_key "lists", "accounts", on_delete: :cascade

View File

@ -29,17 +29,48 @@ describe Api::V1::Lists::AccountsController do
let(:scopes) { 'write:lists' } let(:scopes) { 'write:lists' }
let(:bob) { Fabricate(:account, username: 'bob') } let(:bob) { Fabricate(:account, username: 'bob') }
before do context 'when the added account is followed' do
user.account.follow!(bob) before do
post :create, params: { list_id: list.id, account_ids: [bob.id] } user.account.follow!(bob)
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'adds account to the list' do
expect(list.accounts.include?(bob)).to be true
end
end end
it 'returns http success' do context 'when the added account has been sent a follow request' do
expect(response).to have_http_status(200) before do
user.account.follow_requests.create!(target_account: bob)
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'adds account to the list' do
expect(list.accounts.include?(bob)).to be true
end
end end
it 'adds account to the list' do context 'when the added account is not followed' do
expect(list.accounts.include?(bob)).to be true before do
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
it 'does not add the account to the list' do
expect(list.accounts.include?(bob)).to be false
end
end end
end end

View File

@ -683,4 +683,28 @@ describe AccountInteractions do
end end
end end
end end
describe '#lists_for_local_distribution' do
let!(:inactive_follower_user) { Fabricate(:user, current_sign_in_at: 5.years.ago) }
let!(:follower_user) { Fabricate(:user, current_sign_in_at: Time.now.utc) }
let!(:follow_request_user) { Fabricate(:user, current_sign_in_at: Time.now.utc) }
let!(:inactive_follower_list) { Fabricate(:list, account: inactive_follower_user.account) }
let!(:follower_list) { Fabricate(:list, account: follower_user.account) }
let!(:follow_request_list) { Fabricate(:list, account: follow_request_user.account) }
before do
inactive_follower_user.account.follow!(account)
follower_user.account.follow!(account)
follow_request_user.account.follow_requests.create!(target_account: account)
inactive_follower_list.accounts << account
follower_list.accounts << account
follow_request_list.accounts << account
end
it 'includes only the list from the active follower' do
expect(account.lists_for_local_distribution.to_a).to eq [follower_list]
end
end
end end

View File

@ -4,13 +4,27 @@ require 'rails_helper'
RSpec.describe FollowRequest, type: :model do RSpec.describe FollowRequest, type: :model do
describe '#authorize!' do describe '#authorize!' do
let(:follow_request) { Fabricate(:follow_request, account: account, target_account: target_account) } let!(:follow_request) { Fabricate(:follow_request, account: account, target_account: target_account) }
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) } let(:target_account) { Fabricate(:account) }
context 'when the to-be-followed person has been added to a list' do
let!(:list) { Fabricate(:list, account: account) }
before do
list.accounts << target_account
end
it 'updates the ListAccount' do
expect { follow_request.authorize! }.to change { [list.list_accounts.first.follow_request_id, list.list_accounts.first.follow_id] }.from([follow_request.id, nil]).to([nil, anything])
end
end
it 'calls Account#follow!, MergeWorker.perform_async, and #destroy!' do it 'calls Account#follow!, MergeWorker.perform_async, and #destroy!' do
expect(account).to receive(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri, languages: nil, bypass_limit: true) expect(account).to receive(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri, languages: nil, bypass_limit: true) do
expect(MergeWorker).to receive(:perform_async).with(target_account.id, account.id) account.active_relationships.create!(target_account: target_account)
end
expect(MergeWorker).to receive(:perform_async).with(target_account.id, account.id)
expect(follow_request).to receive(:destroy!) expect(follow_request).to receive(:destroy!)
follow_request.authorize! follow_request.authorize!
end end
@ -29,4 +43,22 @@ RSpec.describe FollowRequest, type: :model do
expect(follow_request.account.muting_reblogs?(target)).to be true expect(follow_request.account.muting_reblogs?(target)).to be true
end end
end end
describe '#reject!' do
let!(:follow_request) { Fabricate(:follow_request, account: account, target_account: target_account) }
let(:account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) }
context 'when the to-be-followed person has been added to a list' do
let!(:list) { Fabricate(:list, account: account) }
before do
list.accounts << target_account
end
it 'deletes the ListAccount record' do
expect { follow_request.reject! }.to change { list.accounts.count }.from(1).to(0)
end
end
end
end end