Add notification policies and notification requests (#29366)

This commit is contained in:
Eugen Rochko 2024-03-07 15:53:37 +01:00 committed by GitHub
parent 653ce43abe
commit 50b17f7e10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
104 changed files with 1096 additions and 247 deletions

View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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

View file

@ -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')

View 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

View file

@ -1288,7 +1288,6 @@ an:
notifications:
email_events: Eventos pa notificacions per correu electronico
email_events_hint: 'Tría los eventos pa los quals deseyas recibir notificacions:'
other_settings: Atros achustes de notificacions
number:
human:
decimal_units:

View file

@ -1594,7 +1594,6 @@ ar:
administration_emails: إشعارات البريد الإلكتروني الإدارية
email_events: الأحداث للإشعارات عبر البريد الإلكتروني
email_events_hint: 'اختر الأحداث التي تريد أن تصِلَك اشعارات عنها:'
other_settings: إعدادات أخرى للإشعارات
number:
human:
decimal_units:

View file

@ -695,7 +695,6 @@ ast:
notifications:
email_events: Unviu d'avisos per corréu electrónicu
email_events_hint: 'Seleiciona los eventos de los que quies recibir avisos:'
other_settings: Configuración d'otros avisos
number:
human:
decimal_units:

View file

@ -1547,7 +1547,6 @@ be:
administration_emails: Апавяшчэнні эл. пошты для адміністратара
email_events: Падзеі для апавяшчэнняў эл. пошты
email_events_hint: 'Выберыце падзеі, аб якіх вы хочаце атрымліваць апавяшчэнні:'
other_settings: Іншыя налады апавяшчэнняў
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ bg:
administration_emails: Администраторски известия по имейла
email_events: Събития за известия по имейл
email_events_hint: 'Изберете събития, за които искате да получавате известия:'
other_settings: Настройки за други известия
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ ca:
administration_emails: Notificacions per correu-e de l'Admin
email_events: Esdeveniments per a notificacions de correu electrònic
email_events_hint: 'Selecciona els esdeveniments per als quals vols rebre notificacions:'
other_settings: Altres opcions de notificació
number:
human:
decimal_units:

View file

@ -840,7 +840,6 @@ ckb:
notifications:
email_events: رووداوەکان بۆ ئاگاداری ئیمەیلی
email_events_hint: 'ئەو ڕووداوانە دیاریبکە کە دەتەوێت ئاگانامەکان وەربگری بۆ:'
other_settings: ڕێکبەندەکانی ئاگانامەکانی تر
otp_authentication:
code_hint: کۆدێک داخڵ بکە کە دروست کراوە لەلایەن ئەپی ڕەسەنایەتیەوە بۆ دڵنیابوون
description_html: ئەگەر تۆ <strong> هاتنەژوورەوەی دوو قۆناغی</strong> بە یارمەتی ئەپێکی پەسەندکردن چالاک بکەن، پێویستە بۆ چوونەژوورەوە ، بە تەلەفۆنەکەتان کە کۆدیکتان بۆ دروستدەکات دەستپێگەیشتنتان هەبێت.

View file

@ -809,7 +809,6 @@ co:
notifications:
email_events: Avvenimenti da nutificà cù l'e-mail
email_events_hint: 'Selezziunate l''avvenimenti per quelli vulete riceve nutificazione:'
other_settings: Altri parametri di nutificazione
number:
human:
decimal_units:

View file

@ -1547,7 +1547,6 @@ cs:
administration_emails: E-mailová oznámení administrátora
email_events: Události pro e-mailová oznámení
email_events_hint: 'Vyberte události, pro které chcete dostávat oznámení:'
other_settings: Další nastavení oznámení
number:
human:
decimal_units:

View file

@ -1599,7 +1599,6 @@ cy:
administration_emails: Hysbysiadau e-bost gweinyddol
email_events: Digwyddiadau ar gyfer hysbysiadau e-bost
email_events_hint: 'Dewiswch ddigwyddiadau yr ydych am dderbyn hysbysiadau ar eu cyfer:'
other_settings: Gosodiadau hysbysiadau arall
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ da:
administration_emails: Admin e-mailnotifikationer
email_events: Begivenheder for e-mailnotifikationer
email_events_hint: 'Vælg begivenheder, for hvilke notifikationer skal modtages:'
other_settings: Andre notifikationsindstillinger
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ de:
administration_emails: Admin-E-Mail-Benachrichtigungen
email_events: Benachrichtigungen per E-Mail
email_events_hint: 'Bitte die Ereignisse auswählen, für die du Benachrichtigungen per E-Mail erhalten möchtest:'
other_settings: Weitere Benachrichtigungseinstellungen
number:
human:
decimal_units:

View file

@ -1383,7 +1383,6 @@ el:
notifications:
email_events: Συμβάντα για ειδοποιήσεις μέσω email
email_events_hint: 'Επέλεξε συμβάντα για τα οποία θέλεις να λαμβάνεις ειδοποιήσεις μέσω email:'
other_settings: Άλλες ρυθμίσεις ειδοποιήσεων
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ en-GB:
administration_emails: Admin e-mail notifications
email_events: Events for e-mail notifications
email_events_hint: 'Select events that you want to receive notifications for:'
other_settings: Other notifications settings
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ en:
administration_emails: Admin e-mail notifications
email_events: Events for e-mail notifications
email_events_hint: 'Select events that you want to receive notifications for:'
other_settings: Other notifications settings
number:
human:
decimal_units:

View file

@ -1425,7 +1425,6 @@ eo:
administration_emails: Admin retpoŝtaj sciigoj
email_events: Eventoj por retpoŝtaj sciigoj
email_events_hint: 'Elekti la eventojn pri kioj vi volas ricevi sciigojn:'
other_settings: Aliaj agordoj de sciigoj
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ es-AR:
administration_emails: Notificaciones de administración por correo electrónico
email_events: Eventos para notificaciones por correo electrónico
email_events_hint: 'Seleccioná los eventos para los que querés recibir notificaciones:'
other_settings: Configuración de otras notificaciones
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ es-MX:
administration_emails: Notificaciones de administración por correo electrónico
email_events: Eventos para notificaciones por correo electrónico
email_events_hint: 'Selecciona los eventos para los que deseas recibir notificaciones:'
other_settings: Otros ajustes de notificaciones
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ es:
administration_emails: Notificaciones de administración por correo electrónico
email_events: Eventos para notificaciones por correo electrónico
email_events_hint: 'Selecciona los eventos para los que deseas recibir notificaciones:'
other_settings: Otros ajustes de notificaciones
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ et:
administration_emails: Admini e-postiteavitused
email_events: E-posti teadete sündmused
email_events_hint: 'Vali sündmused, mille kohta soovid teavitusi:'
other_settings: Muud teadete sätted
number:
human:
decimal_units:

View file

@ -1496,7 +1496,6 @@ eu:
administration_emails: Administratzailearen posta elektroniko bidezko jakinarazpenak
email_events: E-mail jakinarazpenentzako gertaerak
email_events_hint: 'Hautatu jaso nahi dituzun gertaeren jakinarazpenak:'
other_settings: Bezte jakinarazpen konfigurazioak
number:
human:
decimal_units:

View file

@ -1271,7 +1271,6 @@ fa:
notifications:
email_events: رویدادها برای آگاهی‌های رایانامه‌ای
email_events_hint: 'گزینش رویدادهایی که می‌خواهید برایشان آگاهی دریافت کنید:'
other_settings: سایر تنظیمات آگاهی‌ها
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ fi:
administration_emails: Ylläpitäjän sähköposti-ilmoitukset
email_events: Sähköposti-ilmoitusten tapahtumat
email_events_hint: 'Valitse tapahtumat, joista haluat saada ilmoituksia:'
other_settings: Muut ilmoitusasetukset
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ fo:
administration_emails: Fráboðanir um teldupost til umsitarar
email_events: Hendingar fyri teldupostfráboðanir
email_events_hint: 'Vel hendingar, sum tú vil hava fráboðanir um:'
other_settings: Aðrar fráboðanarstillingar
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ fr-CA:
administration_emails: Notifications par e-mail de ladmin
email_events: Événements pour les notifications par courriel
email_events_hint: 'Sélectionnez les événements pour lesquels vous souhaitez recevoir des notifications :'
other_settings: Autres paramètres de notifications
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ fr:
administration_emails: Notifications par e-mail de ladmin
email_events: Événements pour les notifications par courriel
email_events_hint: 'Sélectionnez les événements pour lesquels vous souhaitez recevoir des notifications :'
other_settings: Autres paramètres de notifications
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@ fy:
administration_emails: E-mailmeldingen behearder
email_events: E-mailmeldingen foar eveneminten
email_events_hint: 'Selektearje eveneminten wêrfoart jo meldingen ûntfange wolle:'
other_settings: Oare meldingsynstellingen
number:
human:
decimal_units:

View file

@ -1532,7 +1532,6 @@ gd:
administration_emails: Brathan puist-d na rianachd
email_events: Tachartasan nam brathan puist-d
email_events_hint: 'Tagh na tachartasan dhan a bheil thu airson brathan fhaighinn:'
other_settings: Roghainnean eile nam brathan
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ gl:
administration_emails: Notificacións de Admin por correo electrónico
email_events: Eventos para os correos de notificación
email_events_hint: 'Escolle os eventos sobre os que queres recibir notificacións:'
other_settings: Outros axustes das notificacións
number:
human:
decimal_units:

View file

@ -1547,7 +1547,6 @@ he:
administration_emails: התראות לדוא"ל חשבון מנהל
email_events: ארועים להתראות דוא"ל
email_events_hint: 'בחר/י ארועים עבורים תרצה/י לקבל התראות:'
other_settings: הגדרות התראות אחרות
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ hu:
administration_emails: Adminisztrátori e-mail-értesítések
email_events: Események email értesítésekhez
email_events_hint: 'Válaszd ki azokat az eseményeket, melyekről értesítést szeretnél:'
other_settings: Értesítések egyéb beállításai
number:
human:
decimal_units:

View file

@ -672,7 +672,6 @@ hy:
subject: "%{name}-ը փոխել է գրառումը"
notifications:
email_events_hint: Ընտրիր իրադարձութիւնները, որոնց վերաբերեալ ցանկանում ես ստանալ ծանուցումներ․
other_settings: Ծանուցումների այլ կարգաւորումներ
number:
human:
decimal_units:

View file

@ -1257,7 +1257,6 @@ id:
notifications:
email_events: Event untuk notifikasi email
email_events_hint: 'Pilih event yang ingin Anda terima notifikasinya:'
other_settings: Pengaturan notifikasi lain
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ ie:
administration_emails: Email-notificationes pri administration
email_events: Evenimentes por email-notificationes
email_events_hint: 'Selecte li evenimentes pri queles tu vole reciver notificationes:'
other_settings: Parametres pri altri notificationes
number:
human:
decimal_units:

View file

@ -1472,7 +1472,6 @@ io:
administration_emails: Jerala retpostonotifiki
email_events: Eventi por retpostoavizi
email_events_hint: 'Selektez eventi quon vu volas ganar avizi:'
other_settings: Altra avizopcioni
number:
human:
decimal_units:

View file

@ -1499,7 +1499,6 @@ is:
administration_emails: Kerfisstjórnunartilkynningar í tölvupósti
email_events: Atburðir fyrir tilkynningar í tölvupósti
email_events_hint: 'Veldu þá atburði sem þú vilt fá tilkynningar í tölvupósti þegar þeir koma upp:'
other_settings: Aðrar stillingar varðandi tilkynningar
number:
human:
decimal_units:

View file

@ -1497,7 +1497,6 @@ it:
administration_emails: Notifiche email amministratore
email_events: Eventi per notifiche via email
email_events_hint: 'Seleziona gli eventi per i quali vuoi ricevere le notifiche:'
other_settings: Altre impostazioni delle notifiche
number:
human:
decimal_units:

View file

@ -1469,7 +1469,6 @@ ja:
administration_emails: 管理にかかわるメール通知
email_events: メールによる通知
email_events_hint: '受信する通知を選択:'
other_settings: その他の通知設定
number:
human:
decimal_units:

View file

@ -626,8 +626,6 @@ kab:
subject: Yuder-ik·ikem-id %{name}
reblog:
subject: "%{name} yesselha addad-ik·im"
notifications:
other_settings: Iɣewwaṛen nniḍen n yilɣa
number:
human:
decimal_units:

View file

@ -539,7 +539,6 @@ kk:
notifications:
email_events: E-mail ескертпелеріне шаралар
email_events_hint: 'Ескертпе болып келетін шараларды таңда:'
other_settings: Ескертпелердің басқа баптаулары
number:
human:
decimal_units:

View file

@ -1471,7 +1471,6 @@ ko:
administration_emails: 관리자 이메일 알림
email_events: 이메일 알림에 대한 이벤트
email_events_hint: '알림 받을 이벤트를 선택해주세요:'
other_settings: 기타 알림 설정
number:
human:
decimal_units:

View file

@ -1285,7 +1285,6 @@ ku:
notifications:
email_events: Bûyer bo agahdariyên e-nameyê
email_events_hint: 'Bûyera ku tu dixwazî agahdariyan jê wergerî hilbijêre:'
other_settings: Sazkariya agahdariyên din
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ lad:
administration_emails: Avizos de administrasyon por posta
email_events: Evenimyentos para avizos por posta
email_events_hint: 'Eskoje los evenimientos para los kualos keres risivir avizos:'
other_settings: Otras preferensyas de avizos
number:
human:
decimal_units:

View file

@ -1513,7 +1513,6 @@ lv:
administration_emails: Administrators e-pasta paziņojumi
email_events: E-pasta paziņojumu notikumi
email_events_hint: 'Atlasi notikumus, par kuriem vēlies saņemt paziņojumus:'
other_settings: Citu paziņojumu iestatījumi
number:
human:
decimal_units:

View file

@ -1442,7 +1442,6 @@ ms:
administration_emails: Notifikasi e-mel pentadbir
email_events: Acara untuk pemberitahuan e-mel
email_events_hint: 'Pilih acara yang ingin anda terima pemberitahuan:'
other_settings: Tetapan notifikasi lain
number:
human:
decimal_units:

View file

@ -1445,7 +1445,6 @@ my:
administration_emails: စီမံခန့်ခွဲသူ အီးမေးလ် အသိပေးချက်များ
email_events: အီးမေးလ်သတိပေးချက်များအတွက်အကြောင်းအရာများ
email_events_hint: အသိပေးချက်များရယူမည့် အစီအစဉ်များကို ရွေးပါ -
other_settings: အခြားအသိပေးချက်များ၏ သတ်မှတ်ချက်များ
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ nl:
administration_emails: E-mailmeldingen beheerder
email_events: E-mailmeldingen voor gebeurtenissen
email_events_hint: 'Selecteer gebeurtenissen waarvoor je meldingen wilt ontvangen:'
other_settings: Andere meldingsinstellingen
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ nn:
administration_emails: Administrator sine epost-varsler
email_events: E-postvarslinger for hendelser
email_events_hint: 'Velg hendelser som du vil motta varslinger for:'
other_settings: Andre varslingsinnstillinger
number:
human:
decimal_units:

View file

@ -1490,7 +1490,6 @@
administration_emails: Administrators e-postvarslinger
email_events: E-postvarslinger for hendelser
email_events_hint: 'Velg hendelser som du vil motta varslinger for:'
other_settings: Andre varslingsinnstillinger
number:
human:
decimal_units:

View file

@ -724,7 +724,6 @@ oc:
notifications:
email_events: Eveniments per las notificacions per corrièl
email_events_hint: 'Seleccionatz los eveniments que volètz recebre:'
other_settings: Autres paramètres de notificacion
number:
human:
decimal_units:

View file

@ -1547,7 +1547,6 @@ pl:
administration_emails: Administracyjne powiadomienia e-mail
email_events: 'Powiadamiaj e-mailem o:'
email_events_hint: 'Wybierz wydarzenia, o których chcesz otrzymywać powiadomienia:'
other_settings: Inne ustawienia powiadomień
number:
human:
decimal_units:

View file

@ -1491,7 +1491,6 @@ pt-BR:
administration_emails: Notificações por e-mail sobre administração
email_events: Eventos para notificações por e-mail
email_events_hint: 'Selecione os eventos que deseja receber notificações:'
other_settings: Outras opções para notificações
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ pt-PT:
administration_emails: Notificções administrativas por e-mail
email_events: Eventos para notificações por e-mail
email_events_hint: 'Selecione os casos para os quais deseja receber notificações:'
other_settings: Outras opções de notificações
number:
human:
decimal_units:

View file

@ -1542,7 +1542,6 @@ ru:
administration_emails: Уведомления администратора по электронной почте
email_events: События для e-mail уведомлений
email_events_hint: 'Выберите события, для которых вы хотели бы получать уведомления:'
other_settings: Остальные настройки уведомлений
number:
human:
decimal_units:

View file

@ -865,7 +865,6 @@ sc:
notifications:
email_events: Eventos pro notìficas cun posta eletrònica
email_events_hint: 'Seletziona eventos pro is chi boles retzire notìficas:'
other_settings: Àteras configuratziones de notìficas
number:
human:
decimal_units:

View file

@ -1275,7 +1275,6 @@ sco:
notifications:
email_events: Events fir email notes
email_events_hint: 'Pick events thit ye''r wantin tae get notes fir:'
other_settings: Ither notes settins
number:
human:
decimal_units:

View file

@ -1144,7 +1144,6 @@ si:
notifications:
email_events: ඊමේල් දැනුම්දීම් සඳහා සිදුවීම්
email_events_hint: 'ඔබට දැනුම්දීම් ලැබීමට අවශ්‍ය සිදුවීම් තෝරන්න:'
other_settings: වෙනත් දැනුම්දීම් සැකසුම්
number:
human:
decimal_units:

View file

@ -1085,7 +1085,6 @@ sk:
notifications:
email_events: Udalosti oznamované emailom
email_events_hint: 'Vyber si udalosti, pre ktoré chceš dostávať oboznámenia:'
other_settings: Ostatné oboznamovacie nastavenia
otp_authentication:
enable: Povoľ
pagination:

View file

@ -1547,7 +1547,6 @@ sl:
administration_emails: E-poštna obvestila skrbnika
email_events: Dogodki za e-obvestila
email_events_hint: 'Izberite dogodke, za katere želite prejmati obvestila:'
other_settings: Druge nastavitve obvestil
number:
human:
decimal_units:

View file

@ -1491,7 +1491,6 @@ sq:
administration_emails: Njoftime email për përgjegjësin
email_events: Akte për njoftim me email
email_events_hint: 'Përzgjidhni akte për të cilët doni të merrni njoftime:'
other_settings: Rregullimet të tjera njoftimesh
number:
human:
decimal_units:

View file

@ -1516,7 +1516,6 @@ sr-Latn:
administration_emails: Obaveštenja e-poštom od administratora
email_events: Događaji za obaveštenja e-poštom
email_events_hint: 'Izaberite dešavanja za koja želite da primate obaveštenja:'
other_settings: Ostala podešavanja obaveštenja
number:
human:
decimal_units:

View file

@ -1516,7 +1516,6 @@ sr:
administration_emails: Обавештења е-поштом од администратора
email_events: Догађаји за обавештења е-поштом
email_events_hint: 'Изаберите дешавања за која желите да примате обавештења:'
other_settings: Остала подешавања обавештења
number:
human:
decimal_units:

View file

@ -1489,7 +1489,6 @@ sv:
administration_emails: Admin e-postaviseringar
email_events: Händelser för e-postnotiser
email_events_hint: 'Välj händelser som du vill ta emot aviseringar för:'
other_settings: Andra aviseringsinställningar
number:
human:
decimal_units:

View file

@ -212,7 +212,6 @@ ta:
notifications:
email_events: மின்னஞ்சல் அறிவிப்புகளுக்கான நிகழ்வுகள்
email_events_hint: 'எந்த நிகழ்வுகளுக்கு அறிவிப்புகளைப் பெற வேண்டும் என்று தேர்வு செய்க:'
other_settings: அறிவிப்புகள் குறித்த பிற அமைப்புகள்
polls:
errors:
invalid_choice: நீங்கள் தேர்வு செய்த விருப்பம் கிடைக்கவில்லை

View file

@ -1469,7 +1469,6 @@ th:
administration_emails: การแจ้งเตือนอีเมลผู้ดูแล
email_events: เหตุการณ์สำหรับการแจ้งเตือนอีเมล
email_events_hint: 'เลือกเหตุการณ์ที่คุณต้องการรับการแจ้งเตือน:'
other_settings: การตั้งค่าการแจ้งเตือนอื่น ๆ
number:
human:
decimal_units:

View file

@ -1495,7 +1495,6 @@ tr:
administration_emails: Yönetici e-posta bildirimleri
email_events: E-posta bildirimi gönderilecek etkinlikler
email_events_hint: 'Bildirim almak istediğiniz olayları seçin:'
other_settings: Diğer bildirim ayarları
number:
human:
decimal_units:

View file

@ -1547,7 +1547,6 @@ uk:
administration_emails: Сповіщення е-пошти адміністратора
email_events: Події, про які сповіщати електронною поштою
email_events_hint: 'Оберіть події, про які ви хочете отримувати сповіщення:'
other_settings: Інші налаштування сповіщень
number:
human:
decimal_units:

View file

@ -1467,7 +1467,6 @@ vi:
administration_emails: Email thông báo admin
email_events: Email
email_events_hint: 'Chọn những hoạt động sẽ gửi thông báo qua email:'
other_settings: Chặn thông báo từ
number:
human:
decimal_units:

View file

@ -1469,7 +1469,6 @@ zh-CN:
administration_emails: 管理员电子邮件通知
email_events: 电子邮件通知事件
email_events_hint: 选择你想要收到通知的事件:
other_settings: 其它通知设置
number:
human:
decimal_units:

View file

@ -1464,7 +1464,6 @@ zh-HK:
administration_emails: 管理員電郵通知
email_events: 電郵通知活動
email_events_hint: 選擇你想接收通知的活動:
other_settings: 其他通知設定
number:
human:
decimal_units:

View file

@ -1471,7 +1471,6 @@ zh-TW:
administration_emails: 管理員電子郵件通知
email_events: 電子郵件通知設定
email_events_hint: 選取您想接收通知的事件:
other_settings: 其他通知設定
number:
human:
decimal_units:

View file

@ -150,6 +150,17 @@ namespace :api, format: false do
end
end
namespace :notifications do
resources :requests, only: :index do
member do
post :accept
post :dismiss
end
end
resource :policy, only: [:show, :update]
end
resources :notifications, only: [:index, :show] do
collection do
post :clear

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddFilteredToNotifications < ActiveRecord::Migration[7.1]
def change
add_column :notifications, :filtered, :boolean, default: false, null: false
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
class CreateNotificationRequests < ActiveRecord::Migration[7.1]
def change
create_table :notification_requests do |t|
t.references :account, null: false, foreign_key: { on_delete: :cascade }, index: false
t.references :from_account, null: false, foreign_key: { to_table: :accounts, on_delete: :cascade }
t.references :last_status, null: false, foreign_key: { to_table: :statuses, on_delete: :nullify }
t.bigint :notifications_count, null: false, default: 0
t.boolean :dismissed, null: false, default: false
t.timestamps
end
add_index :notification_requests, [:account_id, :from_account_id], unique: true
add_index :notification_requests, [:account_id, :id], where: 'dismissed = false', order: { id: :desc }
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class NotificationRequestIdsToTimestampIds < ActiveRecord::Migration[7.1]
def up
safety_assured do
execute("ALTER TABLE notification_requests ALTER COLUMN id SET DEFAULT timestamp_id('notification_requests')")
end
end
def down
execute('LOCK notification_requests')
execute("SELECT setval('notification_requests_id_seq', (SELECT MAX(id) FROM notification_requests))")
execute("ALTER TABLE notification_requests ALTER COLUMN id SET DEFAULT nextval('notification_requests_id_seq')")
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
class CreateNotificationPermissions < ActiveRecord::Migration[7.1]
def change
create_table :notification_permissions do |t|
t.references :account, null: false, foreign_key: true
t.references :from_account, null: false, foreign_key: { to_table: :accounts }
t.timestamps
end
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class CreateNotificationPolicies < ActiveRecord::Migration[7.1]
def change
create_table :notification_policies do |t|
t.references :account, null: false, foreign_key: true, index: { unique: true }
t.boolean :filter_not_following, null: false, default: false
t.boolean :filter_not_followers, null: false, default: false
t.boolean :filter_new_accounts, null: false, default: false
t.boolean :filter_private_mentions, null: false, default: true
t.timestamps
end
end
end

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddFilteredIndexOnNotifications < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
def change
add_index :notifications, [:account_id, :id, :type], where: 'filtered = false', order: { id: :desc }, name: 'index_notifications_on_filtered', algorithm: :concurrently
end
end

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
class MigrateInteractionSettingsToPolicy < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
# Dummy classes, to make migration possible across version changes
class Account < ApplicationRecord
has_one :user, inverse_of: :account
has_one :notification_policy, inverse_of: :account
end
class User < ApplicationRecord
belongs_to :account
end
class NotificationPolicy < ApplicationRecord
belongs_to :account
end
def up
User.includes(account: :notification_policy).find_each do |user|
deserialized_settings = Oj.load(user.attributes_before_type_cast['settings'])
policy = user.account.notification_policy || user.account.build_notification_policy
requires_new_policy = false
if deserialized_settings['interactions.must_be_follower']
policy.filter_not_followers = true
requires_new_policy = true
end
if deserialized_settings['interactions.must_be_following']
policy.filter_not_following = true
requires_new_policy = true
end
if deserialized_settings['interactions.must_be_following_dm']
policy.filter_private_mentions = true
requires_new_policy = true
end
policy.save if requires_new_policy && policy.changed?
end
end
def down; end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_01_11_033014) do
ActiveRecord::Schema[7.1].define(version: 2024_03_04_090449) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -666,6 +666,40 @@ ActiveRecord::Schema[7.1].define(version: 2024_01_11_033014) do
t.index ["target_account_id"], name: "index_mutes_on_target_account_id"
end
create_table "notification_permissions", force: :cascade do |t|
t.bigint "account_id", null: false
t.bigint "from_account_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_notification_permissions_on_account_id"
t.index ["from_account_id"], name: "index_notification_permissions_on_from_account_id"
end
create_table "notification_policies", force: :cascade do |t|
t.bigint "account_id", null: false
t.boolean "filter_not_following", default: false, null: false
t.boolean "filter_not_followers", default: false, null: false
t.boolean "filter_new_accounts", default: false, null: false
t.boolean "filter_private_mentions", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_notification_policies_on_account_id", unique: true
end
create_table "notification_requests", id: :bigint, default: -> { "timestamp_id('notification_requests'::text)" }, force: :cascade do |t|
t.bigint "account_id", null: false
t.bigint "from_account_id", null: false
t.bigint "last_status_id", null: false
t.bigint "notifications_count", default: 0, null: false
t.boolean "dismissed", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "from_account_id"], name: "index_notification_requests_on_account_id_and_from_account_id", unique: true
t.index ["account_id", "id"], name: "index_notification_requests_on_account_id_and_id", order: { id: :desc }, where: "(dismissed = false)"
t.index ["from_account_id"], name: "index_notification_requests_on_from_account_id"
t.index ["last_status_id"], name: "index_notification_requests_on_last_status_id"
end
create_table "notifications", force: :cascade do |t|
t.bigint "activity_id", null: false
t.string "activity_type", null: false
@ -674,7 +708,9 @@ ActiveRecord::Schema[7.1].define(version: 2024_01_11_033014) do
t.bigint "account_id", null: false
t.bigint "from_account_id", null: false
t.string "type"
t.boolean "filtered", default: false, null: false
t.index ["account_id", "id", "type"], name: "index_notifications_on_account_id_and_id_and_type", order: { id: :desc }
t.index ["account_id", "id", "type"], name: "index_notifications_on_filtered", order: { id: :desc }, where: "(filtered = false)"
t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type"
t.index ["from_account_id"], name: "index_notifications_on_from_account_id"
end
@ -1255,6 +1291,12 @@ ActiveRecord::Schema[7.1].define(version: 2024_01_11_033014) do
add_foreign_key "mentions", "statuses", on_delete: :cascade
add_foreign_key "mutes", "accounts", column: "target_account_id", name: "fk_eecff219ea", on_delete: :cascade
add_foreign_key "mutes", "accounts", name: "fk_b8d8daf315", on_delete: :cascade
add_foreign_key "notification_permissions", "accounts"
add_foreign_key "notification_permissions", "accounts", column: "from_account_id"
add_foreign_key "notification_policies", "accounts"
add_foreign_key "notification_requests", "accounts", column: "from_account_id", on_delete: :cascade
add_foreign_key "notification_requests", "accounts", on_delete: :cascade
add_foreign_key "notification_requests", "statuses", column: "last_status_id", on_delete: :nullify
add_foreign_key "notifications", "accounts", column: "from_account_id", name: "fk_fbd6b0bf9e", on_delete: :cascade
add_foreign_key "notifications", "accounts", name: "fk_c141c8ee55", on_delete: :cascade
add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id", name: "fk_34d54b0a33", on_delete: :cascade

View file

@ -24,14 +24,13 @@ describe Settings::Preferences::NotificationsController do
describe 'PUT #update' do
it 'updates notifications settings' do
user.settings.update('notification_emails.follow': false, 'interactions.must_be_follower': true)
user.settings.update('notification_emails.follow': false)
user.save
put :update, params: {
user: {
settings_attributes: {
'notification_emails.follow': '1',
'interactions.must_be_follower': '0',
},
},
}
@ -39,7 +38,6 @@ describe Settings::Preferences::NotificationsController do
expect(response).to redirect_to(settings_preferences_notifications_path)
user.reload
expect(user.settings['notification_emails.follow']).to be true
expect(user.settings['interactions.must_be_follower']).to be false
end
end
end

View file

@ -0,0 +1,6 @@
# frozen_string_literal: true
Fabricator(:notification_permission) do
account
from_account { Fabricate.build(:account) }
end

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
Fabricator(:notification_policy) do
account
filter_not_following false
filter_not_followers false
filter_new_accounts false
filter_private_mentions true
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
Fabricator(:notification_request) do
account
from_account { Fabricate.build(:account) }
last_status { Fabricate.build(:status) }
dismissed false
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe NotificationPolicy do
describe '#summarize!' do
subject { Fabricate(:notification_policy) }
let(:sender) { Fabricate(:account) }
before do
Fabricate.times(2, :notification, account: subject.account, activity: Fabricate(:status, account: sender))
Fabricate(:notification_request, account: subject.account, from_account: sender)
subject.summarize!
end
it 'sets pending_requests_count' do
expect(subject.pending_requests_count).to eq 1
end
it 'sets pending_notifications_count' do
expect(subject.pending_notifications_count).to eq 2
end
end
end

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe NotificationRequest do
describe '#reconsider_existence!' do
subject { Fabricate(:notification_request, dismissed: dismissed) }
let(:dismissed) { false }
context 'when there are remaining notifications' do
before do
Fabricate(:notification, account: subject.account, activity: Fabricate(:status, account: subject.from_account))
subject.reconsider_existence!
end
it 'leaves request intact' do
expect(subject.destroyed?).to be false
end
it 'updates notifications_count' do
expect(subject.notifications_count).to eq 1
end
end
context 'when there are no notifications' do
before do
subject.reconsider_existence!
end
context 'when dismissed' do
let(:dismissed) { true }
it 'leaves request intact' do
expect(subject.destroyed?).to be false
end
end
it 'removes the request' do
expect(subject.destroyed?).to be true
end
end
end
end

Some files were not shown because too many files have changed in this diff Show more