about summary refs log tree commit diff
path: root/app/models/status.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/status.rb')
-rw-r--r--app/models/status.rb98
1 files changed, 80 insertions, 18 deletions
diff --git a/app/models/status.rb b/app/models/status.rb
index e568bfed7..d31e5668e 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -135,6 +135,10 @@ class Status < ApplicationRecord
   scope :without_semiprivate, -> { where(semiprivate: false) }
   scope :reblogs, -> { where('statuses.reblog_of_id IS NOT NULL') }
   scope :locally_reblogged, -> { where(id: Status.unscoped.local.reblogs.select(:reblog_of_id)) }
+  scope :public_conversations, -> { joins(:conversation).where(conversations: { public: true }) }
+  scope :conversations_by, ->(account) { joins(:conversation).where(conversations: { account: account }) }
+  scope :mentioning_account, ->(account) { joins(:mentions).where(mentions: { account: account }) }
+  scope :replies, -> { where(reply: true).where('statuses.in_reply_to_account_id != statuses.account_id') }
 
   scope :not_hidden_by_account, ->(account) do
     left_outer_joins(:mutes, :conversation_mute).where('(status_mutes.account_id IS NULL OR status_mutes.account_id != ?) AND (conversation_mutes.account_id IS NULL OR (conversation_mutes.account_id != ? AND conversation_mutes.hidden = TRUE))', account.id, account.id)
@@ -504,27 +508,27 @@ class Status < ApplicationRecord
       end
     end
 
-    def permitted_for(target_account, account, user_signed_in: false)
-      visibility = user_signed_in || target_account.show_unlisted? ? [:public, :unlisted] : :public
+    def permitted_for(target_account, account, **options)
+      visibility = [:public, :unlisted]
 
-      if account.nil?
-        where(visibility: visibility).not_local_only.published
-      elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
-        none
-      elsif account.id == target_account.id # author can see own stuff
-        all
-      else
-        # followers can see followers-only stuff, but also things they are mentioned in.
-        # non-followers can see everything that isn't private/direct, but can see stuff they are mentioned in.
-        visibility.push(:private) if account.following?(target_account) && user_signed_in
-        scope = left_outer_joins(:reblog)
-
-        scope = scope.where(visibility: visibility)
-                     .or(scope.where(id: account.mentions.select(:status_id)))
-                     .merge(scope.where(reblog_of_id: nil).or(scope.where.not(reblogs_statuses: { account_id: account.excluded_from_timeline_account_ids })))
+      if account.present?
+        return none if target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain))
+        return apply_category_filters(all, target_account, account, **options) if account.id == target_account.id
 
-        apply_timeline_filters(scope.published, account, false)
+        visibility.push(:private) if account.following?(target_account)
+      elsif !target_account.show_unlisted?
+        visibility = :public
       end
+
+      scope = where(visibility: visibility)
+      scope = apply_account_filters(scope, account, **options)
+      apply_category_filters(scope, target_account, account, **options)
+    end
+
+    def mentions_between(account, target_account)
+      return none if account.blank? || target_account.blank?
+
+      account.statuses.mentioning_account(target_account).or(target_account.statuses.mentioning_account(account))
     end
 
     def from_text(text)
@@ -544,6 +548,64 @@ class Status < ApplicationRecord
 
     private
 
+    # TODO: Cast cleanup spell.
+    # rubocop:disable Metrics/PerceivedComplexity
+    def apply_category_filters(query, target_account, account, **options)
+      return query if options[:without_category_filters]
+
+      query = query.published unless options[:include_unpublished]
+
+      if options[:only_reblogs]
+        query = query.joins(:reblog)
+        if account.present? && account.excluded_from_timeline_account_ids.present?
+          query = query.where.not(
+            reblogs_statuses: { account_id: account.excluded_from_timeline_account_ids }
+          )
+        end
+      elsif target_account.id == account&.id
+        query = query.without_replies unless options[:include_replies] || options[:only_replies]
+        query = query.without_reblogs unless options[:include_reblogs] || options[:only_reblogs]
+        query = query.reblogs if options[:only_reblogs]
+        query = query.replies if options[:only_replies]
+      else
+        if options[:include_reblogs] && account.present? && account.excluded_from_timeline_account_ids.present?
+          query = query.left_outer_joins(:reblog).where(
+            '(statuses.reblog_of_id IS NULL OR reblogs_statuses.account_id NOT IN (?))',
+            account.excluded_from_timeline_account_ids
+          )
+        elsif !options[:include_reblogs]
+          query = query.without_reblogs
+        end
+
+        query = if options[:only_replies]
+                  query.replies
+                elsif options[:include_replies]
+                  if target_account.present?
+                    query.public_conversations.or(query.conversations_by(target_account))
+                  else
+                    query.public_conversations
+                  end
+                else
+                  query.without_replies
+                end
+      end
+
+      return query if options[:tag].blank?
+
+      (tag = Tag.find_normalized(options[:tag])) ? query.merge(Status.tagged_with(tag.id)) : none
+    end
+    # rubocop:enable Metrics/PerceivedComplexity
+
+    def apply_account_filters(query, account, **options)
+      return query.not_local_only if account.blank?
+      return (account.local? ? query : query.not_local_only) if options[:without_account_filters]
+
+      query = query.not_local_only unless account.local?
+      query = query.not_hidden_by_account(account)
+      query = query.in_chosen_languages(account) if account.chosen_languages.present?
+      query
+    end
+
     def timeline_scope(scope = false, include_unlisted: false)
       starting_scope = case scope
                        when :local, true