From af0b6f445c597b41e861da9e77f39b4caed3e753 Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Sun, 16 Aug 2020 21:48:42 -0500 Subject: [Filters, Timelines] Add options to toggle filters for replies to and boosts of unfollowed accounts --- .../settings/preferences/filters_controller.rb | 9 +++++ app/controllers/settings/preferences_controller.rb | 3 ++ app/lib/feed_manager.rb | 39 +++++++++++++++++----- app/lib/user_settings_decorator.rb | 10 ++++++ app/models/user.rb | 1 + .../settings/preferences/filters/show.html.haml | 22 ++++++++++++ .../settings/preferences/other/show.html.haml | 5 --- config/locales/en-MP.yml | 5 +-- config/locales/simple_form.en-MP.yml | 4 +++ config/navigation.rb | 1 + config/routes.rb | 1 + 11 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 app/controllers/settings/preferences/filters_controller.rb create mode 100644 app/views/settings/preferences/filters/show.html.haml 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: Plain-text console formatting. setting_default_content_type_bbcode_html: "[b]Bold[/b], [u]Underline[/u], [i]Italic[/i], [code]Console[/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 action menu (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] -- cgit