notification_group.rb 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. # frozen_string_literal: true
  2. class NotificationGroup < ActiveModelSerializers::Model
  3. attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id, :pagination_data
  4. # Try to keep this consistent with `app/javascript/mastodon/models/notification_group.ts`
  5. SAMPLE_ACCOUNTS_SIZE = 8
  6. def self.from_notifications(notifications, pagination_range: nil, grouped_types: nil)
  7. return [] if notifications.empty?
  8. grouped_types = grouped_types.presence&.map(&:to_sym) || Notification::GROUPABLE_NOTIFICATION_TYPES
  9. grouped_notifications = notifications.filter { |notification| notification.group_key.present? && grouped_types.include?(notification.type) }
  10. group_keys = grouped_notifications.pluck(:group_key)
  11. groups_data = load_groups_data(notifications.first.account_id, group_keys, pagination_range: pagination_range)
  12. accounts_map = Account.where(id: groups_data.values.pluck(1).flatten).index_by(&:id)
  13. notifications.map do |notification|
  14. if notification.group_key.present? && grouped_types.include?(notification.type)
  15. most_recent_notification_id, sample_account_ids, count, *raw_pagination_data = groups_data[notification.group_key]
  16. pagination_data = raw_pagination_data.empty? ? nil : { min_id: raw_pagination_data[0], latest_notification_at: raw_pagination_data[1] }
  17. NotificationGroup.new(
  18. notification: notification,
  19. group_key: notification.group_key,
  20. sample_accounts: sample_account_ids.map { |id| accounts_map[id] },
  21. notifications_count: count,
  22. most_recent_notification_id: most_recent_notification_id,
  23. pagination_data: pagination_data
  24. )
  25. else
  26. pagination_data = pagination_range.blank? ? nil : { min_id: notification.id, latest_notification_at: notification.created_at }
  27. NotificationGroup.new(
  28. notification: notification,
  29. group_key: "ungrouped-#{notification.id}",
  30. sample_accounts: [notification.from_account],
  31. notifications_count: 1,
  32. most_recent_notification_id: notification.id,
  33. pagination_data: pagination_data
  34. )
  35. end
  36. end
  37. end
  38. delegate :type,
  39. :target_status,
  40. :report,
  41. :account_relationship_severance_event,
  42. :account_warning,
  43. :generated_annual_report,
  44. to: :notification, prefix: false
  45. class << self
  46. private
  47. def load_groups_data(account_id, group_keys, pagination_range: nil)
  48. return {} if group_keys.empty?
  49. if pagination_range.present?
  50. binds = [
  51. account_id,
  52. SAMPLE_ACCOUNTS_SIZE,
  53. pagination_range.begin,
  54. pagination_range.end,
  55. ActiveRecord::Relation::QueryAttribute.new('group_keys', group_keys, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ActiveModel::Type::String.new)),
  56. ]
  57. ActiveRecord::Base.connection.select_all(<<~SQL.squish, 'grouped_notifications', binds).cast_values.to_h { |k, *values| [k, values] }
  58. SELECT
  59. groups.group_key,
  60. (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT 1),
  61. array(SELECT from_account_id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT $2),
  62. (SELECT count(*) FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4) AS notifications_count,
  63. (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id >= $3 ORDER BY id ASC LIMIT 1) AS min_id,
  64. (SELECT created_at FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT 1)
  65. FROM
  66. unnest($5::text[]) AS groups(group_key);
  67. SQL
  68. else
  69. binds = [
  70. account_id,
  71. SAMPLE_ACCOUNTS_SIZE,
  72. ActiveRecord::Relation::QueryAttribute.new('group_keys', group_keys, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ActiveModel::Type::String.new)),
  73. ]
  74. ActiveRecord::Base.connection.select_all(<<~SQL.squish, 'grouped_notifications', binds).cast_values.to_h { |k, *values| [k, values] }
  75. SELECT
  76. groups.group_key,
  77. (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key ORDER BY id DESC LIMIT 1),
  78. array(SELECT from_account_id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key ORDER BY id DESC LIMIT $2),
  79. (SELECT count(*) FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key) AS notifications_count
  80. FROM
  81. unnest($3::text[]) AS groups(group_key);
  82. SQL
  83. end
  84. end
  85. end
  86. end