diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/has_user_settings.rb index 62c617f731..c8769ac2be 100644 --- a/app/models/concerns/has_user_settings.rb +++ b/app/models/concerns/has_user_settings.rb @@ -107,6 +107,14 @@ module HasUserSettings settings['link_preview'] end + def setting_dtl_force_with_tag + settings['dtl_force_with_tag']&.to_sym || :none + end + + def setting_dtl_force_subscribable + settings['dtl_force_subscribable'] + end + def setting_hide_statuses_count settings['hide_statuses_count'] end diff --git a/app/models/status.rb b/app/models/status.rb index 011e961848..786daa142c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -290,6 +290,10 @@ class Status < ApplicationRecord @reported ||= Report.where(target_account: account).unresolved.where('? = ANY(status_ids)', id).exists? end + def dtl? + tags.where(name: 'kmyblue').exists? + end + def emojis return @emojis if defined?(@emojis) diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 802d706191..20ac679977 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -35,6 +35,8 @@ class UserSettings setting :stop_emoji_reaction_streaming, default: false setting :emoji_reaction_streaming_notify_impl2, default: false setting :unsafe_limited_distribution, default: false + setting :dtl_force_with_tag, default: :none, in: %w(full searchability none) + setting :dtl_force_subscribable, default: false setting_inverse_alias :indexable, :noindex diff --git a/app/services/delivery_antenna_service.rb b/app/services/delivery_antenna_service.rb index 3d4f7c0b51..9c4914515d 100644 --- a/app/services/delivery_antenna_service.rb +++ b/app/services/delivery_antenna_service.rb @@ -18,6 +18,7 @@ class DeliveryAntennaService private def delivery! + must_dtl_tag = @account.dissubscribable tag_ids = @status.tags.pluck(:id) domain = @account.domain || Rails.configuration.x.local_domain follower_ids = @status.unlisted_visibility? ? @status.account.followers.pluck(:id) : [] @@ -28,9 +29,15 @@ class DeliveryAntennaService antennas = Antenna.where(id: antennas.select(:id)) antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.left_joins(:antenna_accounts).where(antenna_accounts: { account: @account })) - tag_ids = @status.tags.pluck(:id) antennas = Antenna.where(id: antennas.select(:id)) - antennas = antennas.left_joins(:antenna_tags).where(any_tags: true).or(Antenna.left_joins(:antenna_tags).where(antenna_tags: { tag_id: tag_ids })) + if must_dtl_tag + dtl_tag = Tag.find_or_create_by_names('kmyblue').first + return if !dtl_tag || tag_ids.exclude?(dtl_tag.id) + + antennas = antennas.left_joins(:antenna_tags).where(antenna_tags: { tag_id: dtl_tag.id }) + else + antennas = antennas.left_joins(:antenna_tags).where(any_tags: true).or(Antenna.left_joins(:antenna_tags).where(antenna_tags: { tag_id: tag_ids })) + end antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)) antennas = antennas.where(account: @status.account.followers) if [:public, :public_unlisted, :login, :limited].exclude?(@status.visibility.to_sym) && !@status.public_searchability? diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 865589dacd..156a811f51 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -51,7 +51,7 @@ class FanOutOnWriteService < BaseService when :public, :unlisted, :public_unlisted, :login, :private deliver_to_all_followers! deliver_to_lists! - deliver_to_antennas! unless @account.dissubscribable + deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: 'kmyblue')) deliver_to_stl_antennas! when :limited deliver_to_lists_mentioned_accounts_only! diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 22624dc913..2f56c4da0f 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -86,6 +86,7 @@ class PostStatusService < BaseService @scheduled_at = nil if scheduled_in_the_past? @reference_ids = (@options[:status_reference_ids] || []).map(&:to_i).filter(&:positive?) load_circle + overwrite_dtl_post process_sensitive_words rescue ArgumentError raise ActiveRecord::RecordInvalid @@ -99,6 +100,15 @@ class PostStatusService < BaseService raise ArgumentError if @circle.nil? || @circle.account_id != @account.id end + def overwrite_dtl_post + raw_tags = Extractor.extract_hashtags(@text) + return if raw_tags.exclude?('kmyblue') + + @visibility = :unlisted if @account.user&.setting_dtl_force_with_tag == :full + @searchability = :public if %i(full searchability).include?(@account.user&.setting_dtl_force_with_tag) + @dtl = true + end + def process_sensitive_words if [:public, :public_unlisted, :login].include?(@visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@text, @options[:spoiler_text] || '') @text = Admin::SensitiveWord.modified_text(@text, @options[:spoiler_text]) @@ -170,7 +180,7 @@ class PostStatusService < BaseService end def postprocess_status! - @account.user.update!(settings_attributes: { default_privacy: @options[:visibility] }) if @account.user&.setting_stay_privacy && !@status.reply? && %i(public public_unlisted login unlisted private).include?(@status.visibility.to_sym) && @status.visibility.to_s != @account.user&.setting_default_privacy + @account.user.update!(settings_attributes: { default_privacy: @options[:visibility] }) if @account.user&.setting_stay_privacy && !@status.reply? && %i(public public_unlisted login unlisted private).include?(@status.visibility.to_sym) && @status.visibility.to_s != @account.user&.setting_default_privacy && !@dtl process_hashtags_service.call(@status) ProcessReferencesWorker.perform_async(@status.id, @reference_ids, []) diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index 7bdce84c23..208fcd20bb 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -42,6 +42,16 @@ .fields-group = ff.input :'web.enable_login_privacy', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_enable_login_privacy'), hint: false + %h4= t 'preferences.dtl' + + %p.hint= t 'preferences.dtl_hint' + + .fields-group + = ff.input :dtl_force_with_tag, kmyblue: true, collection: ['full', 'searchability', 'none'], label_method: lambda { |item| safe_join([t("simple_form.labels.dtl_force_with_tag.#{item}")]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_with_tag'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_with_tag') + + .fields-group + = ff.input :dtl_force_subscribable, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_subscribable'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_subscribable') + %h4= t 'preferences.public_timelines' .fields-group diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 3cdaed7d03..21a148828e 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -116,7 +116,18 @@ SimpleForm.setup do |config| config.wrappers :with_floating_label, class: [:input, :with_floating_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b| b.use :html5 - b.use :label_input, wrap_with: { tag: :div, class: :label_input } + + b.wrapper tag: :div, class: :label_input do |ba| + ba.optional :recommended + ba.optional :kmyblue + ba.use :label + + ba.wrapper tag: :div, class: :label_input__wrapper do |bb| + bb.use :input + bb.optional :append, wrap_with: { tag: :div, class: 'label_input__append' } + end + end + b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :error, wrap_with: { tag: :span, class: :error } end diff --git a/config/locales/en.yml b/config/locales/en.yml index d6fd65bbeb..25a88b8738 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1611,6 +1611,8 @@ en: too_few_options: must have more than one item too_many_options: can't contain more than %{max} items preferences: + dtl: Deep timeline + dtl_hint: "You can join deep timeline with #kmyblue tag. Following settings make convenient to use deep timeline." other: Other posting_defaults: Posting defaults public_timelines: Public timelines diff --git a/config/locales/ja.yml b/config/locales/ja.yml index c5f2546149..f219a97112 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1582,6 +1582,8 @@ ja: too_few_options: は複数必要です too_many_options: は%{max}個までです preferences: + dtl: ディープタイムライン + dtl_hint: "#kmyblue ハッシュタグに参加することで、ディープタイムラインに投稿できます。ここではディープタイムラインを利用しやすくするための設定ができます。" other: その他 posting_defaults: デフォルトの投稿設定 public_timelines: 公開タイムライン diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index bf2d957c72..79d5a0e22c 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -66,6 +66,8 @@ en: setting_display_media_default: Hide media marked as sensitive setting_display_media_hide_all: Always hide media setting_display_media_show_all: Always show media + setting_dtl_force_subscribable: Your post can be detected local user's antenna to subscribe deep timeline + setting_dtl_force_with_tag: "With using #kmyblue tag, your post settings will be changed forcibly" setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed username: You can use letters, numbers, and underscores @@ -234,6 +236,8 @@ en: setting_display_media_expand: Show more medias setting_display_media_hide_all: Hide all setting_display_media_show_all: Show all + setting_dtl_force_subscribable: Ignore your dissubscribable setting when using the DTL tag + setting_dtl_force_with_tag: Post with DTL tag setting_emoji_reaction_streaming_notify_impl2: Enable stamp notification compat with Nyastodon, Catstodon, glitch-soc setting_enable_login_privacy: Enable login visibility setting_expand_spoilers: Always expand posts marked with content warnings @@ -267,6 +271,10 @@ en: username: Username username_or_email: Username or Email whole_word: Whole word + dtl_force_with_tag: + full: Visibility is unlisted, searchability is public + none: No changes + searchability: Searchability is public email_domain_block: with_dns_records: Include MX records and IPs of the domain emoji_reactions: diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 36a9628bae..3e5d4136fd 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -68,6 +68,8 @@ ja: setting_display_media_expand: Misskeyなどは4個を超えて投稿可能です。その追加分を最大16個まで表示します。kmyblueからアップロードはできません setting_display_media_hide_all: メディアを常に隠す setting_display_media_show_all: メディアを常に表示する + setting_dtl_force_subscribable: 購読拒否設定に関係なく、ディープタイムラインに向けた投稿はアンテナに掲載されます。ディープタイムラインをアンテナ経由で閲覧している人にあなたの発言が届きます + setting_dtl_force_with_tag: "ハッシュタグ #kmyblue をつけて投稿するとき、公開範囲と検索許可を強制的に置き換えるかを設定します" setting_emoji_reaction_streaming_notify_impl2: 当該サーバーの独自機能に対応したアプリを利用時に、スタンプ機能を利用できます。動作確認していないため(そもそもそのようなアプリ自体を確認できていないため)正しく動かない場合があります setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします setting_link_preview: プレビュー生成を停止することは、センシティブなサイトへのリンクを頻繁に投稿する人にも有効かもしれません @@ -244,6 +246,8 @@ ja: setting_display_media_expand: 5個目以降のメディアも表示する (最大16) setting_display_media_hide_all: 非表示 setting_display_media_show_all: 表示 + setting_dtl_force_subscribable: ディープタイムライン用のハッシュタグを購読するアンテナに限り、購読拒否設定を無視する + setting_dtl_force_with_tag: DTL参加時の投稿設定 setting_enable_login_privacy: 公開範囲「ログインユーザーのみ」をWeb UIで選択可能にする setting_emoji_reaction_streaming_notify_impl2: Nyastodon, Catstodon, glitch-soc互換のスタンプ機能を有効にする setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する @@ -278,6 +282,10 @@ ja: username: ユーザー名 username_or_email: ユーザー名またはメールアドレス whole_word: 単語全体にマッチ + dtl_force_with_tag: + full: 公開範囲「未収載」検索許可「全て」にする + none: 公開範囲も検索許可も変更しない + searchability: 検索許可を「全て」にする email_domain_block: with_dns_records: ドメインのMXレコードとIPアドレスを含む emoji_reactions: