Add experimental server-side notification grouping (#29889)
This commit is contained in:
parent
db49b0e5e9
commit
974335e414
14 changed files with 618 additions and 0 deletions
|
@ -13,6 +13,7 @@
|
|||
# from_account_id :bigint(8) not null
|
||||
# type :string
|
||||
# filtered :boolean default(FALSE), not null
|
||||
# group_key :string
|
||||
#
|
||||
|
||||
class Notification < ApplicationRecord
|
||||
|
@ -136,6 +137,67 @@ class Notification < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
# This returns notifications from the request page, but with at most one notification per group.
|
||||
# Notifications that have no `group_key` each count as a separate group.
|
||||
def paginate_groups_by_max_id(limit, max_id: nil, since_id: nil)
|
||||
query = reorder(id: :desc)
|
||||
query = query.where(id: ...max_id) if max_id.present?
|
||||
query = query.where(id: (since_id + 1)...) if since_id.present?
|
||||
|
||||
unscoped
|
||||
.with_recursive(
|
||||
grouped_notifications: [
|
||||
query
|
||||
.select('notifications.*', "ARRAY[COALESCE(notifications.group_key, 'ungrouped-' || notifications.id)] groups")
|
||||
.limit(1),
|
||||
query
|
||||
.joins('CROSS JOIN grouped_notifications')
|
||||
.where('notifications.id < grouped_notifications.id')
|
||||
.where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
|
||||
.select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
|
||||
.limit(1),
|
||||
]
|
||||
)
|
||||
.from('grouped_notifications AS notifications')
|
||||
.order(id: :desc)
|
||||
.limit(limit)
|
||||
end
|
||||
|
||||
# Differs from :paginate_groups_by_max_id in that it gives the results immediately following min_id,
|
||||
# whereas since_id gives the items with largest id, but with since_id as a cutoff.
|
||||
# Results will be in ascending order by id.
|
||||
def paginate_groups_by_min_id(limit, max_id: nil, min_id: nil)
|
||||
query = reorder(id: :asc)
|
||||
query = query.where(id: (min_id + 1)...) if min_id.present?
|
||||
query = query.where(id: ...max_id) if max_id.present?
|
||||
|
||||
unscoped
|
||||
.with_recursive(
|
||||
grouped_notifications: [
|
||||
query
|
||||
.select('notifications.*', "ARRAY[COALESCE(notifications.group_key, 'ungrouped-' || notifications.id)] groups")
|
||||
.limit(1),
|
||||
query
|
||||
.joins('CROSS JOIN grouped_notifications')
|
||||
.where('notifications.id > grouped_notifications.id')
|
||||
.where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
|
||||
.select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
|
||||
.limit(1),
|
||||
]
|
||||
)
|
||||
.from('grouped_notifications AS notifications')
|
||||
.order(id: :asc)
|
||||
.limit(limit)
|
||||
end
|
||||
|
||||
def to_a_grouped_paginated_by_id(limit, options = {})
|
||||
if options[:min_id].present?
|
||||
paginate_groups_by_min_id(limit, min_id: options[:min_id], max_id: options[:max_id]).reverse
|
||||
else
|
||||
paginate_groups_by_max_id(limit, max_id: options[:max_id], since_id: options[:since_id]).to_a
|
||||
end
|
||||
end
|
||||
|
||||
def preload_cache_collection_target_statuses(notifications, &_block)
|
||||
notifications.group_by(&:type).each do |type, grouped_notifications|
|
||||
associations = TARGET_STATUS_INCLUDES_BY_TYPE[type]
|
||||
|
|
29
app/models/notification_group.rb
Normal file
29
app/models/notification_group.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class NotificationGroup < ActiveModelSerializers::Model
|
||||
attributes :group_key, :sample_accounts, :notifications_count, :notification
|
||||
|
||||
def self.from_notification(notification)
|
||||
if notification.group_key.present?
|
||||
# TODO: caching and preloading
|
||||
sample_accounts = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).limit(3).map(&:from_account)
|
||||
notifications_count = notification.account.notifications.where(group_key: notification.group_key).count
|
||||
else
|
||||
sample_accounts = [notification.from_account]
|
||||
notifications_count = 1
|
||||
end
|
||||
|
||||
NotificationGroup.new(
|
||||
notification: notification,
|
||||
group_key: notification.group_key || "ungrouped-#{notification.id}",
|
||||
sample_accounts: sample_accounts,
|
||||
notifications_count: notifications_count
|
||||
)
|
||||
end
|
||||
|
||||
delegate :type,
|
||||
:target_status,
|
||||
:report,
|
||||
:account_relationship_severance_event,
|
||||
to: :notification, prefix: false
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue