mirror of
https://github.com/mastodon/mastodon.git
synced 2024-11-18 04:19:27 +00:00
140 lines
4.1 KiB
Ruby
140 lines
4.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: account_conversations
|
|
#
|
|
# id :bigint(8) not null, primary key
|
|
# account_id :bigint(8)
|
|
# conversation_id :bigint(8)
|
|
# participant_account_ids :bigint(8) default([]), not null, is an Array
|
|
# status_ids :bigint(8) default([]), not null, is an Array
|
|
# last_status_id :bigint(8)
|
|
# lock_version :integer default(0), not null
|
|
# unread :boolean default(FALSE), not null
|
|
#
|
|
|
|
class AccountConversation < ApplicationRecord
|
|
include Redisable
|
|
|
|
attr_writer :participant_accounts
|
|
|
|
before_validation :set_last_status
|
|
after_commit :push_to_streaming_api
|
|
|
|
belongs_to :account
|
|
belongs_to :conversation
|
|
belongs_to :last_status, class_name: 'Status'
|
|
|
|
def participant_account_ids=(arr)
|
|
self[:participant_account_ids] = arr.sort
|
|
@participant_accounts = nil
|
|
end
|
|
|
|
def participant_accounts
|
|
@participant_accounts ||= begin
|
|
if participant_account_ids.empty?
|
|
[account]
|
|
else
|
|
participants = Account.where(id: participant_account_ids).to_a
|
|
participants.empty? ? [account] : participants
|
|
end
|
|
end
|
|
end
|
|
|
|
class << self
|
|
def to_a_paginated_by_id(limit, min_id: nil, max_id: nil, since_id: nil, preload_participants: true)
|
|
array = begin
|
|
if min_id
|
|
paginate_by_min_id(limit, min_id, max_id).reverse
|
|
else
|
|
paginate_by_max_id(limit, max_id, since_id).to_a
|
|
end
|
|
end
|
|
|
|
if preload_participants
|
|
participant_ids = array.flat_map(&:participant_account_ids)
|
|
accounts_by_id = Account.where(id: participant_ids).index_by(&:id)
|
|
|
|
array.each do |conversation|
|
|
conversation.participant_accounts = conversation.participant_account_ids.filter_map { |id| accounts_by_id[id] }
|
|
end
|
|
end
|
|
|
|
array
|
|
end
|
|
|
|
def paginate_by_min_id(limit, min_id = nil, max_id = nil)
|
|
query = order(arel_table[:last_status_id].asc).limit(limit)
|
|
query = query.where(arel_table[:last_status_id].gt(min_id)) if min_id.present?
|
|
query = query.where(arel_table[:last_status_id].lt(max_id)) if max_id.present?
|
|
query
|
|
end
|
|
|
|
def paginate_by_max_id(limit, max_id = nil, since_id = nil)
|
|
query = order(arel_table[:last_status_id].desc).limit(limit)
|
|
query = query.where(arel_table[:last_status_id].lt(max_id)) if max_id.present?
|
|
query = query.where(arel_table[:last_status_id].gt(since_id)) if since_id.present?
|
|
query
|
|
end
|
|
|
|
def add_status(recipient, status)
|
|
conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))
|
|
|
|
return conversation if conversation.status_ids.include?(status.id)
|
|
|
|
conversation.status_ids << status.id
|
|
conversation.unread = status.account_id != recipient.id
|
|
conversation.save
|
|
conversation
|
|
rescue ActiveRecord::StaleObjectError
|
|
retry
|
|
end
|
|
|
|
def remove_status(recipient, status)
|
|
conversation = find_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))
|
|
|
|
return if conversation.nil?
|
|
|
|
conversation.status_ids.delete(status.id)
|
|
|
|
if conversation.status_ids.empty?
|
|
conversation.destroy
|
|
else
|
|
conversation.save
|
|
end
|
|
|
|
conversation
|
|
rescue ActiveRecord::StaleObjectError
|
|
retry
|
|
end
|
|
|
|
private
|
|
|
|
def participants_from_status(recipient, status)
|
|
((status.active_mentions.pluck(:account_id) + [status.account_id]).uniq - [recipient.id]).sort
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def set_last_status
|
|
self.status_ids = status_ids.sort
|
|
self.last_status_id = status_ids.last
|
|
end
|
|
|
|
def push_to_streaming_api
|
|
return if destroyed? || !subscribed_to_timeline?
|
|
|
|
PushConversationWorker.perform_async(id)
|
|
end
|
|
|
|
def subscribed_to_timeline?
|
|
redis.exists?("subscribed:#{streaming_channel}")
|
|
end
|
|
|
|
def streaming_channel
|
|
"timeline:direct:#{account_id}"
|
|
end
|
|
end
|