Change e-mail notifications to only be sent when recipient is offline (#17984)
* Change e-mail notifications to only be sent when recipient is offline Change the default for follow and mention notifications back on * Add preference to always send e-mail notifications * Change wording
This commit is contained in:
parent
fd9a9b07c2
commit
8e20e16cf0
7 changed files with 58 additions and 25 deletions
|
@ -54,7 +54,8 @@ class Settings::PreferencesController < Settings::BaseController
|
||||||
:setting_use_pending_items,
|
:setting_use_pending_items,
|
||||||
:setting_trends,
|
:setting_trends,
|
||||||
:setting_crop_images,
|
:setting_crop_images,
|
||||||
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
|
:setting_always_send_emails,
|
||||||
|
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag appeal),
|
||||||
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,6 +38,7 @@ class UserSettingsDecorator
|
||||||
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
|
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
|
||||||
user.settings['trends'] = trends_preference if change?('setting_trends')
|
user.settings['trends'] = trends_preference if change?('setting_trends')
|
||||||
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
|
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
|
||||||
|
user.settings['always_send_emails'] = always_send_emails_preference if change?('setting_always_send_emails')
|
||||||
end
|
end
|
||||||
|
|
||||||
def merged_notification_emails
|
def merged_notification_emails
|
||||||
|
@ -132,6 +133,10 @@ class UserSettingsDecorator
|
||||||
boolean_cast_setting 'setting_crop_images'
|
boolean_cast_setting 'setting_crop_images'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def always_send_emails_preference
|
||||||
|
boolean_cast_setting 'setting_always_send_emails'
|
||||||
|
end
|
||||||
|
|
||||||
def boolean_cast_setting(key)
|
def boolean_cast_setting(key)
|
||||||
ActiveModel::Type::Boolean.new.cast(settings[key])
|
ActiveModel::Type::Boolean.new.cast(settings[key])
|
||||||
end
|
end
|
||||||
|
|
|
@ -130,7 +130,7 @@ class User < ApplicationRecord
|
||||||
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media,
|
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media,
|
||||||
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
|
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
|
||||||
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
|
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
|
||||||
:disable_swiping,
|
:disable_swiping, :always_send_emails,
|
||||||
to: :settings, prefix: :setting, allow_nil: false
|
to: :settings, prefix: :setting, allow_nil: false
|
||||||
|
|
||||||
attr_reader :invite_code
|
attr_reader :invite_code
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class NotifyService < BaseService
|
class NotifyService < BaseService
|
||||||
|
include Redisable
|
||||||
|
|
||||||
def call(recipient, type, activity)
|
def call(recipient, type, activity)
|
||||||
@recipient = recipient
|
@recipient = recipient
|
||||||
@activity = activity
|
@activity = activity
|
||||||
|
@ -8,10 +10,15 @@ class NotifyService < BaseService
|
||||||
|
|
||||||
return if recipient.user.nil? || blocked?
|
return if recipient.user.nil? || blocked?
|
||||||
|
|
||||||
create_notification!
|
@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_notification!
|
||||||
push_to_conversation! if direct_message?
|
push_to_conversation! if direct_message?
|
||||||
send_email! if email_enabled?
|
send_email! if email_needed?
|
||||||
rescue ActiveRecord::RecordInvalid
|
rescue ActiveRecord::RecordInvalid
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
@ -92,8 +99,8 @@ class NotifyService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocked?
|
def blocked?
|
||||||
blocked = @recipient.suspended? # Skip if the recipient account is suspended anyway
|
blocked = @recipient.suspended?
|
||||||
blocked ||= from_self? && @notification.type != :poll # Skip for interactions with self
|
blocked ||= from_self? && @notification.type != :poll
|
||||||
|
|
||||||
return blocked if message? && from_staff?
|
return blocked if message? && from_staff?
|
||||||
|
|
||||||
|
@ -117,38 +124,52 @@ class NotifyService < BaseService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_notification!
|
def push_notification!
|
||||||
@notification.save!
|
push_to_streaming_api! if subscribed_to_streaming_api?
|
||||||
|
push_to_web_push_subscriptions!
|
||||||
end
|
end
|
||||||
|
|
||||||
def push_notification!
|
def push_to_streaming_api!
|
||||||
return if @notification.activity.nil?
|
redis.publish("timeline:#{@recipient.id}:notifications", Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification)))
|
||||||
|
end
|
||||||
|
|
||||||
Redis.current.publish("timeline:#{@recipient.id}:notifications", Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification)))
|
def subscribed_to_streaming_api?
|
||||||
send_push_notifications!
|
redis.exists?("subscribed:timeline:#{@recipient.id}") || redis.exists?("subscribed:timeline:#{@recipient.id}:notifications")
|
||||||
end
|
end
|
||||||
|
|
||||||
def push_to_conversation!
|
def push_to_conversation!
|
||||||
return if @notification.activity.nil?
|
|
||||||
AccountConversation.add_status(@recipient, @notification.target_status)
|
AccountConversation.add_status(@recipient, @notification.target_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_push_notifications!
|
def push_to_web_push_subscriptions!
|
||||||
subscriptions_ids = ::Web::PushSubscription.where(user_id: @recipient.user.id)
|
::Web::PushNotificationWorker.push_bulk(web_push_subscriptions.select { |subscription| subscription.pushable?(@notification) }) { |subscription| [subscription.id, @notification.id] }
|
||||||
.select { |subscription| subscription.pushable?(@notification) }
|
end
|
||||||
.map(&:id)
|
|
||||||
|
|
||||||
::Web::PushNotificationWorker.push_bulk(subscriptions_ids) do |subscription_id|
|
def web_push_subscriptions
|
||||||
[subscription_id, @notification.id]
|
@web_push_subscriptions ||= ::Web::PushSubscription.where(user_id: @recipient.user.id).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def subscribed_to_web_push?
|
||||||
|
web_push_subscriptions.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_email!
|
def send_email!
|
||||||
return if @notification.activity.nil?
|
NotificationMailer.public_send(@notification.type, @recipient, @notification).deliver_later(wait: 2.minutes) if NotificationMailer.respond_to?(@notification.type)
|
||||||
NotificationMailer.public_send(@notification.type, @recipient, @notification).deliver_later(wait: 2.minutes)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def email_enabled?
|
def email_needed?
|
||||||
|
(!recipient_online? || always_send_emails?) && send_email_for_notification_type?
|
||||||
|
end
|
||||||
|
|
||||||
|
def recipient_online?
|
||||||
|
subscribed_to_streaming_api? || subscribed_to_web_push?
|
||||||
|
end
|
||||||
|
|
||||||
|
def always_send_emails?
|
||||||
|
@recipient.user.settings.always_send_emails
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_email_for_notification_type?
|
||||||
@recipient.user.settings.notification_emails[@notification.type.to_s]
|
@recipient.user.settings.notification_emails[@notification.type.to_s]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
= ff.input :pending_account, as: :boolean, wrapper: :with_label
|
= ff.input :pending_account, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :trending_tag, as: :boolean, wrapper: :with_label
|
= ff.input :trending_tag, as: :boolean, wrapper: :with_label
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :setting_always_send_emails, as: :boolean, wrapper: :with_label
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
|
= f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
|
||||||
= ff.input :digest, as: :boolean, wrapper: :with_label
|
= ff.input :digest, as: :boolean, wrapper: :with_label
|
||||||
|
|
|
@ -49,6 +49,7 @@ en:
|
||||||
phrase: Will be matched regardless of casing in text or content warning of a post
|
phrase: Will be matched regardless of casing in text or content warning of a post
|
||||||
scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones.
|
scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones.
|
||||||
setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts)
|
setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts)
|
||||||
|
setting_always_send_emails: Normally e-mail notifications won't be sent when you are actively using Mastodon
|
||||||
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
|
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
|
||||||
setting_display_media_default: Hide media marked as sensitive
|
setting_display_media_default: Hide media marked as sensitive
|
||||||
setting_display_media_hide_all: Always hide media
|
setting_display_media_hide_all: Always hide media
|
||||||
|
@ -151,6 +152,7 @@ en:
|
||||||
phrase: Keyword or phrase
|
phrase: Keyword or phrase
|
||||||
setting_advanced_layout: Enable advanced web interface
|
setting_advanced_layout: Enable advanced web interface
|
||||||
setting_aggregate_reblogs: Group boosts in timelines
|
setting_aggregate_reblogs: Group boosts in timelines
|
||||||
|
setting_always_send_emails: Always send e-mail notifications
|
||||||
setting_auto_play_gif: Auto-play animated GIFs
|
setting_auto_play_gif: Auto-play animated GIFs
|
||||||
setting_boost_modal: Show confirmation dialog before boosting
|
setting_boost_modal: Show confirmation dialog before boosting
|
||||||
setting_crop_images: Crop images in non-expanded posts to 16x9
|
setting_crop_images: Crop images in non-expanded posts to 16x9
|
||||||
|
|
|
@ -38,16 +38,17 @@ defaults: &defaults
|
||||||
trendable_by_default: false
|
trendable_by_default: false
|
||||||
crop_images: true
|
crop_images: true
|
||||||
notification_emails:
|
notification_emails:
|
||||||
follow: false
|
follow: true
|
||||||
reblog: false
|
reblog: false
|
||||||
favourite: false
|
favourite: false
|
||||||
mention: false
|
mention: true
|
||||||
follow_request: true
|
follow_request: true
|
||||||
digest: true
|
digest: true
|
||||||
report: true
|
report: true
|
||||||
pending_account: true
|
pending_account: true
|
||||||
trending_tag: true
|
trending_tag: true
|
||||||
appeal: true
|
appeal: true
|
||||||
|
always_send_emails: false
|
||||||
interactions:
|
interactions:
|
||||||
must_be_follower: false
|
must_be_follower: false
|
||||||
must_be_following: false
|
must_be_following: false
|
||||||
|
|
Loading…
Reference in a new issue