Add notification policies and notification requests (#29366)
This commit is contained in:
parent
653ce43abe
commit
50b17f7e10
104 changed files with 1096 additions and 247 deletions
37
app/controllers/api/v1/notifications/policies_controller.rb
Normal file
37
app/controllers/api/v1/notifications/policies_controller.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Notifications::PoliciesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, only: :show
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: :update
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_policy
|
||||
|
||||
def show
|
||||
render json: @policy, serializer: REST::NotificationPolicySerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@policy.update!(resource_params)
|
||||
render json: @policy, serializer: REST::NotificationPolicySerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_policy
|
||||
@policy = NotificationPolicy.find_or_initialize_by(account: current_account)
|
||||
|
||||
with_read_replica do
|
||||
@policy.summarize!
|
||||
end
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(
|
||||
:filter_not_following,
|
||||
:filter_not_followers,
|
||||
:filter_new_accounts,
|
||||
:filter_private_mentions
|
||||
)
|
||||
end
|
||||
end
|
75
app/controllers/api/v1/notifications/requests_controller.rb
Normal file
75
app/controllers/api/v1/notifications/requests_controller.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Notifications::RequestsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, except: :index
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_request, except: :index
|
||||
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
def index
|
||||
with_read_replica do
|
||||
@requests = load_requests
|
||||
@relationships = relationships
|
||||
end
|
||||
|
||||
render json: @requests, each_serializer: REST::NotificationRequestSerializer, relationships: @relationships
|
||||
end
|
||||
|
||||
def accept
|
||||
AcceptNotificationRequestService.new.call(@request)
|
||||
render_empty
|
||||
end
|
||||
|
||||
def dismiss
|
||||
@request.update!(dismissed: true)
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_requests
|
||||
requests = NotificationRequest.where(account: current_account).where(dismissed: truthy_param?(:dismissed)).includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
|
||||
NotificationRequest.preload_cache_collection(requests) do |statuses|
|
||||
cache_collection(statuses, Status)
|
||||
end
|
||||
end
|
||||
|
||||
def relationships
|
||||
StatusRelationshipsPresenter.new(@requests.map(&:last_status), current_user&.account_id)
|
||||
end
|
||||
|
||||
def set_request
|
||||
@request = NotificationRequest.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_notifications_requests_url pagination_params(max_id: pagination_max_id) unless @requests.empty?
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_notifications_requests_url pagination_params(min_id: pagination_since_id) unless @requests.empty?
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@requests.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@requests.first.id
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:dismissed).permit(:dismissed).merge(core_params)
|
||||
end
|
||||
end
|
|
@ -49,7 +49,8 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
current_account.notifications.without_suspended.browserable(
|
||||
types: Array(browserable_params[:types]),
|
||||
exclude_types: Array(browserable_params[:exclude_types]),
|
||||
from_account_id: browserable_params[:account_id]
|
||||
from_account_id: browserable_params[:account_id],
|
||||
include_filtered: truthy_param?(:include_filtered)
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -78,10 +79,10 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
end
|
||||
|
||||
def browserable_params
|
||||
params.permit(:account_id, types: [], exclude_types: [])
|
||||
params.permit(:account_id, :include_filtered, types: [], exclude_types: [])
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit, :account_id, :types, :exclude_types).permit(:limit, :account_id, types: [], exclude_types: []).merge(core_params)
|
||||
params.slice(:limit, :account_id, :types, :exclude_types, :include_filtered).permit(:limit, :account_id, :include_filtered, types: [], exclude_types: []).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,10 +15,15 @@ module Account::Associations
|
|||
has_many :favourites, inverse_of: :account, dependent: :destroy
|
||||
has_many :bookmarks, inverse_of: :account, dependent: :destroy
|
||||
has_many :mentions, inverse_of: :account, dependent: :destroy
|
||||
has_many :notifications, inverse_of: :account, dependent: :destroy
|
||||
has_many :conversations, class_name: 'AccountConversation', dependent: :destroy, inverse_of: :account
|
||||
has_many :scheduled_statuses, inverse_of: :account, dependent: :destroy
|
||||
|
||||
# Notifications
|
||||
has_many :notifications, inverse_of: :account, dependent: :destroy
|
||||
has_one :notification_policy, inverse_of: :account, dependent: :destroy
|
||||
has_many :notification_permissions, inverse_of: :account, dependent: :destroy
|
||||
has_many :notification_requests, inverse_of: :account, dependent: :destroy
|
||||
|
||||
# Pinned statuses
|
||||
has_many :status_pins, inverse_of: :account, dependent: :destroy
|
||||
has_many :pinned_statuses, -> { reorder('status_pins.created_at DESC') }, through: :status_pins, class_name: 'Status', source: :status
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# account_id :bigint(8) not null
|
||||
# from_account_id :bigint(8) not null
|
||||
# type :string
|
||||
# filtered :boolean default(FALSE), not null
|
||||
#
|
||||
|
||||
class Notification < ApplicationRecord
|
||||
|
@ -89,7 +90,7 @@ class Notification < ApplicationRecord
|
|||
end
|
||||
|
||||
class << self
|
||||
def browserable(types: [], exclude_types: [], from_account_id: nil)
|
||||
def browserable(types: [], exclude_types: [], from_account_id: nil, include_filtered: false)
|
||||
requested_types = if types.empty?
|
||||
TYPES
|
||||
else
|
||||
|
@ -99,6 +100,7 @@ class Notification < ApplicationRecord
|
|||
requested_types -= exclude_types.map(&:to_sym)
|
||||
|
||||
all.tap do |scope|
|
||||
scope.merge!(where(filtered: false)) unless include_filtered || from_account_id.present?
|
||||
scope.merge!(where(from_account_id: from_account_id)) if from_account_id.present?
|
||||
scope.merge!(where(type: requested_types)) unless requested_types.size == TYPES.size
|
||||
end
|
||||
|
@ -144,6 +146,8 @@ class Notification < ApplicationRecord
|
|||
after_initialize :set_from_account
|
||||
before_validation :set_from_account
|
||||
|
||||
after_destroy :remove_from_notification_request
|
||||
|
||||
private
|
||||
|
||||
def set_from_account
|
||||
|
@ -158,4 +162,9 @@ class Notification < ApplicationRecord
|
|||
self.from_account_id = activity&.id
|
||||
end
|
||||
end
|
||||
|
||||
def remove_from_notification_request
|
||||
notification_request = NotificationRequest.find_by(account_id: account_id, from_account_id: from_account_id)
|
||||
notification_request&.reconsider_existence!
|
||||
end
|
||||
end
|
||||
|
|
16
app/models/notification_permission.rb
Normal file
16
app/models/notification_permission.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: notification_permissions
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8) not null
|
||||
# from_account_id :bigint(8) not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class NotificationPermission < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :from_account, class_name: 'Account'
|
||||
end
|
36
app/models/notification_policy.rb
Normal file
36
app/models/notification_policy.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: notification_policies
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8) not null
|
||||
# filter_not_following :boolean default(FALSE), not null
|
||||
# filter_not_followers :boolean default(FALSE), not null
|
||||
# filter_new_accounts :boolean default(FALSE), not null
|
||||
# filter_private_mentions :boolean default(TRUE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class NotificationPolicy < ApplicationRecord
|
||||
belongs_to :account
|
||||
|
||||
has_many :notification_requests, primary_key: :account_id, foreign_key: :account_id, dependent: nil, inverse_of: false
|
||||
|
||||
attr_reader :pending_requests_count, :pending_notifications_count
|
||||
|
||||
MAX_MEANINGFUL_COUNT = 100
|
||||
|
||||
def summarize!
|
||||
@pending_requests_count = pending_notification_requests.first
|
||||
@pending_notifications_count = pending_notification_requests.last
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pending_notification_requests
|
||||
@pending_notification_requests ||= notification_requests.where(dismissed: false).limit(MAX_MEANINGFUL_COUNT).pick(Arel.sql('count(*), coalesce(sum(notifications_count), 0)::bigint'))
|
||||
end
|
||||
end
|
53
app/models/notification_request.rb
Normal file
53
app/models/notification_request.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: notification_requests
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8) not null
|
||||
# from_account_id :bigint(8) not null
|
||||
# last_status_id :bigint(8) not null
|
||||
# notifications_count :bigint(8) default(0), not null
|
||||
# dismissed :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class NotificationRequest < ApplicationRecord
|
||||
include Paginable
|
||||
|
||||
MAX_MEANINGFUL_COUNT = 100
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :from_account, class_name: 'Account'
|
||||
belongs_to :last_status, class_name: 'Status'
|
||||
|
||||
before_save :prepare_notifications_count
|
||||
|
||||
def self.preload_cache_collection(requests)
|
||||
cached_statuses_by_id = yield(requests.filter_map(&:last_status)).index_by(&:id) # Call cache_collection in block
|
||||
|
||||
requests.each do |request|
|
||||
request.last_status = cached_statuses_by_id[request.last_status_id] unless request.last_status_id.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def reconsider_existence!
|
||||
return if dismissed?
|
||||
|
||||
prepare_notifications_count
|
||||
|
||||
if notifications_count.positive?
|
||||
save
|
||||
else
|
||||
destroy
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_notifications_count
|
||||
self.notifications_count = Notification.where(account: account, from_account: from_account).limit(MAX_MEANINGFUL_COUNT).count
|
||||
end
|
||||
end
|
16
app/serializers/rest/notification_policy_serializer.rb
Normal file
16
app/serializers/rest/notification_policy_serializer.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::NotificationPolicySerializer < ActiveModel::Serializer
|
||||
attributes :filter_not_following,
|
||||
:filter_not_followers,
|
||||
:filter_new_accounts,
|
||||
:filter_private_mentions,
|
||||
:summary
|
||||
|
||||
def summary
|
||||
{
|
||||
pending_requests_count: object.pending_requests_count.to_s,
|
||||
pending_notifications_count: object.pending_notifications_count.to_s,
|
||||
}
|
||||
end
|
||||
end
|
16
app/serializers/rest/notification_request_serializer.rb
Normal file
16
app/serializers/rest/notification_request_serializer.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::NotificationRequestSerializer < ActiveModel::Serializer
|
||||
attributes :id, :created_at, :updated_at, :notifications_count
|
||||
|
||||
belongs_to :from_account, key: :account, serializer: REST::AccountSerializer
|
||||
belongs_to :last_status, serializer: REST::StatusSerializer
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
|
||||
def notifications_count
|
||||
object.notifications_count.to_s
|
||||
end
|
||||
end
|
8
app/services/accept_notification_request_service.rb
Normal file
8
app/services/accept_notification_request_service.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AcceptNotificationRequestService < BaseService
|
||||
def call(request)
|
||||
NotificationPermission.create!(account: request.account, from_account: request.from_account)
|
||||
UnfilterNotificationsWorker.perform_async(request.id)
|
||||
end
|
||||
end
|
|
@ -11,126 +11,195 @@ class NotifyService < BaseService
|
|||
status
|
||||
).freeze
|
||||
|
||||
class DismissCondition
|
||||
def initialize(notification)
|
||||
@recipient = notification.account
|
||||
@sender = notification.from_account
|
||||
@notification = notification
|
||||
end
|
||||
|
||||
def dismiss?
|
||||
blocked = @recipient.unavailable?
|
||||
blocked ||= from_self? && @notification.type != :poll
|
||||
|
||||
return blocked if message? && from_staff?
|
||||
|
||||
blocked ||= domain_blocking?
|
||||
blocked ||= @recipient.blocking?(@sender)
|
||||
blocked ||= @recipient.muting_notifications?(@sender)
|
||||
blocked ||= conversation_muted?
|
||||
blocked ||= blocked_mention? if message?
|
||||
blocked
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def blocked_mention?
|
||||
FeedManager.instance.filter?(:mentions, @notification.target_status, @recipient)
|
||||
end
|
||||
|
||||
def message?
|
||||
@notification.type == :mention
|
||||
end
|
||||
|
||||
def from_staff?
|
||||
@sender.local? && @sender.user.present? && @sender.user_role&.overrides?(@recipient.user_role)
|
||||
end
|
||||
|
||||
def from_self?
|
||||
@recipient.id == @sender.id
|
||||
end
|
||||
|
||||
def domain_blocking?
|
||||
@recipient.domain_blocking?(@sender.domain) && !following_sender?
|
||||
end
|
||||
|
||||
def conversation_muted?
|
||||
@notification.target_status && @recipient.muting_conversation?(@notification.target_status.conversation)
|
||||
end
|
||||
|
||||
def following_sender?
|
||||
@recipient.following?(@sender)
|
||||
end
|
||||
end
|
||||
|
||||
class FilterCondition
|
||||
NEW_ACCOUNT_THRESHOLD = 30.days.freeze
|
||||
|
||||
NEW_FOLLOWER_THRESHOLD = 3.days.freeze
|
||||
|
||||
def initialize(notification)
|
||||
@notification = notification
|
||||
@recipient = notification.account
|
||||
@sender = notification.from_account
|
||||
@policy = NotificationPolicy.find_or_initialize_by(account: @recipient)
|
||||
end
|
||||
|
||||
def filter?
|
||||
return false if override_for_sender?
|
||||
|
||||
from_limited? ||
|
||||
filtered_by_not_following_policy? ||
|
||||
filtered_by_not_followers_policy? ||
|
||||
filtered_by_new_accounts_policy? ||
|
||||
filtered_by_private_mentions_policy?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filtered_by_not_following_policy?
|
||||
@policy.filter_not_following? && not_following?
|
||||
end
|
||||
|
||||
def filtered_by_not_followers_policy?
|
||||
@policy.filter_not_followers? && not_follower?
|
||||
end
|
||||
|
||||
def filtered_by_new_accounts_policy?
|
||||
@policy.filter_new_accounts? && new_account?
|
||||
end
|
||||
|
||||
def filtered_by_private_mentions_policy?
|
||||
@policy.filter_private_mentions? && not_following? && private_mention_not_in_response?
|
||||
end
|
||||
|
||||
def not_following?
|
||||
!@recipient.following?(@sender)
|
||||
end
|
||||
|
||||
def not_follower?
|
||||
follow = Follow.find_by(account: @sender, target_account: @recipient)
|
||||
follow.nil? || follow.created_at > NEW_FOLLOWER_THRESHOLD.ago
|
||||
end
|
||||
|
||||
def new_account?
|
||||
@sender.created_at > NEW_ACCOUNT_THRESHOLD.ago
|
||||
end
|
||||
|
||||
def override_for_sender?
|
||||
NotificationPermission.exists?(account: @recipient, from_account: @sender)
|
||||
end
|
||||
|
||||
def from_limited?
|
||||
@sender.silenced? && not_following?
|
||||
end
|
||||
|
||||
def private_mention_not_in_response?
|
||||
@notification.type == :mention && @notification.target_status.direct_visibility? && !response_to_recipient?
|
||||
end
|
||||
|
||||
def response_to_recipient?
|
||||
return false if @notification.target_status.in_reply_to_id.nil?
|
||||
|
||||
statuses_that_mention_sender.positive?
|
||||
end
|
||||
|
||||
def statuses_that_mention_sender
|
||||
Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @sender.id, depth_limit: 100])
|
||||
WITH RECURSIVE ancestors(id, in_reply_to_id, mention_id, path, depth) AS (
|
||||
SELECT s.id, s.in_reply_to_id, m.id, ARRAY[s.id], 0
|
||||
FROM statuses s
|
||||
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
|
||||
WHERE s.id = :id
|
||||
UNION ALL
|
||||
SELECT s.id, s.in_reply_to_id, m.id, st.path || s.id, st.depth + 1
|
||||
FROM ancestors st
|
||||
JOIN statuses s ON s.id = st.in_reply_to_id
|
||||
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
|
||||
WHERE st.mention_id IS NULL AND NOT s.id = ANY(path) AND st.depth < :depth_limit
|
||||
)
|
||||
SELECT COUNT(*)
|
||||
FROM ancestors st
|
||||
JOIN statuses s ON s.id = st.id
|
||||
WHERE st.mention_id IS NOT NULL AND s.visibility = 3
|
||||
SQL
|
||||
end
|
||||
end
|
||||
|
||||
def call(recipient, type, activity)
|
||||
return if recipient.user.nil?
|
||||
|
||||
@recipient = recipient
|
||||
@activity = activity
|
||||
@notification = Notification.new(account: @recipient, type: type, activity: @activity)
|
||||
|
||||
return if recipient.user.nil? || blocked?
|
||||
# For certain conditions we don't need to create a notification at all
|
||||
return if dismiss?
|
||||
|
||||
@notification.filtered = filter?
|
||||
@notification.save!
|
||||
|
||||
# It's possible the underlying activity has been deleted
|
||||
# between the save call and now
|
||||
return if @notification.activity.nil?
|
||||
|
||||
push_notification!
|
||||
push_to_conversation! if direct_message?
|
||||
send_email! if email_needed?
|
||||
if @notification.filtered?
|
||||
update_notification_request!
|
||||
else
|
||||
push_notification!
|
||||
push_to_conversation! if direct_message?
|
||||
send_email! if email_needed?
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def blocked_mention?
|
||||
FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient)
|
||||
def dismiss?
|
||||
DismissCondition.new(@notification).dismiss?
|
||||
end
|
||||
|
||||
def following_sender?
|
||||
return @following_sender if defined?(@following_sender)
|
||||
|
||||
@following_sender = @recipient.following?(@notification.from_account) || @recipient.requested?(@notification.from_account)
|
||||
def filter?
|
||||
FilterCondition.new(@notification).filter?
|
||||
end
|
||||
|
||||
def optional_non_follower?
|
||||
@recipient.user.settings['interactions.must_be_follower'] && !@notification.from_account.following?(@recipient)
|
||||
end
|
||||
def update_notification_request!
|
||||
return unless @notification.type == :mention
|
||||
|
||||
def optional_non_following?
|
||||
@recipient.user.settings['interactions.must_be_following'] && !following_sender?
|
||||
end
|
||||
|
||||
def message?
|
||||
@notification.type == :mention
|
||||
end
|
||||
|
||||
def direct_message?
|
||||
message? && @notification.target_status.direct_visibility?
|
||||
end
|
||||
|
||||
# Returns true if the sender has been mentioned by the recipient up the thread
|
||||
def response_to_recipient?
|
||||
return false if @notification.target_status.in_reply_to_id.nil?
|
||||
|
||||
# Using an SQL CTE to avoid unneeded back-and-forth with SQL server in case of long threads
|
||||
!Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @notification.from_account.id, depth_limit: 100]).zero?
|
||||
WITH RECURSIVE ancestors(id, in_reply_to_id, mention_id, path, depth) AS (
|
||||
SELECT s.id, s.in_reply_to_id, m.id, ARRAY[s.id], 0
|
||||
FROM statuses s
|
||||
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
|
||||
WHERE s.id = :id
|
||||
UNION ALL
|
||||
SELECT s.id, s.in_reply_to_id, m.id, st.path || s.id, st.depth + 1
|
||||
FROM ancestors st
|
||||
JOIN statuses s ON s.id = st.in_reply_to_id
|
||||
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
|
||||
WHERE st.mention_id IS NULL AND NOT s.id = ANY(path) AND st.depth < :depth_limit
|
||||
)
|
||||
SELECT COUNT(*)
|
||||
FROM ancestors st
|
||||
JOIN statuses s ON s.id = st.id
|
||||
WHERE st.mention_id IS NOT NULL AND s.visibility = 3
|
||||
SQL
|
||||
end
|
||||
|
||||
def from_staff?
|
||||
@notification.from_account.local? && @notification.from_account.user.present? && @notification.from_account.user_role&.overrides?(@recipient.user_role)
|
||||
end
|
||||
|
||||
def optional_non_following_and_direct?
|
||||
direct_message? &&
|
||||
@recipient.user.settings['interactions.must_be_following_dm'] &&
|
||||
!following_sender? &&
|
||||
!response_to_recipient?
|
||||
end
|
||||
|
||||
def hellbanned?
|
||||
@notification.from_account.silenced? && !following_sender?
|
||||
end
|
||||
|
||||
def from_self?
|
||||
@recipient.id == @notification.from_account.id
|
||||
end
|
||||
|
||||
def domain_blocking?
|
||||
@recipient.domain_blocking?(@notification.from_account.domain) && !following_sender?
|
||||
end
|
||||
|
||||
def blocked?
|
||||
blocked = @recipient.unavailable?
|
||||
blocked ||= from_self? && @notification.type != :poll
|
||||
|
||||
return blocked if message? && from_staff?
|
||||
|
||||
blocked ||= domain_blocking?
|
||||
blocked ||= @recipient.blocking?(@notification.from_account)
|
||||
blocked ||= @recipient.muting_notifications?(@notification.from_account)
|
||||
blocked ||= hellbanned?
|
||||
blocked ||= optional_non_follower?
|
||||
blocked ||= optional_non_following?
|
||||
blocked ||= optional_non_following_and_direct?
|
||||
blocked ||= conversation_muted?
|
||||
blocked ||= blocked_mention? if @notification.type == :mention
|
||||
blocked
|
||||
end
|
||||
|
||||
def conversation_muted?
|
||||
if @notification.target_status
|
||||
@recipient.muting_conversation?(@notification.target_status.conversation)
|
||||
else
|
||||
false
|
||||
end
|
||||
notification_request = NotificationRequest.find_or_initialize_by(account_id: @recipient.id, from_account_id: @notification.from_account_id)
|
||||
notification_request.last_status_id = @notification.target_status.id
|
||||
notification_request.save
|
||||
end
|
||||
|
||||
def push_notification!
|
||||
|
@ -150,6 +219,10 @@ class NotifyService < BaseService
|
|||
AccountConversation.add_status(@recipient, @notification.target_status)
|
||||
end
|
||||
|
||||
def direct_message?
|
||||
@notification.type == :mention && @notification.target_status.direct_visibility?
|
||||
end
|
||||
|
||||
def push_to_web_push_subscriptions!
|
||||
::Web::PushNotificationWorker.push_bulk(web_push_subscriptions.select { |subscription| subscription.pushable?(@notification) }) { |subscription| [subscription.id, @notification.id] }
|
||||
end
|
||||
|
|
|
@ -40,11 +40,3 @@
|
|||
label_method: ->(setting) { I18n.t("simple_form.labels.notification_emails.software_updates.#{setting}") },
|
||||
label: I18n.t('simple_form.labels.notification_emails.software_updates.label'),
|
||||
wrapper: :with_label
|
||||
|
||||
%h4= t 'notifications.other_settings'
|
||||
|
||||
.fields-group
|
||||
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||
= ff.input :'interactions.must_be_follower', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_follower')
|
||||
= ff.input :'interactions.must_be_following', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following')
|
||||
= ff.input :'interactions.must_be_following_dm', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following_dm')
|
||||
|
|
37
app/workers/unfilter_notifications_worker.rb
Normal file
37
app/workers/unfilter_notifications_worker.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UnfilterNotificationsWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(notification_request_id)
|
||||
@notification_request = NotificationRequest.find(notification_request_id)
|
||||
|
||||
push_to_conversations!
|
||||
unfilter_notifications!
|
||||
remove_request!
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def push_to_conversations!
|
||||
notifications_with_private_mentions.find_each { |notification| AccountConversation.add_status(@notification_request.account, notification.target_status) }
|
||||
end
|
||||
|
||||
def unfilter_notifications!
|
||||
filtered_notifications.in_batches.update_all(filtered: false)
|
||||
end
|
||||
|
||||
def remove_request!
|
||||
@notification_request.destroy!
|
||||
end
|
||||
|
||||
def filtered_notifications
|
||||
Notification.where(account: @notification_request.account, from_account: @notification_request.from_account, filtered: true)
|
||||
end
|
||||
|
||||
def notifications_with_private_mentions
|
||||
filtered_notifications.joins(mention: :status).merge(Status.where(visibility: :direct)).includes(mention: :status)
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue