FEATURE: consolidate group membership request notifications.
This commit is contained in:
parent
c54be3ff25
commit
481c8314f0
|
@ -36,7 +36,8 @@ const REPLACEMENTS = {
|
||||||
"notification.watching_first_post": "far-dot-circle",
|
"notification.watching_first_post": "far-dot-circle",
|
||||||
"notification.group_message_summary": "users",
|
"notification.group_message_summary": "users",
|
||||||
"notification.post_approved": "check",
|
"notification.post_approved": "check",
|
||||||
"notification.membership_request_accepted": "user-plus"
|
"notification.membership_request_accepted": "user-plus",
|
||||||
|
"notification.membership_request_consolidated": "users"
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: use lib/svg_sprite/fa4-renames.json here
|
// TODO: use lib/svg_sprite/fa4-renames.json here
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createWidgetFrom } from "discourse/widgets/widget";
|
||||||
|
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
||||||
|
import { userPath } from "discourse/lib/url";
|
||||||
|
|
||||||
|
createWidgetFrom(
|
||||||
|
DefaultNotificationItem,
|
||||||
|
"membership-request-consolidated-notification-item",
|
||||||
|
{
|
||||||
|
url() {
|
||||||
|
return userPath(
|
||||||
|
`${this.attrs.username || this.currentUser.username}/messages`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
text(notificationName, data) {
|
||||||
|
return I18n.t(
|
||||||
|
"notifications.membership_request_consolidated",
|
||||||
|
{
|
||||||
|
group_name: data.group_name,
|
||||||
|
count: parseInt(data.count, 10)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
|
@ -4,6 +4,9 @@ class Notification < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
|
|
||||||
|
MEMBERSHIP_REQUEST_CONSOLIDATION_WINDOW_HOURS = 24
|
||||||
|
MEMBERSHIP_REQUEST_CONSOLIDATION_THRESHOLD = 3
|
||||||
|
|
||||||
validates_presence_of :data
|
validates_presence_of :data
|
||||||
validates_presence_of :notification_type
|
validates_presence_of :notification_type
|
||||||
|
|
||||||
|
@ -20,11 +23,15 @@ class Notification < ActiveRecord::Base
|
||||||
|
|
||||||
attr_accessor :skip_send_email
|
attr_accessor :skip_send_email
|
||||||
|
|
||||||
after_commit :send_email, on: :create
|
|
||||||
after_commit :refresh_notification_count, on: [:create, :update, :destroy]
|
after_commit :refresh_notification_count, on: [:create, :update, :destroy]
|
||||||
|
|
||||||
after_commit(on: :create) do
|
after_commit(on: :create) do
|
||||||
|
consolidated = consolidate_membership_requests
|
||||||
|
|
||||||
|
unless consolidated
|
||||||
DiscourseEvent.trigger(:notification_created, self)
|
DiscourseEvent.trigger(:notification_created, self)
|
||||||
|
send_email
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
|
@ -66,7 +73,8 @@ class Notification < ActiveRecord::Base
|
||||||
liked_consolidated: 19,
|
liked_consolidated: 19,
|
||||||
post_approved: 20,
|
post_approved: 20,
|
||||||
code_review_commit_approved: 21,
|
code_review_commit_approved: 21,
|
||||||
membership_request_accepted: 22
|
membership_request_accepted: 22,
|
||||||
|
membership_request_consolidated: 23
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -226,6 +234,70 @@ class Notification < ActiveRecord::Base
|
||||||
NotificationEmailer.process_notification(self) if !skip_send_email
|
NotificationEmailer.process_notification(self) if !skip_send_email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def consolidate_membership_requests
|
||||||
|
return unless unread_pm?
|
||||||
|
|
||||||
|
post_id = data_hash[:original_post_id]
|
||||||
|
return if post_id.blank?
|
||||||
|
|
||||||
|
custom_field = PostCustomField.select(:value).find_by(post_id: post_id, name: "requested_group_id")
|
||||||
|
return if custom_field.blank?
|
||||||
|
|
||||||
|
group_id = custom_field.value.to_i
|
||||||
|
group_name = Group.select(:name).find_by(id: group_id)&.name
|
||||||
|
return if group_name.blank?
|
||||||
|
|
||||||
|
consolidation_window = MEMBERSHIP_REQUEST_CONSOLIDATION_WINDOW_HOURS.hours.ago
|
||||||
|
timestamp = Time.zone.now
|
||||||
|
unread = user.notifications.unread
|
||||||
|
|
||||||
|
consolidated_notification = unread
|
||||||
|
.where("created_at > ? AND data::json ->> 'group_name' = ?", consolidation_window, group_name)
|
||||||
|
.find_by(notification_type: Notification.types[:membership_request_consolidated])
|
||||||
|
|
||||||
|
if consolidated_notification.present?
|
||||||
|
data = consolidated_notification.data_hash
|
||||||
|
data["count"] += 1
|
||||||
|
|
||||||
|
Notification.transaction do
|
||||||
|
consolidated_notification.update!(
|
||||||
|
data: data.to_json,
|
||||||
|
read: false,
|
||||||
|
updated_at: timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
destroy!
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
notifications = unread
|
||||||
|
.where(notification_type: Notification.types[:private_message])
|
||||||
|
.where("created_at > ? AND data::json ->> 'topic_title' = ?", consolidation_window, data_hash[:topic_title])
|
||||||
|
|
||||||
|
return if notifications.count < MEMBERSHIP_REQUEST_CONSOLIDATION_THRESHOLD
|
||||||
|
|
||||||
|
Notification.transaction do
|
||||||
|
Notification.create!(
|
||||||
|
notification_type: Notification.types[:membership_request_consolidated],
|
||||||
|
user_id: user_id,
|
||||||
|
data: {
|
||||||
|
group_name: group_name,
|
||||||
|
count: notifications.count
|
||||||
|
}.to_json,
|
||||||
|
updated_at: timestamp,
|
||||||
|
created_at: timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
notifications.destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -1782,6 +1782,7 @@ en:
|
||||||
topic_reminder: "<span>{{username}}</span> {{description}}"
|
topic_reminder: "<span>{{username}}</span> {{description}}"
|
||||||
watching_first_post: "<span>New Topic</span> {{description}}"
|
watching_first_post: "<span>New Topic</span> {{description}}"
|
||||||
membership_request_accepted: "Membership accepted in '{{group_name}}'"
|
membership_request_accepted: "Membership accepted in '{{group_name}}'"
|
||||||
|
membership_request_consolidated: "{{count}} open membership requests for '{{group_name}}'"
|
||||||
|
|
||||||
group_message_summary:
|
group_message_summary:
|
||||||
one: "{{count}} message in your {{group_name}} inbox"
|
one: "{{count}} message in your {{group_name}} inbox"
|
||||||
|
@ -1820,6 +1821,7 @@ en:
|
||||||
topic_reminder: "topic reminder"
|
topic_reminder: "topic reminder"
|
||||||
liked_consolidated: "new likes"
|
liked_consolidated: "new likes"
|
||||||
post_approved: "post approved"
|
post_approved: "post approved"
|
||||||
|
membership_request_consolidated: "new membership requests"
|
||||||
|
|
||||||
upload_selector:
|
upload_selector:
|
||||||
title: "Add an image"
|
title: "Add an image"
|
||||||
|
|
|
@ -355,5 +355,49 @@ describe Notification do
|
||||||
expect(Notification.recent_report(user)).to contain_exactly(notification)
|
expect(Notification.recent_report(user)).to contain_exactly(notification)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#consolidate_membership_requests' do
|
||||||
|
fab!(:group) { Fabricate(:group, name: "XXsssssddd") }
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
fab!(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
|
def create_membership_request_notification
|
||||||
|
Notification.create(
|
||||||
|
notification_type: Notification.types[:private_message],
|
||||||
|
user_id: user.id,
|
||||||
|
data: {
|
||||||
|
topic_title: I18n.t('groups.request_membership_pm.title', group_name: group.name),
|
||||||
|
original_post_id: post.id
|
||||||
|
}.to_json,
|
||||||
|
updated_at: Time.zone.now,
|
||||||
|
created_at: Time.zone.now
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
PostCustomField.create!(post_id: post.id, name: "requested_group_id", value: group.id)
|
||||||
|
create_membership_request_notification
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should consolidate membership requests to a new notification' do
|
||||||
|
notification = create_membership_request_notification
|
||||||
|
notification.reload
|
||||||
|
|
||||||
|
notification = create_membership_request_notification
|
||||||
|
expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
|
||||||
|
notification = Notification.last
|
||||||
|
expect(notification.notification_type).to eq(Notification.types[:membership_request_consolidated])
|
||||||
|
|
||||||
|
data = notification.data_hash
|
||||||
|
expect(data[:group_name]).to eq(group.name)
|
||||||
|
expect(data[:count]).to eq(3)
|
||||||
|
|
||||||
|
notification = create_membership_request_notification
|
||||||
|
expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
|
||||||
|
expect(Notification.last.data_hash[:count]).to eq(4)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue