Add the ability to make status reference notifications followee-only

This commit is contained in:
noellabo 2022-09-11 23:03:39 +09:00
parent 91910d7d2d
commit ccafeaf32f
11 changed files with 116 additions and 4 deletions

View file

@ -96,7 +96,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_default_search_searchability, :setting_default_search_searchability,
:setting_show_reload_button, :setting_show_reload_button,
notification_emails: %i(follow follow_request reblog favourite emoji_reaction status_reference mention digest report pending_account trending_tag), notification_emails: %i(follow follow_request reblog favourite emoji_reaction status_reference mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm must_be_dm_to_send_email) interactions: %i(must_be_follower must_be_following must_be_following_dm must_be_dm_to_send_email must_be_following_reference)
) )
end end

View file

@ -75,7 +75,7 @@ const initialState = ImmutableMap({
poll: true, poll: true,
status: true, status: true,
emoji_reaction: true, emoji_reaction: true,
status_reference: false, status_reference: true,
}), }),
sounds: ImmutableMap({ sounds: ImmutableMap({
@ -87,7 +87,7 @@ const initialState = ImmutableMap({
poll: true, poll: true,
status: true, status: true,
emoji_reaction: true, emoji_reaction: true,
status_reference: false, status_reference: true,
}), }),
}), }),

View file

@ -56,7 +56,7 @@ class StatusPolicy < ApplicationPolicy
def subscribe? def subscribe?
return false unless show? return false unless show?
!unlisted? || owned? || following_author? || mention_exists? public? || owned? || following_author? || mention_exists?
end end
private private
@ -69,6 +69,10 @@ class StatusPolicy < ApplicationPolicy
author.id == current_account&.id author.id == current_account&.id
end end
def public?
record.public_visibility?
end
def unlisted? def unlisted?
record.unlisted_visibility? record.unlisted_visibility?
end end

View file

@ -67,6 +67,10 @@ class NotifyService < BaseService
@recipient.user.settings.interactions['must_be_following'] && !following_sender? @recipient.user.settings.interactions['must_be_following'] && !following_sender?
end end
def optional_non_following_and_reference?
status_reference? && @recipient.user.settings.interactions['must_be_following_reference'] && !following_sender?
end
def optional_non_direct_message? def optional_non_direct_message?
message? && @recipient.user.settings.interactions['must_be_dm_to_send_email'] && !@notification.target_status.direct_visibility? message? && @recipient.user.settings.interactions['must_be_dm_to_send_email'] && !@notification.target_status.direct_visibility?
end end
@ -75,6 +79,10 @@ class NotifyService < BaseService
@notification.type == :mention @notification.type == :mention
end end
def status_reference?
@notification.type == :status_reference
end
def direct_message? def direct_message?
message? && @notification.target_status.direct_visibility? message? && @notification.target_status.direct_visibility?
end end
@ -162,6 +170,7 @@ class NotifyService < BaseService
blocked ||= hellbanned? # Hellban blocked ||= hellbanned? # Hellban
blocked ||= optional_non_follower? # Options blocked ||= optional_non_follower? # Options
blocked ||= optional_non_following? # Options blocked ||= optional_non_following? # Options
blocked ||= optional_non_following_and_reference? # Options
blocked ||= optional_non_following_and_direct? # Options blocked ||= optional_non_following_and_direct? # Options
blocked ||= conversation_muted? blocked ||= conversation_muted?
blocked ||= send("blocked_#{@notification.type}?") # Type-dependent filters blocked ||= send("blocked_#{@notification.type}?") # Type-dependent filters

View file

@ -38,3 +38,4 @@
= ff.input :must_be_following, as: :boolean, wrapper: :with_label = ff.input :must_be_following, as: :boolean, wrapper: :with_label
= ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label = ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label
= ff.input :must_be_dm_to_send_email, as: :boolean, wrapper: :with_label, fedibird_features: true = ff.input :must_be_dm_to_send_email, as: :boolean, wrapper: :with_label, fedibird_features: true
= ff.input :must_be_following_reference, as: :boolean, wrapper: :with_label, fedibird_features: true

View file

@ -307,6 +307,7 @@ en:
must_be_follower: Block notifications from non-followers must_be_follower: Block notifications from non-followers
must_be_following: Block notifications from people you don't follow must_be_following: Block notifications from people you don't follow
must_be_following_dm: Block direct messages from people you don't follow must_be_following_dm: Block direct messages from people you don't follow
must_be_following_reference: Block reference notifications from people you don't follow
invite: invite:
comment: Comment comment: Comment
invite_request: invite_request:

View file

@ -311,6 +311,7 @@ ja:
must_be_follower: フォロワー以外からの通知をブロック must_be_follower: フォロワー以外からの通知をブロック
must_be_following: フォローしていないユーザーからの通知をブロック must_be_following: フォローしていないユーザーからの通知をブロック
must_be_following_dm: フォローしていないユーザーからのダイレクトメッセージをブロック must_be_following_dm: フォローしていないユーザーからのダイレクトメッセージをブロック
must_be_following_reference: フォローしていないユーザーからの参照通知をブロック
invite: invite:
comment: コメント comment: コメント
invite_request: invite_request:

View file

@ -82,6 +82,7 @@ defaults: &defaults
must_be_follower: false must_be_follower: false
must_be_following: false must_be_following: false
must_be_following_dm: false must_be_following_dm: false
must_be_following_reference: true
reserved_usernames: reserved_usernames:
- admin - admin
- support - support

View file

@ -0,0 +1,4 @@
Fabricator(:status_reference) do
status_id nil
target_status_id nil
end

View file

@ -25,6 +25,37 @@ RSpec.describe StatusPolicy, type: :model do
end end
end end
permissions :subscribe? do
it 'grants access when public and account is viewer' do
viewer = Fabricate(:account)
status.visibility = :public
expect(subject).to permit(viewer, status)
end
it 'grants access when direct and viewer is mentioned' do
status.visibility = :unlisted
status.mentions = [Fabricate(:mention, account: alice)]
expect(subject).to permit(alice, status)
end
it 'grants access when unlisted and account is following viewer' do
follow = Fabricate(:follow)
status.visibility = :unlisted
status.account = follow.target_account
expect(subject).to permit(follow.account, status)
end
it 'denies access when unlisted and account is not mentioned or following viewer' do
viewer = Fabricate(:account)
status.visibility = :unlisted
expect(subject).to_not permit(viewer, status)
end
end
permissions :show? do permissions :show? do
it 'grants access when direct and account is viewer' do it 'grants access when direct and account is viewer' do
status.visibility = :direct status.visibility = :direct

View file

@ -126,6 +126,66 @@ RSpec.describe NotifyService, type: :service do
end end
end end
describe 'status references' do
let(:target_status) { Fabricate(:status, account: recipient, visibility: :public) }
let(:activity) { Fabricate(:status_reference, status: status, target_status: target_status) }
let(:type) { :status_reference }
before do
user.settings.interactions = user.settings.interactions.merge('must_be_following_reference' => enabled)
end
context 'if must_be_following_reference is true' do
let(:enabled) { true }
describe 'with public' do
let(:status) { Fabricate(:status, account: sender, visibility: :public) }
it 'does notify' do
is_expected.to_not change(Notification, :count)
end
end
describe 'with unlisted' do
let(:status) { Fabricate(:status, account: sender, visibility: :unlisted) }
it 'does notify when sender is followed' do
recipient.follow!(sender)
is_expected.to change(Notification, :count)
end
it 'does not notify when sender is not followed' do
is_expected.to_not change(Notification, :count)
end
end
end
context 'if must_be_following_reference is false' do
let(:enabled) { false }
describe 'with public' do
let(:status) { Fabricate(:status, account: sender, visibility: :public) }
it 'does notify' do
is_expected.to change(Notification, :count)
end
end
describe 'with unlisted' do
let(:status) { Fabricate(:status, account: sender, visibility: :unlisted) }
it 'does notify when sender is followed' do
recipient.follow!(sender)
is_expected.to change(Notification, :count)
end
it 'does not notify when sender is not followed' do
is_expected.to_not change(Notification, :count)
end
end
end
end
context do context do
let(:asshole) { Fabricate(:account, username: 'asshole') } let(:asshole) { Fabricate(:account, username: 'asshole') }
let(:reply_to) { Fabricate(:status, account: asshole) } let(:reply_to) { Fabricate(:status, account: asshole) }