From e3ed212b859a7bec6b26c3d52e6f7ea14069f76d Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 25 Nov 2016 12:35:21 +0100
Subject: [PATCH 1/4] Filter statuses that mention blocked users

---
 app/lib/feed_manager.rb | 26 +++++++++++++++-----------
 app/models/account.rb   |  4 ++--
 2 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index c8512476d..81489365e 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -68,30 +68,34 @@ class FeedManager
   def filter_from_home?(status, receiver)
     should_filter = false
 
-    if status.reply? && !status.thread.account.nil?                      # Filter out if it's a reply
-      should_filter   = !receiver.following?(status.thread.account)      # and I'm not following the person it's a reply to
-      should_filter &&= !(receiver.id == status.thread.account_id)       # and it's not a reply to me
-      should_filter &&= !(status.account_id == status.thread.account_id) # and it's not a self-reply
-    elsif status.reblog?                                                 # Filter out a reblog
-      should_filter = receiver.blocking?(status.reblog.account)          # if I'm blocking the reblogged person
+    if status.reply? && !status.thread.account.nil?                         # Filter out if it's a reply
+      should_filter   = !receiver.following?(status.thread.account)         # and I'm not following the person it's a reply to
+      should_filter &&= !(receiver.id == status.thread.account_id)          # and it's not a reply to me
+      should_filter &&= !(status.account_id == status.thread.account_id)    # and it's not a self-reply
+    elsif status.reblog?                                                    # Filter out a reblog
+      should_filter = receiver.blocking?(status.reblog.account)             # if I'm blocking the reblogged person
     end
 
+    should_filter ||= receiver.blocking?(status.mentions.map(&:account_id)) # or if it mentions someone I blocked
+
     should_filter
   end
 
   def filter_from_mentions?(status, receiver)
-    should_filter   = receiver.id == status.account_id            # Filter if I'm mentioning myself
-    should_filter ||= receiver.blocking?(status.account)          # or it's from someone I blocked
+    should_filter   = receiver.id == status.account_id                      # Filter if I'm mentioning myself
+    should_filter ||= receiver.blocking?(status.account)                    # or it's from someone I blocked
+    should_filter ||= receiver.blocking?(status.mentions.map(&:account_id)) # or if it mentions someone I blocked
 
-    if status.reply? && !status.thread.account.nil?               # or it's a reply
-      should_filter ||= receiver.blocking?(status.thread.account) # to a user I blocked
+    if status.reply? && !status.thread.account.nil?                         # or it's a reply
+      should_filter ||= receiver.blocking?(status.thread.account)           # to a user I blocked
     end
 
     should_filter
   end
 
   def filter_from_public?(status, receiver)
-    should_filter = receiver.blocking?(status.account)
+    should_filter   = receiver.blocking?(status.account)
+    should_filter ||= receiver.blocking?(status.mentions.map(&:account_id))
 
     if status.reply? && !status.thread.account.nil?
       should_filter ||= receiver.blocking?(status.thread.account)
diff --git a/app/models/account.rb b/app/models/account.rb
index a60a23e14..870de8b7c 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -66,12 +66,12 @@ class Account < ApplicationRecord
 
   def unfollow!(other_account)
     follow = active_relationships.find_by(target_account: other_account)
-    follow.destroy unless follow.nil?
+    follow&.destroy
   end
 
   def unblock!(other_account)
     block = block_relationships.find_by(target_account: other_account)
-    block.destroy unless block.nil?
+    block&.destroy
   end
 
   def following?(other_account)

From 71401659b812b8768fe70aa27221e3319ee1c434 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 25 Nov 2016 13:13:16 +0100
Subject: [PATCH 2/4] Fix #65 - Options to block notifications from people you
 don't follow/who don't follow you

---
 app/controllers/settings/preferences_controller.rb | 7 +++++--
 app/lib/feed_manager.rb                            | 4 ++--
 app/models/user.rb                                 | 1 +
 app/services/notify_service.rb                     | 2 ++
 app/views/settings/preferences/show.html.haml      | 4 ++++
 config/i18n-tasks.yml                              | 3 ---
 config/locales/simple_form.de.yml                  | 3 +++
 config/locales/simple_form.en.yml                  | 3 +++
 8 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index 5be8719ae..cacc03b65 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -14,7 +14,10 @@ class Settings::PreferencesController < ApplicationController
     current_user.settings(:notification_emails).favourite = user_params[:notification_emails][:favourite] == '1'
     current_user.settings(:notification_emails).mention   = user_params[:notification_emails][:mention]   == '1'
 
-    if current_user.update(user_params.except(:notification_emails))
+    current_user.settings(:interactions).must_be_follower  = user_params[:interactions][:must_be_follower]  == '1'
+    current_user.settings(:interactions).must_be_following = user_params[:interactions][:must_be_following] == '1'
+
+    if current_user.update(user_params.except(:notification_emails, :interactions))
       redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg')
     else
       render action: :show
@@ -24,6 +27,6 @@ class Settings::PreferencesController < ApplicationController
   private
 
   def user_params
-    params.require(:user).permit(:locale, notification_emails: [:follow, :reblog, :favourite, :mention])
+    params.require(:user).permit(:locale, notification_emails: [:follow, :reblog, :favourite, :mention], interactions: [:must_be_follower, :must_be_following])
   end
 end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 81489365e..b812ad1f4 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -84,7 +84,7 @@ class FeedManager
   def filter_from_mentions?(status, receiver)
     should_filter   = receiver.id == status.account_id                      # Filter if I'm mentioning myself
     should_filter ||= receiver.blocking?(status.account)                    # or it's from someone I blocked
-    should_filter ||= receiver.blocking?(status.mentions.map(&:account_id)) # or if it mentions someone I blocked
+    should_filter ||= receiver.blocking?(status.mentions.includes(:account).map(&:account)) # or if it mentions someone I blocked
 
     if status.reply? && !status.thread.account.nil?                         # or it's a reply
       should_filter ||= receiver.blocking?(status.thread.account)           # to a user I blocked
@@ -95,7 +95,7 @@ class FeedManager
 
   def filter_from_public?(status, receiver)
     should_filter   = receiver.blocking?(status.account)
-    should_filter ||= receiver.blocking?(status.mentions.map(&:account_id))
+    should_filter ||= receiver.blocking?(status.mentions.includes(:account).map(&:account))
 
     if status.reply? && !status.thread.account.nil?
       should_filter ||= receiver.blocking?(status.thread.account)
diff --git a/app/models/user.rb b/app/models/user.rb
index 366172e9a..423833d47 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,7 @@ class User < ApplicationRecord
 
   has_settings do |s|
     s.key :notification_emails, defaults: { follow: false, reblog: false, favourite: false, mention: false }
+    s.key :interactions, defaults: { must_be_follower: false, must_be_following: false }
   end
 
   def send_devise_notification(notification, *args)
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index 772adfb90..1efd326b0 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -36,6 +36,8 @@ class NotifyService < BaseService
     blocked   = false
     blocked ||= @recipient.id == @notification.from_account.id
     blocked ||= @recipient.blocking?(@notification.from_account)
+    blocked ||= (@recipient.user.settings(:interactions).must_be_follower  && !@notification.from_account.following?(@recipient))
+    blocked ||= (@recipient.user.settings(:interactions).must_be_following && !@recipient.following?(@notification.from_account))
     blocked ||= send("blocked_#{@notification.type}?")
     blocked
   end
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
index 693702ff7..db5b9fb48 100644
--- a/app/views/settings/preferences/show.html.haml
+++ b/app/views/settings/preferences/show.html.haml
@@ -12,6 +12,10 @@
     = ff.input :favourite, as: :boolean, wrapper: :with_label
     = ff.input :mention, as: :boolean, wrapper: :with_label
 
+  = f.simple_fields_for :interactions, current_user.settings(:interactions) do |ff|
+    = ff.input :must_be_follower, as: :boolean, wrapper: :with_label
+    = ff.input :must_be_following, as: :boolean, wrapper: :with_label
+
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
 
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index d345ce6c0..4dc6985b7 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -30,9 +30,6 @@ search:
     - app/assets/fonts
     - app/assets/videos
 
-ignore_missing:
-  - '{devise,simple_form}.*'
-
 ignore_unused:
   - 'activerecord.attributes.*'
   - '{devise,will_paginate,doorkeeper}.*'
diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
index 36c5141a2..90aeb1e5c 100644
--- a/config/locales/simple_form.de.yml
+++ b/config/locales/simple_form.de.yml
@@ -21,6 +21,9 @@ de:
         follow: E-mail senden, wenn mir jemand folgt
         mention: E-mail senden, wenn mich jemand erwähnt
         reblog: E-mail senden, wenn jemand meinen Beitrag teilt
+      interactions:
+        must_be_follower: Benachrichtigungen von nicht-Folgern blockieren
+        must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
     'no': Nein
     required:
       mark: "*"
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index a7d958c06..1ba7c91e2 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -21,6 +21,9 @@ en:
         follow: Send e-mail when someone follows you
         mention: Send e-mail when someone mentions you
         reblog: Send e-mail when someone reblogs your status
+      interactions:
+        must_be_follower: Block notifications from non-followers
+        must_be_following: Block notifications from people you don't follow
     'no': 'No'
     required:
       mark: "*"

From 1ff0d5aea60363e65e749d3c44e4acd4cc9b5e36 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 25 Nov 2016 13:15:07 +0100
Subject: [PATCH 3/4] Normalized locale files

---
 config/locales/simple_form.de.yml | 6 +++---
 config/locales/simple_form.en.yml | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
index 90aeb1e5c..47e30ccb4 100644
--- a/config/locales/simple_form.de.yml
+++ b/config/locales/simple_form.de.yml
@@ -16,14 +16,14 @@ de:
         password: Passwort
         silenced: Öffentliche Beiträge nicht auflisten
         username: Nutzername
+      interactions:
+        must_be_follower: Benachrichtigungen von nicht-Folgern blockieren
+        must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
       notification_emails:
         favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert
         follow: E-mail senden, wenn mir jemand folgt
         mention: E-mail senden, wenn mich jemand erwähnt
         reblog: E-mail senden, wenn jemand meinen Beitrag teilt
-      interactions:
-        must_be_follower: Benachrichtigungen von nicht-Folgern blockieren
-        must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
     'no': Nein
     required:
       mark: "*"
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 1ba7c91e2..1e975af14 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -16,14 +16,14 @@ en:
         password: Password
         silenced: Unlisted mode
         username: Username
+      interactions:
+        must_be_follower: Block notifications from non-followers
+        must_be_following: Block notifications from people you don't follow
       notification_emails:
         favourite: Send e-mail when someone favourites your status
         follow: Send e-mail when someone follows you
         mention: Send e-mail when someone mentions you
         reblog: Send e-mail when someone reblogs your status
-      interactions:
-        must_be_follower: Block notifications from non-followers
-        must_be_following: Block notifications from people you don't follow
     'no': 'No'
     required:
       mark: "*"

From 8a3745a4df89f5a5f980370dabfab8f95c92a5f8 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 25 Nov 2016 13:25:40 +0100
Subject: [PATCH 4/4] Remove stale entries from cache results

---
 app/controllers/api/v1/accounts_controller.rb      | 2 +-
 app/controllers/api/v1/notifications_controller.rb | 2 +-
 app/controllers/api/v1/timelines_controller.rb     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 5546ee588..ffa8b04fb 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -128,6 +128,6 @@ class Api::V1::AccountsController < ApiController
       end
     end
 
-    raw.map { |status| cached_keys_with_value[status.cache_key] || uncached[status.id] }
+    raw.map { |status| cached_keys_with_value[status.cache_key] || uncached[status.id] }.compact
   end
 end
diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb
index d74b99a86..b23d7570d 100644
--- a/app/controllers/api/v1/notifications_controller.rb
+++ b/app/controllers/api/v1/notifications_controller.rb
@@ -39,6 +39,6 @@ class Api::V1::NotificationsController < ApiController
       end
     end
 
-    raw.map { |notification| cached_keys_with_value[notification.cache_key] || uncached[notification.id] }
+    raw.map { |notification| cached_keys_with_value[notification.cache_key] || uncached[notification.id] }.compact
   end
 end
diff --git a/app/controllers/api/v1/timelines_controller.rb b/app/controllers/api/v1/timelines_controller.rb
index b1d7c3052..3debbdfc4 100644
--- a/app/controllers/api/v1/timelines_controller.rb
+++ b/app/controllers/api/v1/timelines_controller.rb
@@ -87,6 +87,6 @@ class Api::V1::TimelinesController < ApiController
       end
     end
 
-    raw.map { |status| cached_keys_with_value[status.cache_key] || uncached[status.id] }
+    raw.map { |status| cached_keys_with_value[status.cache_key] || uncached[status.id] }.compact
   end
 end