diff options
Diffstat (limited to 'app/models/status.rb')
-rw-r--r-- | app/models/status.rb | 85 |
1 files changed, 79 insertions, 6 deletions
diff --git a/app/models/status.rb b/app/models/status.rb index 0c01a5389..c189d19bf 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 # @@ -54,6 +57,7 @@ class Status < ApplicationRecord belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true has_many :favourites, inverse_of: :status, dependent: :destroy + has_many :bookmarks, inverse_of: :status, dependent: :destroy has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread has_many :mentions, dependent: :destroy, inverse_of: :status @@ -73,6 +77,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 @@ -102,6 +107,8 @@ class Status < ApplicationRecord end } + scope :not_local_only, -> { where(local_only: [false, nil]) } + cache_associated :application, :media_attachments, :conversation, @@ -265,6 +272,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 @@ -282,8 +291,50 @@ class Status < ApplicationRecord 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 as_public_timeline(account = nil, local_only = false) - query = timeline_scope(local_only).without_replies + query = timeline_scope(local_only) + query = query.without_replies unless Setting.show_replies_in_public_timelines apply_timeline_filters(query, account, local_only) end @@ -302,6 +353,10 @@ class Status < ApplicationRecord 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 + def bookmarks_map(status_ids, account_id) + Bookmark.select('status_id').where(status_id: status_ids).where(account_id: account_id).map { |f| [f.status_id, true] }.to_h + end + def reblogs_map(status_ids, account_id) unscoped.select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).each_with_object({}) { |s, h| h[s.reblog_of_id] = true } end @@ -338,7 +393,7 @@ class Status < ApplicationRecord visibility = [:public, :unlisted] if account.nil? - where(visibility: visibility) + where(visibility: visibility).not_local_only elsif target_account.blocking?(account) # get rid of blocked peeps none elsif account.id == target_account.id # author can see own stuff @@ -360,9 +415,12 @@ class Status < ApplicationRecord def timeline_scope(local_only = false) starting_scope = local_only ? Status.local : Status - starting_scope - .with_public_visibility - .without_reblogs + starting_scope = starting_scope.with_public_visibility + if Setting.show_reblogs_in_public_timelines + starting_scope + else + starting_scope.without_reblogs + end end def apply_timeline_filters(query, account, local_only) @@ -381,7 +439,7 @@ class Status < ApplicationRecord end def filter_timeline_default(query) - query.excluding_silenced_accounts + query.not_local_only.excluding_silenced_accounts end def account_silencing_filter(account) @@ -394,6 +452,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 @@ -429,6 +496,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? |