account_interactions.rb 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # frozen_string_literal: true
  2. module AccountInteractions
  3. extend ActiveSupport::Concern
  4. class_methods do
  5. def following_map(target_account_ids, account_id)
  6. Follow.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow, mapping|
  7. mapping[follow.target_account_id] = {
  8. reblogs: follow.show_reblogs?,
  9. }
  10. end
  11. end
  12. def followed_by_map(target_account_ids, account_id)
  13. follow_mapping(Follow.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
  14. end
  15. def blocking_map(target_account_ids, account_id)
  16. follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
  17. end
  18. def blocked_by_map(target_account_ids, account_id)
  19. follow_mapping(Block.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
  20. end
  21. def muting_map(target_account_ids, account_id)
  22. Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping|
  23. mapping[mute.target_account_id] = {
  24. notifications: mute.hide_notifications?,
  25. }
  26. end
  27. end
  28. def requested_map(target_account_ids, account_id)
  29. FollowRequest.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow_request, mapping|
  30. mapping[follow_request.target_account_id] = {
  31. reblogs: follow_request.show_reblogs?,
  32. }
  33. end
  34. end
  35. def endorsed_map(target_account_ids, account_id)
  36. follow_mapping(AccountPin.where(account_id: account_id, target_account_id: target_account_ids), :target_account_id)
  37. end
  38. def domain_blocking_map(target_account_ids, account_id)
  39. accounts_map = Account.where(id: target_account_ids).select('id, domain').each_with_object({}) { |a, h| h[a.id] = a.domain }
  40. blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
  41. accounts_map.reduce({}) { |h, (id, domain)| h.merge(id => blocked_domains[domain]) }
  42. end
  43. def domain_blocking_map_by_domain(target_domains, account_id)
  44. follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
  45. end
  46. private
  47. def follow_mapping(query, field)
  48. query.pluck(field).each_with_object({}) { |id, mapping| mapping[id] = true }
  49. end
  50. end
  51. included do
  52. # Follow relations
  53. has_many :follow_requests, dependent: :destroy
  54. has_many :active_relationships, class_name: 'Follow', foreign_key: 'account_id', dependent: :destroy
  55. has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_account_id', dependent: :destroy
  56. has_many :following, -> { order('follows.id desc') }, through: :active_relationships, source: :target_account
  57. has_many :followers, -> { order('follows.id desc') }, through: :passive_relationships, source: :account
  58. # Block relationships
  59. has_many :block_relationships, class_name: 'Block', foreign_key: 'account_id', dependent: :destroy
  60. has_many :blocking, -> { order('blocks.id desc') }, through: :block_relationships, source: :target_account
  61. has_many :blocked_by_relationships, class_name: 'Block', foreign_key: :target_account_id, dependent: :destroy
  62. has_many :blocked_by, -> { order('blocks.id desc') }, through: :blocked_by_relationships, source: :account
  63. # Mute relationships
  64. has_many :mute_relationships, class_name: 'Mute', foreign_key: 'account_id', dependent: :destroy
  65. has_many :muting, -> { order('mutes.id desc') }, through: :mute_relationships, source: :target_account
  66. has_many :muted_by_relationships, class_name: 'Mute', foreign_key: :target_account_id, dependent: :destroy
  67. has_many :muted_by, -> { order('mutes.id desc') }, through: :muted_by_relationships, source: :account
  68. has_many :conversation_mutes, dependent: :destroy
  69. has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
  70. has_many :announcement_mutes, dependent: :destroy
  71. end
  72. def follow!(other_account, reblogs: nil, uri: nil, rate_limit: false)
  73. reblogs = true if reblogs.nil?
  74. rel = active_relationships.create_with(show_reblogs: reblogs, uri: uri, rate_limit: rate_limit)
  75. .find_or_create_by!(target_account: other_account)
  76. rel.update!(show_reblogs: reblogs)
  77. remove_potential_friendship(other_account)
  78. rel
  79. end
  80. def request_follow!(other_account, reblogs: nil, uri: nil, rate_limit: false)
  81. reblogs = true if reblogs.nil?
  82. rel = follow_requests.create_with(show_reblogs: reblogs, uri: uri, rate_limit: rate_limit)
  83. .find_or_create_by!(target_account: other_account)
  84. rel.update!(show_reblogs: reblogs)
  85. remove_potential_friendship(other_account)
  86. rel
  87. end
  88. def block!(other_account, uri: nil)
  89. remove_potential_friendship(other_account)
  90. block_relationships.create_with(uri: uri)
  91. .find_or_create_by!(target_account: other_account)
  92. end
  93. def mute!(other_account, notifications: nil)
  94. notifications = true if notifications.nil?
  95. mute = mute_relationships.create_with(hide_notifications: notifications).find_or_create_by!(target_account: other_account)
  96. remove_potential_friendship(other_account)
  97. # When toggling a mute between hiding and allowing notifications, the mute will already exist, so the find_or_create_by! call will return the existing Mute without updating the hide_notifications attribute. Therefore, we check that hide_notifications? is what we want and set it if it isn't.
  98. if mute.hide_notifications? != notifications
  99. mute.update!(hide_notifications: notifications)
  100. end
  101. mute
  102. end
  103. def mute_conversation!(conversation)
  104. conversation_mutes.find_or_create_by!(conversation: conversation)
  105. end
  106. def block_domain!(other_domain)
  107. domain_blocks.find_or_create_by!(domain: other_domain)
  108. end
  109. def unfollow!(other_account)
  110. follow = active_relationships.find_by(target_account: other_account)
  111. follow&.destroy
  112. end
  113. def unblock!(other_account)
  114. block = block_relationships.find_by(target_account: other_account)
  115. block&.destroy
  116. end
  117. def unmute!(other_account)
  118. mute = mute_relationships.find_by(target_account: other_account)
  119. mute&.destroy
  120. end
  121. def unmute_conversation!(conversation)
  122. mute = conversation_mutes.find_by(conversation: conversation)
  123. mute&.destroy!
  124. end
  125. def unblock_domain!(other_domain)
  126. block = domain_blocks.find_by(domain: other_domain)
  127. block&.destroy
  128. end
  129. def following?(other_account)
  130. active_relationships.where(target_account: other_account).exists?
  131. end
  132. def blocking?(other_account)
  133. block_relationships.where(target_account: other_account).exists?
  134. end
  135. def domain_blocking?(other_domain)
  136. domain_blocks.where(domain: other_domain).exists?
  137. end
  138. def muting?(other_account)
  139. mute_relationships.where(target_account: other_account).exists?
  140. end
  141. def muting_conversation?(conversation)
  142. conversation_mutes.where(conversation: conversation).exists?
  143. end
  144. def muting_notifications?(other_account)
  145. mute_relationships.where(target_account: other_account, hide_notifications: true).exists?
  146. end
  147. def muting_reblogs?(other_account)
  148. active_relationships.where(target_account: other_account, show_reblogs: false).exists?
  149. end
  150. def requested?(other_account)
  151. follow_requests.where(target_account: other_account).exists?
  152. end
  153. def favourited?(status)
  154. status.proper.favourites.where(account: self).exists?
  155. end
  156. def bookmarked?(status)
  157. status.proper.bookmarks.where(account: self).exists?
  158. end
  159. def reblogged?(status)
  160. status.proper.reblogs.where(account: self).exists?
  161. end
  162. def pinned?(status)
  163. status_pins.where(status: status).exists?
  164. end
  165. def endorsed?(account)
  166. account_pins.where(target_account: account).exists?
  167. end
  168. def followers_for_local_distribution
  169. followers.local
  170. .joins(:user)
  171. .where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
  172. end
  173. def lists_for_local_distribution
  174. lists.joins(account: :user)
  175. .where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
  176. end
  177. private
  178. def remove_potential_friendship(other_account, mutual = false)
  179. PotentialFriendshipTracker.remove(id, other_account.id)
  180. PotentialFriendshipTracker.remove(other_account.id, id) if mutual
  181. end
  182. end