From 19e89cd031d33d8e20df635fabb8554a4a009852 Mon Sep 17 00:00:00 2001 From: KMY Date: Sun, 9 Apr 2023 12:36:22 +0900 Subject: [PATCH 1/4] Fix listed up muting users in status emoji_reactioned_by --- .../v1/statuses/emoji_reactioned_by_accounts_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/statuses/emoji_reactioned_by_accounts_controller.rb b/app/controllers/api/v1/statuses/emoji_reactioned_by_accounts_controller.rb index 03817c6823..8db0e06daf 100644 --- a/app/controllers/api/v1/statuses/emoji_reactioned_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/emoji_reactioned_by_accounts_controller.rb @@ -16,14 +16,15 @@ class Api::V1::Statuses::EmojiReactionedByAccountsController < Api::BaseControll def load_accounts scope = default_accounts - # scope = scope.where.not(account_id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? + scope = scope.where.not(account_id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? scope.merge(paginated_emoji_reactions).to_a end def default_accounts EmojiReaction .where(status_id: @status.id) - #.where(account: { suspended_at: nil }) + .includes(:account) + .where(account: { suspended_at: nil }) end def paginated_emoji_reactions From dadd77788dff4978d7a6fd6da191ff44d22113e2 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 10 Apr 2023 11:41:51 +0900 Subject: [PATCH 2/4] Fis search min_score --- app/services/search_service.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index b7d889904a..54b421a370 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -25,6 +25,8 @@ class SearchService < BaseService private + MIN_SCORE = 0.7 + def perform_accounts_search! AccountSearchService.new.call( @query, @@ -36,22 +38,22 @@ class SearchService < BaseService end def perform_statuses_search! - privacy_definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id })) + privacy_definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id }).min_score(MIN_SCORE)) # 'private' searchability posts are NOT in here because it's already added at previous line. case @searchability when 'public' - privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'public' })) - privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'unlisted' }).filter(terms: { account_id: following_account_ids })) unless following_account_ids.empty? - privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id })) + privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'public' }).min_score(MIN_SCORE)) + privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'unlisted' }).filter(terms: { account_id: following_account_ids }).min_score(MIN_SCORE)) unless following_account_ids.empty? + privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE)) when 'unlisted', 'private' - privacy_definition = privacy_definition.or(StatusesIndex.filter(terms: { searchability: %w(public unlisted) }).filter(terms: { account_id: following_account_ids })) unless following_account_ids.empty? - privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id })) + privacy_definition = privacy_definition.or(StatusesIndex.filter(terms: { searchability: %w(public unlisted) }).filter(terms: { account_id: following_account_ids }).min_score(MIN_SCORE)) unless following_account_ids.empty? + privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE)) when 'direct' - privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id })) + privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE)) end - definition = parsed_query.apply(StatusesIndex).order(id: :desc) + definition = parsed_query.apply(StatusesIndex.min_score(MIN_SCORE)).order(id: :desc) definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present? definition = definition.and(privacy_definition) From bc48000dceefdee9a0b64ca2364165d9cec7f1bd Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 10 Apr 2023 14:08:43 +0900 Subject: [PATCH 3/4] Add public post to unlisted setting --- app/controllers/settings/preferences_controller.rb | 1 + app/lib/user_settings_decorator.rb | 5 +++++ app/models/user.rb | 2 +- app/services/post_status_service.rb | 6 ++++-- app/views/settings/preferences/other/show.html.haml | 3 +++ config/locales/simple_form.en.yml | 1 + config/locales/simple_form.ja.yml | 2 ++ 7 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 3b2a79cdd4..f6e14ba853 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -36,6 +36,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_default_privacy, :setting_default_searchability, :setting_default_sensitive, + :setting_public_post_to_unlisted, :setting_default_language, :setting_unfollow_modal, :setting_boost_modal, diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 81aff0ee42..28784b8857 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -20,6 +20,7 @@ class UserSettingsDecorator user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy') user.settings['default_searchability']=default_searchability_preference if change?('setting_default_searchability') user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive') + user.settings['public_post_to_unlisted']=public_post_to_unlisted_preference if change?('setting_public_post_to_unlisted') user.settings['default_language'] = default_language_preference if change?('setting_default_language') user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal') user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal') @@ -63,6 +64,10 @@ class UserSettingsDecorator boolean_cast_setting 'setting_default_sensitive' end + def public_post_to_unlisted_preference + boolean_cast_setting 'setting_public_post_to_unlisted' + end + def unfollow_modal_preference boolean_cast_setting 'setting_unfollow_modal' end diff --git a/app/models/user.rb b/app/models/user.rb index df59469d1b..b782a13fa1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -137,7 +137,7 @@ class User < ApplicationRecord :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :display_media_expand, :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images, - :disable_swiping, :always_send_emails, + :disable_swiping, :always_send_emails, :public_post_to_unlisted, to: :settings, prefix: :setting, allow_nil: false delegate :can?, to: :role diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index feeb9de205..c0f15525bf 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -67,6 +67,7 @@ class PostStatusService < BaseService @text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present? @visibility = @options[:visibility] || @account.user&.setting_default_privacy @visibility = :unlisted if (@visibility&.to_sym == :public || @visibility&.to_sym == :public_unlisted) && @account.silenced? + @visibility = :public_unlisted if @visibility&.to_sym == :public && !@options[:application]&.superapp && @account.user&.setting_public_post_to_unlisted @searchability= searchability @scheduled_at = @options[:scheduled_at]&.to_datetime @scheduled_at = nil if scheduled_in_the_past? @@ -81,9 +82,10 @@ class PostStatusService < BaseService when :unlisted case @visibility&.to_sym when :public, :public_unlisted, :unlisted then :unlisted when :private then :private else :direct end when :private - case @visibility&.to_sym when :public, :public_unlisted, :unlisted, :private then :private else :direct end + # direct message also can be searched by receiver + :private when nil - @account.searchability + @account.user&.setting_default_searchability || @account.searchability else :direct end diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index 310a708e6a..8edcfb0b7f 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -25,6 +25,9 @@ .fields-group = f.input :setting_default_searchability, collection: Status.selectable_searchabilities, wrapper: :with_label, include_blank: false, label_method: lambda { |searchability| safe_join([I18n.t("statuses.searchabilities.#{searchability}"), I18n.t("statuses.searchabilities.#{searchability}_long")], ' - ') }, required: false, hint: false + .fields-group + = f.input :setting_public_post_to_unlisted, as: :boolean, wrapper: :with_label + .fields-group = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index caa5cde767..d307489744 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -218,6 +218,7 @@ en: setting_expand_spoilers: Always expand posts marked with content warnings setting_hide_network: Hide your social graph setting_noindex: Opt-out of search engine indexing + setting_public_post_to_unlisted: Convert public post to public unlisted if not using Web app setting_reduce_motion: Reduce motion in animations setting_show_application: Disclose application used to send posts setting_system_font_ui: Use system's default font diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 8d59d5dcf5..009639dbef 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -60,6 +60,7 @@ ja: setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします setting_display_media_expand: Misskeyなどは4個を超えて投稿可能です。その追加分を最大8個まで表示します。kmyblueからアップロードはできません setting_noindex: 公開プロフィールおよび各投稿ページに影響します + setting_public_post_to_unlisted: 未対応のサードパーティアプリからもローカル公開で投稿できますが、公開投稿はWeb以外できなくなります setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします @@ -219,6 +220,7 @@ ja: setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する setting_hide_network: 繋がりを隠す setting_noindex: 検索エンジンによるインデックスを拒否する + setting_public_post_to_unlisted: サードパーティアプリから投稿するとき、公開投稿をローカル公開に変更する setting_reduce_motion: アニメーションの動きを減らす setting_show_application: 送信したアプリを開示する setting_system_font_ui: システムのデフォルトフォントを使う From e6fcf1543b3ff1b2a3a062c2e1bbffa9bed44c07 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 10 Apr 2023 14:08:53 +0900 Subject: [PATCH 4/4] Add emoji reactions to trends --- app/lib/potential_friendship_tracker.rb | 2 +- app/models/emoji_reaction.rb | 8 +++++++ app/models/status.rb | 4 ++-- app/models/status_stat.rb | 21 ++++++++++++------- app/models/trends/statuses.rb | 2 +- app/services/search_service.rb | 2 +- ...d_emoji_reactions_count_to_status_stats.rb | 5 +++++ db/schema.rb | 3 ++- 8 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 db/migrate/20230410004651_add_emoji_reactions_count_to_status_stats.rb diff --git a/app/lib/potential_friendship_tracker.rb b/app/lib/potential_friendship_tracker.rb index f98acef303..b32deb1708 100644 --- a/app/lib/potential_friendship_tracker.rb +++ b/app/lib/potential_friendship_tracker.rb @@ -6,7 +6,7 @@ class PotentialFriendshipTracker WEIGHTS = { reply: 1, - emoji_reaction: 2, + emoji_reaction: 3, favourite: 10, reblog: 20, }.freeze diff --git a/app/models/emoji_reaction.rb b/app/models/emoji_reaction.rb index 85d2914d13..80d92c2424 100644 --- a/app/models/emoji_reaction.rb +++ b/app/models/emoji_reaction.rb @@ -28,6 +28,8 @@ class EmojiReaction < ApplicationRecord has_one :notification, as: :activity, dependent: :destroy + validate :status_emoji_reactions_count + after_create :refresh_cache after_destroy :refresh_cache after_destroy :invalidate_cleanup_info @@ -50,4 +52,10 @@ class EmojiReaction < ApplicationRecord query = query.where(arel_table[:id].gt(since_id)) if since_id.present? query end + + def status_emoji_reactions_count + if status && account && status.emoji_reactions.where(account: account).count >= EMOJI_REACTION_PER_ACCOUNT_LIMIT + raise Mastodon::ValidationError, I18n.t('reactions.errors.limit_reached') + end + end end diff --git a/app/models/status.rb b/app/models/status.rb index b67c0b529e..da4bbc1e9f 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -339,8 +339,8 @@ class Status < ApplicationRecord end def refresh_emoji_reactions_grouped_by_name! - generate_emoji_reactions_grouped_by_name.tap do |emoji_reactions| - update_status_stat!(emoji_reactions: emoji_reactions) + generate_emoji_reactions_grouped_by_name.tap do |emoji_reactions_json| + update_status_stat!(emoji_reactions: emoji_reactions_json, emoji_reactions_count: emoji_reactions.size) end end diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb index 7e46f02769..80e7270318 100644 --- a/app/models/status_stat.rb +++ b/app/models/status_stat.rb @@ -4,14 +4,15 @@ # # Table name: status_stats # -# id :bigint(8) not null, primary key -# status_id :bigint(8) not null -# replies_count :bigint(8) default(0), not null -# reblogs_count :bigint(8) default(0), not null -# favourites_count :bigint(8) default(0), not null -# created_at :datetime not null -# updated_at :datetime not null -# emoji_reactions :string +# id :bigint(8) not null, primary key +# status_id :bigint(8) not null +# replies_count :bigint(8) default(0), not null +# reblogs_count :bigint(8) default(0), not null +# favourites_count :bigint(8) default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# emoji_reactions :string +# emoji_reactions_count :integer default(0), not null # class StatusStat < ApplicationRecord @@ -35,6 +36,10 @@ class StatusStat < ApplicationRecord attributes['emoji_reactions'] || '' end + def emoji_reactions_count + [attributes['emoji_reactions_count'], 0].max + end + private def reset_parent_cache diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb index 84bff9c027..110c3da045 100644 --- a/app/models/trends/statuses.rb +++ b/app/models/trends/statuses.rb @@ -97,7 +97,7 @@ class Trends::Statuses < Trends::Base def calculate_scores(statuses, at_time) items = statuses.map do |status| expected = 1.0 - observed = (status.reblogs_count + status.favourites_count).to_f + observed = (status.reblogs_count + status.favourites_count + status.emoji_reactions_count * 0.3).to_f score = if expected > observed || observed < options[:threshold] 0 diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 54b421a370..15a7b033fe 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -53,7 +53,7 @@ class SearchService < BaseService privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE)) end - definition = parsed_query.apply(StatusesIndex.min_score(MIN_SCORE)).order(id: :desc) + definition = parsed_query.apply(StatusesIndex.min_score(MIN_SCORE).track_scores(true)).order(id: :desc) definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present? definition = definition.and(privacy_definition) diff --git a/db/migrate/20230410004651_add_emoji_reactions_count_to_status_stats.rb b/db/migrate/20230410004651_add_emoji_reactions_count_to_status_stats.rb new file mode 100644 index 0000000000..cd04d6c939 --- /dev/null +++ b/db/migrate/20230410004651_add_emoji_reactions_count_to_status_stats.rb @@ -0,0 +1,5 @@ +class AddEmojiReactionsCountToStatusStats < ActiveRecord::Migration[6.1] + def change + add_column :status_stats, :emoji_reactions_count, :integer, null: false, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 57541246c3..645b0d0a90 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_04_06_041523) do +ActiveRecord::Schema.define(version: 2023_04_10_004651) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -933,6 +933,7 @@ ActiveRecord::Schema.define(version: 2023_04_06_041523) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "emoji_reactions" + t.integer "emoji_reactions_count", default: 0, null: false t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true end