about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-08-16 21:48:42 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:18 -0500
commitaf0b6f445c597b41e861da9e77f39b4caed3e753 (patch)
treef8002bf3d903163ea1aa6507b1782c335c1651d3
parentf074595602e4efdd307780c942d95bb12afbffe6 (diff)
[Filters, Timelines] Add options to toggle filters for replies to and boosts of unfollowed accounts
-rw-r--r--app/controllers/settings/preferences/filters_controller.rb9
-rw-r--r--app/controllers/settings/preferences_controller.rb3
-rw-r--r--app/lib/feed_manager.rb39
-rw-r--r--app/lib/user_settings_decorator.rb10
-rw-r--r--app/models/user.rb1
-rw-r--r--app/views/settings/preferences/filters/show.html.haml22
-rw-r--r--app/views/settings/preferences/other/show.html.haml5
-rw-r--r--config/locales/en-MP.yml5
-rw-r--r--config/locales/simple_form.en-MP.yml4
-rw-r--r--config/navigation.rb1
-rw-r--r--config/routes.rb1
11 files changed, 84 insertions, 16 deletions
diff --git a/app/controllers/settings/preferences/filters_controller.rb b/app/controllers/settings/preferences/filters_controller.rb
new file mode 100644
index 000000000..c58a698ef
--- /dev/null
+++ b/app/controllers/settings/preferences/filters_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Settings::Preferences::FiltersController < Settings::PreferencesController
+  private
+
+  def after_update_redirect_path
+    settings_preferences_filters_path
+  end
+end
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index eb7d2ca07..089d94a31 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -11,6 +11,7 @@ class Settings::PreferencesController < Settings::BaseController
     user_settings.update(user_settings_params.to_h)
 
     if current_user.update(user_params)
+      Rails.cache.delete("filter_settings:#{current_user.account_id}")
       I18n.locale = current_user.locale
       redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
     else
@@ -73,6 +74,8 @@ class Settings::PreferencesController < Settings::BaseController
       :setting_boost_every,
       :setting_boost_jitter,
       :setting_boost_random,
+      :setting_filter_to_unknown,
+      :setting_filter_from_unknown,
       notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
       interactions: %i(must_be_follower must_be_following must_be_following_dm)
     )
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 0ec341e3f..88fa592a7 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -2,6 +2,7 @@
 
 require 'singleton'
 
+# rubocop:disable Metrics/ClassLength
 class FeedManager
   include Singleton
   include Redisable
@@ -23,7 +24,7 @@ class FeedManager
 
   def filter?(timeline_type, status, receiver_id)
     if [:home, :list].include?(timeline_type)
-      filter_from_home?(status, receiver_id, build_crutches(receiver_id, [status]))
+      filter_from_home?(status, receiver_id, build_crutches(receiver_id, [status]), filter_options_for(receiver_id))
     elsif timeline_type == :mentions
       filter_from_mentions?(status, receiver_id)
     elsif timeline_type == :direct
@@ -142,9 +143,10 @@ class FeedManager
 
     statuses = query.to_a
     crutches = build_crutches(into_account.id, statuses)
+    filter_options = filter_options_for(receiver_id)
 
     statuses.each do |status|
-      next if filter_from_home?(status, into_account.id, crutches)
+      next if filter_from_home?(status, into_account.id, crutches, filter_options)
 
       add_to_feed(:home, into_account.id, status, aggregate)
     end
@@ -242,9 +244,9 @@ class FeedManager
       (context == :home ? Mute.where(account_id: receiver_id, target_account_id: account_ids).any? : Mute.where(account_id: receiver_id, target_account_id: account_ids, hide_notifications: true).any?)
   end
 
-  def filter_from_home?(status, receiver_id, crutches)
+  def filter_from_home?(status, receiver_id, crutches, filter_options)
     return false if receiver_id == status.account_id
-    return true  unless status.published? && (status.conversation&.public? || crutches[:following][status.conversation&.account_id])
+    return true  unless status.published? && (status.conversation&.public? || status.conversation&.account_id == receiver_id || crutches[:following][status.conversation&.account_id])
     return true  if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?)
     return true  if phrase_filtered?(status, receiver_id, :home)
 
@@ -264,18 +266,25 @@ class FeedManager
 
     if status.reply? && !status.in_reply_to_account_id.nil?
       should_filter   = receiver_id != status.in_reply_to_account_id
+      should_filter &&= receiver_id != status.conversation&.account_id
       should_filter &&= status.account_id != status.in_reply_to_account_id
-      should_filter &&= !(crutches[:following][status.in_reply_to_account_id] && crutches[:following][status.conversation&.account_id])
+      should_filter &&= if filter_options[:to_unknown]
+                          !(crutches[:following][status.in_reply_to_account_id] && crutches[:following][status.conversation&.account_id])
+                        else
+                          !crutches[:following][status.in_reply_to_account_id]
+                        end
 
       return !!should_filter
     elsif status.reblog?
       should_filter   = status.reblog.reply?
       should_filter &&= status.reblog.account_id != status.reblog.in_reply_to_account_id
-      should_filter &&= !(crutches[:following][status.reblog.in_reply_to_account_id] && crutches[:following][status.reblog.conversation&.account_id])
-
-      should_filter ||= !crutches[:following][status.reblog.account_id] if status.reblog.account.silenced?
-      should_filter ||= !crutches[:following][status.reblog.conversation&.account_id] if status.reblog.conversation&.account&.silenced?
+      should_filter &&= if filter_options[:to_unknown]
+                          !(crutches[:following][status.reblog.in_reply_to_account_id] && crutches[:following][status.reblog.conversation&.account_id])
+                        else
+                          !crutches[:following][status.reblog.in_reply_to_account_id]
+                        end
 
+      should_filter ||= !crutches[:following][status.reblog.account_id] if filter_options[:from_account] || status.reblog.account.silenced?
       should_filter ||= crutches[:hiding_reblogs][status.account_id]
       should_filter ||= crutches[:blocked_by][status.reblog.account_id]
       should_filter ||= crutches[:domain_blocking][status.reblog.account.domain]
@@ -423,6 +432,17 @@ class FeedManager
     redis.zrem(timeline_key, status.id)
   end
 
+  def filter_options_for(receiver_id)
+    Rails.cache.fetch("filter_settings:#{receiver_id}", expires_in: 1.month) do
+      return {} if (settings = User.find_by(account_id: receiver_id)&.settings).blank?
+
+      {
+        to_unknown: settings.filter_to_unknown,
+        from_unknown: settings.filter_from_unknown,
+      }
+    end
+  end
+
   def build_crutches(receiver_id, statuses)
     crutches = {}
 
@@ -450,3 +470,4 @@ class FeedManager
     crutches
   end
 end
+# rubocop:enable Metrics/ClassLength
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 28f40dc73..7b67a5a58 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -60,6 +60,8 @@ class UserSettingsDecorator
     user.settings['boost_every']         = boost_every_preference if change?('setting_boost_every')
     user.settings['boost_jitter']        = boost_jitter_preference if change?('setting_boost_jitter')
     user.settings['boost_random']        = boost_random_preference if change?('setting_boost_random')
+    user.settings['filter_to_unknown']   = filter_to_unknown_preference if change?('setting_filter_to_unknown')
+    user.settings['filter_from_unknown'] = filter_from_unknown_preference if change?('setting_filter_from_unknown')
   end
 
   def merged_notification_emails
@@ -226,6 +228,14 @@ class UserSettingsDecorator
     boolean_cast_setting 'setting_boost_random'
   end
 
+  def filter_to_unknown_preference
+    boolean_cast_setting 'setting_filter_to_unknown'
+  end
+
+  def filter_from_unknown_preference
+    boolean_cast_setting 'setting_filter_from_unknown'
+  end
+
   def boolean_cast_setting(key)
     ActiveModel::Type::Boolean.new.cast(settings[key])
   end
diff --git a/app/models/user.rb b/app/models/user.rb
index 8e10d6e9d..9d1af7db6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -119,6 +119,7 @@ class User < ApplicationRecord
            :style_wide_media,
            :publish_in, :unpublish_in, :unpublish_delete, :boost_every, :boost_jitter,
            :boost_random,
+           :filter_to_unknown, :filter_from_unknown,
            to: :settings, prefix: :setting, allow_nil: false
 
   attr_reader :invite_code, :sign_in_token_attempt
diff --git a/app/views/settings/preferences/filters/show.html.haml b/app/views/settings/preferences/filters/show.html.haml
new file mode 100644
index 000000000..f91010724
--- /dev/null
+++ b/app/views/settings/preferences/filters/show.html.haml
@@ -0,0 +1,22 @@
+- content_for :page_title do
+  = t('settings.preferences')
+
+- content_for :heading_actions do
+  = button_tag t('generic.save_changes'), class: 'button', form: 'edit_preferences'
+
+= simple_form_for current_user, url: settings_preferences_filters_path, html: { method: :put, id: 'edit_preferences' } do |f|
+  = render 'shared/error_messages', object: current_user
+
+  %h4= t 'preferences.filtering'
+
+  .fields-group
+    = f.input :setting_filter_to_unknown, as: :boolean, wrapper: :with_label
+    = f.input :setting_filter_from_unknown, as: :boolean, wrapper: :with_label
+
+  %h4= t 'preferences.public_timelines'
+
+  .fields-group
+    = f.input :chosen_languages, collection: filterable_languages.sort, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+
+  .actions
+    = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml
index efe8cd0db..87a03a515 100644
--- a/app/views/settings/preferences/other/show.html.haml
+++ b/app/views/settings/preferences/other/show.html.haml
@@ -41,10 +41,5 @@
   .fields-group
     = f.input :setting_default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
-  %h4= t 'preferences.public_timelines'
-
-  .fields-group
-    = f.input :chosen_languages, collection: filterable_languages.sort, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
-
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/config/locales/en-MP.yml b/config/locales/en-MP.yml
index 26931ffe3..18ce2d7c4 100644
--- a/config/locales/en-MP.yml
+++ b/config/locales/en-MP.yml
@@ -68,7 +68,7 @@ en-MP:
   appearance:
     toot_layout: Roar layout
     custom_css: Custom CSS
-    custom_css_error: "There are problems with the above CSS that must be fixed before it can be applied:"    
+    custom_css_error: "There are problems with the above CSS that must be fixed before it can be applied:"
   auth:
     description:
       prefix_invited_by_user: "@%{name} invites you to join Monsterpit!"
@@ -98,6 +98,7 @@ en-MP:
       title: New roar
   preferences:
     advanced_publishing: Advanced publishing options
+    filtering: Filtering options
   remote_interaction:
     favourite:
       proceed: Proceed to admire
@@ -141,4 +142,4 @@ en-MP:
     warning:
       explanation:
         silence: While your account is limited, only creatures who are already following you will see your roars on this server, and you may be excluded from various public listings. However, others may still manually follow you.
-        suspend: Your account has been suspended, and all of your roars and your uploaded media files have been irreversibly removed from this server, and servers where you had followers.
\ No newline at end of file
+        suspend: Your account has been suspended, and all of your roars and your uploaded media files have been irreversibly removed from this server, and servers where you had followers.
diff --git a/config/locales/simple_form.en-MP.yml b/config/locales/simple_form.en-MP.yml
index 8b9e09b65..f442203cc 100644
--- a/config/locales/simple_form.en-MP.yml
+++ b/config/locales/simple_form.en-MP.yml
@@ -26,6 +26,8 @@ en-MP:
         setting_default_content_type_console_html: <code>Plain-text console formatting.</code>
         setting_default_content_type_bbcode_html: "<strong>[b]Bold[/b]</strong>, <u>[u]Underline[/u]</u>, <em>[i]Italic[/i]</em>, <code>[code]Console[/code]</code>, ..."
         setting_default_language: The language of your roars can be detected automatically, but it's not always accurate
+        setting_filter_to_unknown: Do not show replies to unfollowed accounts on your home timeline.  Takes effect for newly-pushed items.
+        setting_filter_from_unknown: Do not show boosts from unfollowed accounts on your home timeline.  Takes effect for newly-pushed items.
         setting_manual_publish: This allows you to draft, proofread, and edit your roars before publishing them.  You can publish a roar from its <strong>action menu</strong> (the three dots).
         setting_show_application: The application you use to toot will be displayed in the detailed view of your roars
         setting_skin: Reskins the selected UI flavour
@@ -51,6 +53,8 @@ en-MP:
         setting_display_media_show_all: Reveal all
         setting_expand_spoilers: Always expand roars marked with content warnings
         setting_favourite_modal: Show confirmation dialog before admiring (applies to Glitch flavour only)
+        setting_filter_to_unknown: Filter replies to unfollowed accounts
+        setting_filter_to_unknown: Filter boosts from unfollowed accounts
         setting_manual_publish: Manually publish roars
         setting_publish_in: Auto-publish
         setting_show_application: Disclose application used to send roars
diff --git a/config/navigation.rb b/config/navigation.rb
index bd172f25f..49049ef4f 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -13,6 +13,7 @@ SimpleNavigation::Configuration.run do |navigation|
     n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_url, if: -> { current_user.functional? } do |s|
       s.item :appearance, safe_join([fa_icon('desktop fw'), t('settings.appearance')]), settings_preferences_appearance_url
       s.item :notifications, safe_join([fa_icon('bell fw'), t('settings.notifications')]), settings_preferences_notifications_url
+      s.item :filters, safe_join([fa_icon('filter fw'), t('preferences.filters')]), settings_preferences_filters_url
       s.item :other, safe_join([fa_icon('cog fw'), t('preferences.other')]), settings_preferences_other_url
     end
 
diff --git a/config/routes.rb b/config/routes.rb
index cdfb4a77a..74ed750a4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -117,6 +117,7 @@ Rails.application.routes.draw do
       resource :appearance, only: [:show, :update], controller: :appearance
       resource :notifications, only: [:show, :update]
       resource :other, only: [:show, :update], controller: :other
+      resource :filters, only: [:show, :update], controller: :filters
     end
 
     resource :import, only: [:show, :create]