about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-08-14 14:09:33 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:18 -0500
commit79535cb863d2f956bff2af8449f10da4aede4ede (patch)
treec609880ad160a4a8105f8076a1139d63884a83c1
parente3b316dd3e797a9c266fc1ec5e104845df1da9c6 (diff)
[Feature] Add support for unlisted and out-of-body tags
-rw-r--r--app/controllers/admin/tags_controller.rb2
-rw-r--r--app/lib/command_tag/command/status_tools.rb24
-rw-r--r--app/models/status.rb6
-rw-r--r--app/services/fan_out_on_write_service.rb7
-rw-r--r--app/services/process_hashtags_service.rb2
-rw-r--r--app/services/remove_hashtags_service.rb21
-rw-r--r--app/services/remove_status_service.rb6
-rw-r--r--app/services/revoke_status_service.rb10
-rw-r--r--app/services/update_status_service.rb12
9 files changed, 65 insertions, 25 deletions
diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb
index 59df4470e..8abe19626 100644
--- a/app/controllers/admin/tags_controller.rb
+++ b/app/controllers/admin/tags_controller.rb
@@ -54,7 +54,7 @@ module Admin
 
     def set_usage_by_domain
       @usage_by_domain = @tag.statuses
-                             .with_public_visibility
+                             .distributable
                              .excluding_silenced_accounts
                              .where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day)))
                              .joins(:account)
diff --git a/app/lib/command_tag/command/status_tools.rb b/app/lib/command_tag/command/status_tools.rb
index a323138ea..1727a956e 100644
--- a/app/lib/command_tag/command/status_tools.rb
+++ b/app/lib/command_tag/command/status_tools.rb
@@ -49,6 +49,30 @@ module CommandTag::Command::StatusTools
 
   alias handle_notice_before_save handle_notify_before_save
 
+  def handle_tags_before_save(args)
+    return if args.blank?
+
+    cmd = args.shift.downcase
+    args.select! { |tag| tag =~ /\A(#{Tag::HASHTAG_NAME_RE})\z/i }
+
+    case cmd
+    when 'add', 'a', '+'
+      ProcessHashtagsService.new.call(@status, args)
+    when 'del', 'remove', 'rm', 'r', 'd', '-'
+      RemoveHashtagsService.new.call(@status, args)
+    end
+  end
+
+  def handle_tag_before_save(args)
+    args.unshift('add')
+    handle_tags_before_save(args)
+  end
+
+  def handle_untag_before_save(args)
+    args.unshift('del')
+    handle_tags_before_save(args)
+  end
+
   private
 
   def resolve_mention(mention_text)
diff --git a/app/models/status.rb b/app/models/status.rb
index c12e72f10..e568bfed7 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -447,7 +447,7 @@ class Status < ApplicationRecord
     end
 
     def as_tag_timeline(tag, account = nil, local_only = false)
-      query = timeline_scope(local_only).tagged_with(tag)
+      query = timeline_scope(local_only, include_unlisted: true).tagged_with(tag)
 
       apply_timeline_filters(query, account, local_only)
     end
@@ -544,7 +544,7 @@ class Status < ApplicationRecord
 
     private
 
-    def timeline_scope(scope = false)
+    def timeline_scope(scope = false, include_unlisted: false)
       starting_scope = case scope
                        when :local, true
                          Status.local
@@ -555,7 +555,7 @@ class Status < ApplicationRecord
                        else
                          Status
                        end
-      starting_scope = starting_scope.with_public_visibility.published
+      starting_scope = include_unlisted ? starting_scope.distributable : starting_scope.with_public_visibility
       scope != :local_reblogs ? starting_scope.without_reblogs : starting_scope
     end
 
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 2e3061b07..8a37c7695 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -31,11 +31,8 @@ class FanOutOnWriteService < BaseService
       return
     end
 
-    return unless status.public_visibility?
-
-    deliver_to_hashtags(status)
-
-    return if status.reply? && status.in_reply_to_account_id != status.account_id && !Setting.show_replies_in_public_timelines
+    deliver_to_hashtags(status) if status.distributable?
+    return if !status.public_visibility? || (status.reply? && status.in_reply_to_account_id != status.account_id)
 
     deliver_to_media(status) if status.media_attachments.any?
     deliver_to_public(status) if status.local?
diff --git a/app/services/process_hashtags_service.rb b/app/services/process_hashtags_service.rb
index 1f0d64323..5ec5ea0c2 100644
--- a/app/services/process_hashtags_service.rb
+++ b/app/services/process_hashtags_service.rb
@@ -13,7 +13,7 @@ class ProcessHashtagsService < BaseService
       status.tags << tag
       records << tag
 
-      TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility?
+      TrendingTags.record_use!(tag, status.account, status.created_at) if status.distributable?
     end
 
     return unless status.distributable?
diff --git a/app/services/remove_hashtags_service.rb b/app/services/remove_hashtags_service.rb
new file mode 100644
index 000000000..6bf77a068
--- /dev/null
+++ b/app/services/remove_hashtags_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveHashtagsService < BaseService
+  def call(status, tags)
+    tags = status.tags.matching_name(tags) if tags.is_a?(Array)
+
+    status.account.featured_tags.where(tag: tags).each do |featured_tag|
+      featured_tag.decrement(status.id)
+    end
+
+    if status.distributable?
+      delete_payload = Oj.dump(event: :delete, payload: status.id.to_s)
+      tags.pluck(:name).each do |hashtag|
+        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", delete_payload)
+        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", delete_payload) if status.local?
+      end
+    end
+
+    status.tags -= tags
+  end
+end
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index 4d3dc13db..72ef6a9ec 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -139,7 +139,7 @@ class RemoveStatusService < BaseService
       featured_tag.decrement(@status.id)
     end
 
-    return unless @status.public_visibility?
+    return unless @status.distributable?
 
     @tags.each do |hashtag|
       redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload)
@@ -148,7 +148,7 @@ class RemoveStatusService < BaseService
   end
 
   def remove_from_public
-    return unless @status.public_visibility?
+    return unless @status.distributable?
 
     redis.publish('timeline:public', @payload)
     if @status.local?
@@ -159,7 +159,7 @@ class RemoveStatusService < BaseService
   end
 
   def remove_from_media
-    return unless @status.public_visibility?
+    return unless @status.distributable?
 
     redis.publish('timeline:public:media', @payload)
     if @status.local?
diff --git a/app/services/revoke_status_service.rb b/app/services/revoke_status_service.rb
index b7d7a6e18..95810acd2 100644
--- a/app/services/revoke_status_service.rb
+++ b/app/services/revoke_status_service.rb
@@ -12,7 +12,7 @@ class RevokeStatusService < BaseService
     @status       = status
     @account      = status.account
     @account_ids  = account_ids
-    @mentions     = status.active_mentions.where(account_id: account_ids)
+    @mentions     = status.mentions.where(account_id: account_ids)
     @reblogs      = status.reblogs.where(account_id: account_ids)
 
     RedisLock.acquire(lock_options) do |lock|
@@ -21,7 +21,7 @@ class RevokeStatusService < BaseService
         remove_from_lists
         remove_from_affected
         remove_reblogs
-        remove_from_hashtags unless @status.distributable?
+        remove_from_hashtags
         remove_from_public
         remove_from_media
         remove_from_direct if status.direct_visibility?
@@ -62,7 +62,7 @@ class RevokeStatusService < BaseService
       featured_tag.decrement(@status.id)
     end
 
-    return unless @status.public_visibility?
+    return unless @status.distributable?
 
     @tags.each do |hashtag|
       redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload)
@@ -71,7 +71,7 @@ class RevokeStatusService < BaseService
   end
 
   def remove_from_public
-    return if @status.public_visibility?
+    return if @status.distributable?
 
     redis.publish('timeline:public', @payload)
     if @status.local?
@@ -82,7 +82,7 @@ class RevokeStatusService < BaseService
   end
 
   def remove_from_media
-    return if @status.public_visibility?
+    return if @status.distributable?
 
     redis.publish('timeline:public:media', @payload)
     if @status.local?
diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb
index 015010588..d0e61d153 100644
--- a/app/services/update_status_service.rb
+++ b/app/services/update_status_service.rb
@@ -89,13 +89,11 @@ class UpdateStatusService < BaseService
       featured_tag.decrement(@status.id)
     end
 
-    if @status.public_visibility?
-      return if @deleted_tag_names.blank?
+    return unless @status.distributable? && @deleted_tag_names.present?
 
-      @deleted_tag_names.each do |hashtag|
-        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @delete_payload)
-        redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @delete_payload) if @status.local?
-      end
+    @deleted_tag_names.each do |hashtag|
+      redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @delete_payload)
+      redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @delete_payload) if @status.local?
     end
   end
 
@@ -124,7 +122,7 @@ class UpdateStatusService < BaseService
 
       @status.tags << tag
       new_tag_ids << tag.id
-      TrendingTags.record_use!(tag, @account, now) if @status.public_visibility?
+      TrendingTags.record_use!(tag, @account, now) if @status.distributable?
     end
 
     return unless @status.local? && @status.distributable?