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.rb71
1 files changed, 70 insertions, 1 deletions
diff --git a/app/models/status.rb b/app/models/status.rb
index adb92ef91..2fe4eedb5 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -21,7 +21,10 @@
 #  account_id             :bigint(8)        not null
 #  application_id         :bigint(8)
 #  in_reply_to_account_id :bigint(8)
+#  local_only             :boolean
+#  full_status_text       :text             default(""), not null
 #  poll_id                :bigint(8)
+#  content_type           :string
 #  deleted_at             :datetime
 #  edited_at              :datetime
 #  trendable              :boolean
@@ -82,6 +85,7 @@ class Status < ApplicationRecord
   validates_with DisallowedHashtagsValidator
   validates :reblog, uniqueness: { scope: :account }, if: :reblog?
   validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
+  validates :content_type, inclusion: { in: %w(text/plain text/markdown text/html) }, allow_nil: true
 
   accepts_nested_attributes_for :poll
 
@@ -109,6 +113,8 @@ class Status < ApplicationRecord
     where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids)
   }
 
+  scope :not_local_only, -> { where(local_only: [false, nil]) }
+
   cache_associated :application,
                    :media_attachments,
                    :conversation,
@@ -217,6 +223,7 @@ class Status < ApplicationRecord
       spoiler_text: spoiler_text,
       media_attachments_changed: media_attachments_changed,
       account_id: account_id || self.account_id,
+      content_type: content_type,
       created_at: at_time || edited_at
     )
   end
@@ -288,6 +295,8 @@ class Status < ApplicationRecord
 
   around_create Mastodon::Snowflake::Callbacks
 
+  before_create :set_locality
+
   before_validation :prepare_contents, if: :local?
   before_validation :set_reblog
   before_validation :set_visibility
@@ -301,6 +310,51 @@ class Status < ApplicationRecord
       visibilities.keys - %w(direct limited)
     end
 
+    def in_chosen_languages(account)
+      where(language: nil).or where(language: account.chosen_languages)
+    end
+
+    def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
+      # direct timeline is mix of direct message from_me and to_me.
+      # 2 queries are executed with pagination.
+      # constant expression using arel_table is required for partial index
+
+      # _from_me part does not require any timeline filters
+      query_from_me = where(account_id: account.id)
+                      .where(Status.arel_table[:visibility].eq(3))
+                      .limit(limit)
+                      .order('statuses.id DESC')
+
+      # _to_me part requires mute and block filter.
+      # FIXME: may we check mutes.hide_notifications?
+      query_to_me = Status
+                    .joins(:mentions)
+                    .merge(Mention.where(account_id: account.id))
+                    .where(Status.arel_table[:visibility].eq(3))
+                    .limit(limit)
+                    .order('mentions.status_id DESC')
+                    .not_excluded_by_account(account)
+
+      if max_id.present?
+        query_from_me = query_from_me.where('statuses.id < ?', max_id)
+        query_to_me = query_to_me.where('mentions.status_id < ?', max_id)
+      end
+
+      if since_id.present?
+        query_from_me = query_from_me.where('statuses.id > ?', since_id)
+        query_to_me = query_to_me.where('mentions.status_id > ?', since_id)
+      end
+
+      if cache_ids
+        # returns array of cache_ids object that have id and updated_at
+        (query_from_me.cache_ids.to_a + query_to_me.cache_ids.to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
+      else
+        # returns ActiveRecord.Relation
+        items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
+        Status.where(id: items.map(&:id))
+      end
+    end
+
     def favourites_map(status_ids, account_id)
       Favourite.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |f, h| h[f.status_id] = true }
     end
@@ -345,7 +399,7 @@ class Status < ApplicationRecord
       visibility = [:public, :unlisted]
 
       if account.nil?
-        where(visibility: visibility)
+        where(visibility: visibility).not_local_only
       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
@@ -379,6 +433,15 @@ class Status < ApplicationRecord
     end
   end
 
+  def marked_local_only?
+    # match both with and without U+FE0F (the emoji variation selector)
+    /#{local_only_emoji}\ufe0f?\z/.match?(content)
+  end
+
+  def local_only_emoji
+    '👁'
+  end
+
   def status_stat
     super || build_status_stat
   end
@@ -414,6 +477,12 @@ class Status < ApplicationRecord
     self.sensitive  = false if sensitive.nil?
   end
 
+  def set_locality
+    if account.domain.nil? && !attribute_changed?(:local_only)
+      self.local_only = marked_local_only?
+    end
+  end
+
   def set_conversation
     self.thread = thread.reblog if thread&.reblog?