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( current_account.notifications.without_suspended.browserable(
types: Array(browserable_params[:types]), types: Array(browserable_params[:types]),
exclude_types: Array(browserable_params[:exclude_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 end
@ -78,10 +79,10 @@ class Api::V1::NotificationsController < Api::BaseController
end end
def browserable_params def browserable_params
params.permit(:account_id, types: [], exclude_types: []) params.permit(:account_id, :include_filtered, types: [], exclude_types: [])
end end
def pagination_params(core_params) 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
end end

View File

@ -15,10 +15,15 @@ module Account::Associations
has_many :favourites, inverse_of: :account, dependent: :destroy has_many :favourites, inverse_of: :account, dependent: :destroy
has_many :bookmarks, inverse_of: :account, dependent: :destroy has_many :bookmarks, inverse_of: :account, dependent: :destroy
has_many :mentions, 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 :conversations, class_name: 'AccountConversation', dependent: :destroy, inverse_of: :account
has_many :scheduled_statuses, inverse_of: :account, dependent: :destroy 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 # Pinned statuses
has_many :status_pins, inverse_of: :account, dependent: :destroy 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 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 # account_id :bigint(8) not null
# from_account_id :bigint(8) not null # from_account_id :bigint(8) not null
# type :string # type :string
# filtered :boolean default(FALSE), not null
# #
class Notification < ApplicationRecord class Notification < ApplicationRecord
@ -89,7 +90,7 @@ class Notification < ApplicationRecord
end end
class << self 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? requested_types = if types.empty?
TYPES TYPES
else else
@ -99,6 +100,7 @@ class Notification < ApplicationRecord
requested_types -= exclude_types.map(&:to_sym) requested_types -= exclude_types.map(&:to_sym)
all.tap do |scope| 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(from_account_id: from_account_id)) if from_account_id.present?
scope.merge!(where(type: requested_types)) unless requested_types.size == TYPES.size scope.merge!(where(type: requested_types)) unless requested_types.size == TYPES.size
end end
@ -144,6 +146,8 @@ class Notification < ApplicationRecord
after_initialize :set_from_account after_initialize :set_from_account
before_validation :set_from_account before_validation :set_from_account
after_destroy :remove_from_notification_request
private private
def set_from_account def set_from_account
@ -158,4 +162,9 @@ class Notification < ApplicationRecord
self.from_account_id = activity&.id self.from_account_id = activity&.id
end end
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 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,60 +11,131 @@ class NotifyService < BaseService
status status
).freeze ).freeze
def call(recipient, type, activity) class DismissCondition
@recipient = recipient def initialize(notification)
@activity = activity @recipient = notification.account
@notification = Notification.new(account: @recipient, type: type, activity: @activity) @sender = notification.from_account
@notification = notification
end
return if recipient.user.nil? || blocked? def dismiss?
blocked = @recipient.unavailable?
blocked ||= from_self? && @notification.type != :poll
@notification.save! return blocked if message? && from_staff?
# It's possible the underlying activity has been deleted blocked ||= domain_blocking?
# between the save call and now blocked ||= @recipient.blocking?(@sender)
return if @notification.activity.nil? blocked ||= @recipient.muting_notifications?(@sender)
blocked ||= conversation_muted?
push_notification! blocked ||= blocked_mention? if message?
push_to_conversation! if direct_message? blocked
send_email! if email_needed?
rescue ActiveRecord::RecordInvalid
nil
end end
private private
def blocked_mention? def blocked_mention?
FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient) FeedManager.instance.filter?(:mentions, @notification.target_status, @recipient)
end
def following_sender?
return @following_sender if defined?(@following_sender)
@following_sender = @recipient.following?(@notification.from_account) || @recipient.requested?(@notification.from_account)
end
def optional_non_follower?
@recipient.user.settings['interactions.must_be_follower'] && !@notification.from_account.following?(@recipient)
end
def optional_non_following?
@recipient.user.settings['interactions.must_be_following'] && !following_sender?
end end
def message? def message?
@notification.type == :mention @notification.type == :mention
end end
def direct_message? def from_staff?
message? && @notification.target_status.direct_visibility? @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 end
# Returns true if the sender has been mentioned by the recipient up the thread
def response_to_recipient? def response_to_recipient?
return false if @notification.target_status.in_reply_to_id.nil? 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 statuses_that_mention_sender.positive?
!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? 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 ( 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 SELECT s.id, s.in_reply_to_id, m.id, ARRAY[s.id], 0
FROM statuses s FROM statuses s
@ -83,54 +154,52 @@ class NotifyService < BaseService
WHERE st.mention_id IS NOT NULL AND s.visibility = 3 WHERE st.mention_id IS NOT NULL AND s.visibility = 3
SQL SQL
end end
def from_staff?
@notification.from_account.local? && @notification.from_account.user.present? && @notification.from_account.user_role&.overrides?(@recipient.user_role)
end end
def optional_non_following_and_direct? def call(recipient, type, activity)
direct_message? && return if recipient.user.nil?
@recipient.user.settings['interactions.must_be_following_dm'] &&
!following_sender? &&
!response_to_recipient?
end
def hellbanned? @recipient = recipient
@notification.from_account.silenced? && !following_sender? @activity = activity
end @notification = Notification.new(account: @recipient, type: type, activity: @activity)
def from_self? # For certain conditions we don't need to create a notification at all
@recipient.id == @notification.from_account.id return if dismiss?
end
def domain_blocking? @notification.filtered = filter?
@recipient.domain_blocking?(@notification.from_account.domain) && !following_sender? @notification.save!
end
def blocked? # It's possible the underlying activity has been deleted
blocked = @recipient.unavailable? # between the save call and now
blocked ||= from_self? && @notification.type != :poll return if @notification.activity.nil?
return blocked if message? && from_staff? if @notification.filtered?
update_notification_request!
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 else
false push_notification!
push_to_conversation! if direct_message?
send_email! if email_needed?
end end
rescue ActiveRecord::RecordInvalid
nil
end
private
def dismiss?
DismissCondition.new(@notification).dismiss?
end
def filter?
FilterCondition.new(@notification).filter?
end
def update_notification_request!
return unless @notification.type == :mention
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 end
def push_notification! def push_notification!
@ -150,6 +219,10 @@ class NotifyService < BaseService
AccountConversation.add_status(@recipient, @notification.target_status) AccountConversation.add_status(@recipient, @notification.target_status)
end end
def direct_message?
@notification.type == :mention && @notification.target_status.direct_visibility?
end
def push_to_web_push_subscriptions! def push_to_web_push_subscriptions!
::Web::PushNotificationWorker.push_bulk(web_push_subscriptions.select { |subscription| subscription.pushable?(@notification) }) { |subscription| [subscription.id, @notification.id] } ::Web::PushNotificationWorker.push_bulk(web_push_subscriptions.select { |subscription| subscription.pushable?(@notification) }) { |subscription| [subscription.id, @notification.id] }
end end

View File

@ -40,11 +40,3 @@
label_method: ->(setting) { I18n.t("simple_form.labels.notification_emails.software_updates.#{setting}") }, label_method: ->(setting) { I18n.t("simple_form.labels.notification_emails.software_updates.#{setting}") },
label: I18n.t('simple_form.labels.notification_emails.software_updates.label'), label: I18n.t('simple_form.labels.notification_emails.software_updates.label'),
wrapper: :with_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: notifications:
email_events: Eventos pa notificacions per correu electronico email_events: Eventos pa notificacions per correu electronico
email_events_hint: 'Tría los eventos pa los quals deseyas recibir notificacions:' email_events_hint: 'Tría los eventos pa los quals deseyas recibir notificacions:'
other_settings: Atros achustes de notificacions
number: number:
human: human:
decimal_units: decimal_units:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1495,7 +1495,6 @@ hu:
administration_emails: Adminisztrátori e-mail-értesítések administration_emails: Adminisztrátori e-mail-értesítések
email_events: Események email értesítésekhez 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:' 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: number:
human: human:
decimal_units: decimal_units:

View File

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

View File

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

View File

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

View File

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

View File

@ -1499,7 +1499,6 @@ is:
administration_emails: Kerfisstjórnunartilkynningar í tölvupósti administration_emails: Kerfisstjórnunartilkynningar í tölvupósti
email_events: Atburðir fyrir tilkynningar í 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:' 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: number:
human: human:
decimal_units: decimal_units:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -150,6 +150,17 @@ namespace :api, format: false do
end end
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 resources :notifications, only: [:index, :show] do
collection do collection do
post :clear 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. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" 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" t.index ["target_account_id"], name: "index_mutes_on_target_account_id"
end 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| create_table "notifications", force: :cascade do |t|
t.bigint "activity_id", null: false t.bigint "activity_id", null: false
t.string "activity_type", 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 "account_id", null: false
t.bigint "from_account_id", null: false t.bigint "from_account_id", null: false
t.string "type" 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_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 ["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" t.index ["from_account_id"], name: "index_notifications_on_from_account_id"
end 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 "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", column: "target_account_id", name: "fk_eecff219ea", on_delete: :cascade
add_foreign_key "mutes", "accounts", name: "fk_b8d8daf315", 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", column: "from_account_id", name: "fk_fbd6b0bf9e", on_delete: :cascade
add_foreign_key "notifications", "accounts", name: "fk_c141c8ee55", 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 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 describe 'PUT #update' do
it 'updates notifications settings' 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 user.save
put :update, params: { put :update, params: {
user: { user: {
settings_attributes: { settings_attributes: {
'notification_emails.follow': '1', '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) expect(response).to redirect_to(settings_preferences_notifications_path)
user.reload user.reload
expect(user.settings['notification_emails.follow']).to be true expect(user.settings['notification_emails.follow']).to be true
expect(user.settings['interactions.must_be_follower']).to be false
end end
end 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