Merge branch 'kb_development' into kb_migration
This commit is contained in:
commit
139030f230
16 changed files with 61 additions and 25 deletions
|
@ -16,14 +16,15 @@ class Api::V1::Statuses::EmojiReactionedByAccountsController < Api::BaseControll
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
scope = default_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
|
scope.merge(paginated_emoji_reactions).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
EmojiReaction
|
EmojiReaction
|
||||||
.where(status_id: @status.id)
|
.where(status_id: @status.id)
|
||||||
#.where(account: { suspended_at: nil })
|
.includes(:account)
|
||||||
|
.where(account: { suspended_at: nil })
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_emoji_reactions
|
def paginated_emoji_reactions
|
||||||
|
|
|
@ -6,7 +6,7 @@ class PotentialFriendshipTracker
|
||||||
|
|
||||||
WEIGHTS = {
|
WEIGHTS = {
|
||||||
reply: 1,
|
reply: 1,
|
||||||
emoji_reaction: 2,
|
emoji_reaction: 3,
|
||||||
favourite: 10,
|
favourite: 10,
|
||||||
reblog: 20,
|
reblog: 20,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
|
@ -27,6 +27,10 @@ module HasUserSettings
|
||||||
settings['default_sensitive']
|
settings['default_sensitive']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def setting_public_post_to_unlisted
|
||||||
|
settings['public_post_to_unlisted']
|
||||||
|
end
|
||||||
|
|
||||||
def setting_unfollow_modal
|
def setting_unfollow_modal
|
||||||
settings['web.unfollow_modal']
|
settings['web.unfollow_modal']
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,6 +28,8 @@ class EmojiReaction < ApplicationRecord
|
||||||
|
|
||||||
has_one :notification, as: :activity, dependent: :destroy
|
has_one :notification, as: :activity, dependent: :destroy
|
||||||
|
|
||||||
|
validate :status_emoji_reactions_count
|
||||||
|
|
||||||
after_create :refresh_cache
|
after_create :refresh_cache
|
||||||
after_destroy :refresh_cache
|
after_destroy :refresh_cache
|
||||||
after_destroy :invalidate_cleanup_info
|
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 = query.where(arel_table[:id].gt(since_id)) if since_id.present?
|
||||||
query
|
query
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -336,8 +336,8 @@ class Status < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh_emoji_reactions_grouped_by_name!
|
def refresh_emoji_reactions_grouped_by_name!
|
||||||
generate_emoji_reactions_grouped_by_name.tap do |emoji_reactions|
|
generate_emoji_reactions_grouped_by_name.tap do |emoji_reactions_json|
|
||||||
update_status_stat!(emoji_reactions: emoji_reactions)
|
update_status_stat!(emoji_reactions: emoji_reactions_json, emoji_reactions_count: emoji_reactions.size)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,15 @@
|
||||||
#
|
#
|
||||||
# Table name: status_stats
|
# Table name: status_stats
|
||||||
#
|
#
|
||||||
# id :bigint(8) not null, primary key
|
# id :bigint(8) not null, primary key
|
||||||
# status_id :bigint(8) not null
|
# status_id :bigint(8) not null
|
||||||
# replies_count :bigint(8) default(0), not null
|
# replies_count :bigint(8) default(0), not null
|
||||||
# reblogs_count :bigint(8) default(0), not null
|
# reblogs_count :bigint(8) default(0), not null
|
||||||
# favourites_count :bigint(8) default(0), not null
|
# favourites_count :bigint(8) default(0), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# emoji_reactions :string
|
# emoji_reactions :string
|
||||||
|
# emoji_reactions_count :integer default(0), not null
|
||||||
#
|
#
|
||||||
|
|
||||||
class StatusStat < ApplicationRecord
|
class StatusStat < ApplicationRecord
|
||||||
|
@ -35,6 +36,10 @@ class StatusStat < ApplicationRecord
|
||||||
attributes['emoji_reactions'] || ''
|
attributes['emoji_reactions'] || ''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def emoji_reactions_count
|
||||||
|
[attributes['emoji_reactions_count'], 0].max
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def reset_parent_cache
|
def reset_parent_cache
|
||||||
|
|
|
@ -97,7 +97,7 @@ class Trends::Statuses < Trends::Base
|
||||||
def calculate_scores(statuses, at_time)
|
def calculate_scores(statuses, at_time)
|
||||||
items = statuses.map do |status|
|
items = statuses.map do |status|
|
||||||
expected = 1.0
|
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]
|
score = if expected > observed || observed < options[:threshold]
|
||||||
0
|
0
|
||||||
|
|
|
@ -16,6 +16,7 @@ class UserSettings
|
||||||
setting :default_sensitive, default: false
|
setting :default_sensitive, default: false
|
||||||
setting :default_privacy, default: nil
|
setting :default_privacy, default: nil
|
||||||
setting :default_searchability, default: :private
|
setting :default_searchability, default: :private
|
||||||
|
setting :public_post_to_unlisted, default: false
|
||||||
|
|
||||||
namespace :web do
|
namespace :web do
|
||||||
setting :crop_images, default: true
|
setting :crop_images, default: true
|
||||||
|
|
|
@ -67,6 +67,7 @@ class PostStatusService < BaseService
|
||||||
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
|
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
|
||||||
@visibility = @options[:visibility] || @account.user&.setting_default_privacy
|
@visibility = @options[:visibility] || @account.user&.setting_default_privacy
|
||||||
@visibility = :unlisted if (@visibility&.to_sym == :public || @visibility&.to_sym == :public_unlisted) && @account.silenced?
|
@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
|
@searchability= searchability
|
||||||
@scheduled_at = @options[:scheduled_at]&.to_datetime
|
@scheduled_at = @options[:scheduled_at]&.to_datetime
|
||||||
@scheduled_at = nil if scheduled_in_the_past?
|
@scheduled_at = nil if scheduled_in_the_past?
|
||||||
|
@ -81,9 +82,10 @@ class PostStatusService < BaseService
|
||||||
when :unlisted
|
when :unlisted
|
||||||
case @visibility&.to_sym when :public, :public_unlisted, :unlisted then :unlisted when :private then :private else :direct end
|
case @visibility&.to_sym when :public, :public_unlisted, :unlisted then :unlisted when :private then :private else :direct end
|
||||||
when :private
|
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
|
when nil
|
||||||
@account.searchability
|
@account.user&.setting_default_searchability || @account.searchability
|
||||||
else
|
else
|
||||||
:direct
|
:direct
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,6 +25,8 @@ class SearchService < BaseService
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
MIN_SCORE = 0.7
|
||||||
|
|
||||||
def perform_accounts_search!
|
def perform_accounts_search!
|
||||||
AccountSearchService.new.call(
|
AccountSearchService.new.call(
|
||||||
@query,
|
@query,
|
||||||
|
@ -36,22 +38,22 @@ class SearchService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform_statuses_search!
|
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.
|
# 'private' searchability posts are NOT in here because it's already added at previous line.
|
||||||
case @searchability
|
case @searchability
|
||||||
when 'public'
|
when 'public'
|
||||||
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'public' }))
|
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 })) unless following_account_ids.empty?
|
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 }))
|
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE))
|
||||||
when 'unlisted', 'private'
|
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(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 }))
|
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }).min_score(MIN_SCORE))
|
||||||
when 'direct'
|
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
|
end
|
||||||
|
|
||||||
definition = parsed_query.apply(StatusesIndex).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.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present?
|
||||||
|
|
||||||
definition = definition.and(privacy_definition)
|
definition = definition.and(privacy_definition)
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
.fields-group
|
.fields-group
|
||||||
= ff.input :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, label: I18n.t('simple_form.labels.defaults.setting_default_searchability')
|
= ff.input :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, label: I18n.t('simple_form.labels.defaults.setting_default_searchability')
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= ff.input :public_post_to_unlisted, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_public_post_to_unlisted'), hint: I18n.t('simple_form.hints.defaults.setting_public_post_to_unlisted')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
|
= ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,7 @@ en:
|
||||||
setting_expand_spoilers: Always expand posts marked with content warnings
|
setting_expand_spoilers: Always expand posts marked with content warnings
|
||||||
setting_hide_network: Hide your social graph
|
setting_hide_network: Hide your social graph
|
||||||
setting_noindex: Opt-out of search engine indexing
|
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_reduce_motion: Reduce motion in animations
|
||||||
setting_show_application: Disclose application used to send posts
|
setting_show_application: Disclose application used to send posts
|
||||||
setting_system_font_ui: Use system's default font
|
setting_system_font_ui: Use system's default font
|
||||||
|
|
|
@ -60,6 +60,7 @@ ja:
|
||||||
setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします
|
setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします
|
||||||
setting_display_media_expand: Misskeyなどは4個を超えて投稿可能です。その追加分を最大8個まで表示します。kmyblueからアップロードはできません
|
setting_display_media_expand: Misskeyなどは4個を超えて投稿可能です。その追加分を最大8個まで表示します。kmyblueからアップロードはできません
|
||||||
setting_noindex: 公開プロフィールおよび各投稿ページに影響します
|
setting_noindex: 公開プロフィールおよび各投稿ページに影響します
|
||||||
|
setting_public_post_to_unlisted: 未対応のサードパーティアプリからもローカル公開で投稿できますが、公開投稿はWeb以外できなくなります
|
||||||
setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります
|
setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります
|
||||||
setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています
|
setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています
|
||||||
setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします
|
setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします
|
||||||
|
@ -219,6 +220,7 @@ ja:
|
||||||
setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する
|
setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する
|
||||||
setting_hide_network: 繋がりを隠す
|
setting_hide_network: 繋がりを隠す
|
||||||
setting_noindex: 検索エンジンによるインデックスを拒否する
|
setting_noindex: 検索エンジンによるインデックスを拒否する
|
||||||
|
setting_public_post_to_unlisted: サードパーティアプリから投稿するとき、公開投稿をローカル公開に変更する
|
||||||
setting_reduce_motion: アニメーションの動きを減らす
|
setting_reduce_motion: アニメーションの動きを減らす
|
||||||
setting_show_application: 送信したアプリを開示する
|
setting_show_application: 送信したアプリを開示する
|
||||||
setting_system_font_ui: システムのデフォルトフォントを使う
|
setting_system_font_ui: システムのデフォルトフォントを使う
|
||||||
|
|
|
@ -8,6 +8,7 @@ class MoveUserSettings < ActiveRecord::Migration[6.1]
|
||||||
default_searchability: 'default_searchability',
|
default_searchability: 'default_searchability',
|
||||||
default_sensitive: 'web.default_sensitive',
|
default_sensitive: 'web.default_sensitive',
|
||||||
default_language: 'default_language',
|
default_language: 'default_language',
|
||||||
|
public_post_to_unlisted: 'public_post_to_unlisted',
|
||||||
noindex: 'noindex',
|
noindex: 'noindex',
|
||||||
theme: 'theme',
|
theme: 'theme',
|
||||||
trends: 'web.trends',
|
trends: 'web.trends',
|
||||||
|
|
|
@ -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
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -933,6 +933,7 @@ ActiveRecord::Schema.define(version: 2023_04_06_041523) do
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "emoji_reactions"
|
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
|
t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue