mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Redesign chat mentions (#24752)
At the moment, when someone is mentioning a group, or using here or
all mention, we create a chat_mention record per user. What we want
instead is to have special kinds of mentions, so we can create only one
chat_mention record in such cases. This PR implements that.
Note, that such mentions will still have N related notifications, one
notification per a user. We don't expect we'll have performance
problems on the notifications side, but if at some point we do, we
should be able to solve them on the side of notifications
(notifications are handled in jobs, also some little delays with
the notifications are acceptable, so we can make sure notifications
are properly queued, and that processing of every notification is
fast enough to make delays small enough).
The preparation work for this PR was done in fbd24fa, where we make
it possible for one mention to have several related notifications.
A pretty tricky part of this PR is schema and data migration, I've explained
related details inline on the migration files.
This commit is contained in:
committed by
GitHub
parent
6876c52857
commit
62f423da15
@@ -145,13 +145,43 @@ module Jobs
|
||||
memberships = get_memberships(user_ids)
|
||||
|
||||
memberships.each do |membership|
|
||||
mention = ::Chat::Mention.find_by(user: membership.user, chat_message: @chat_message)
|
||||
mention = find_mention(@chat_message, mention_type, membership.user.id)
|
||||
if mention.present?
|
||||
create_notification!(membership, mention, mention_type)
|
||||
send_notifications(membership, mention_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_mention(chat_message, mention_type, user_id)
|
||||
mention_klass = resolve_mention_klass(mention_type)
|
||||
|
||||
target_id = nil
|
||||
if mention_klass == ::Chat::UserMention
|
||||
target_id = user_id
|
||||
elsif mention_klass == ::Chat::GroupMention
|
||||
begin
|
||||
target_id = Group.where("LOWER(name) = ?", "#{mention_type}").first.id
|
||||
rescue => e
|
||||
Discourse.warn_exception(e, message: "Mentioned group doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
mention_klass.find_by(chat_message: chat_message, target_id: target_id)
|
||||
end
|
||||
|
||||
def resolve_mention_klass(mention_type)
|
||||
case mention_type
|
||||
when :global_mentions
|
||||
::Chat::AllMention
|
||||
when :here_mentions
|
||||
::Chat::HereMention
|
||||
when :direct_mentions
|
||||
::Chat::UserMention
|
||||
else
|
||||
::Chat::GroupMention
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
6
plugins/chat/app/models/chat/all_mention.rb
Normal file
6
plugins/chat/app/models/chat/all_mention.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Chat
|
||||
class AllMention < Mention
|
||||
end
|
||||
end
|
||||
7
plugins/chat/app/models/chat/group_mention.rb
Normal file
7
plugins/chat/app/models/chat/group_mention.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Chat
|
||||
class GroupMention < Mention
|
||||
belongs_to :group, foreign_key: :target_id
|
||||
end
|
||||
end
|
||||
6
plugins/chat/app/models/chat/here_mention.rb
Normal file
6
plugins/chat/app/models/chat/here_mention.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Chat
|
||||
class HereMention < Mention
|
||||
end
|
||||
end
|
||||
@@ -3,9 +3,8 @@
|
||||
module Chat
|
||||
class Mention < ActiveRecord::Base
|
||||
self.table_name = "chat_mentions"
|
||||
self.ignored_columns = ["notification_id"]
|
||||
self.ignored_columns = %w[notification_id user_id]
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :chat_message, class_name: "Chat::Message"
|
||||
has_many :mention_notifications,
|
||||
class_name: "Chat::MentionNotification",
|
||||
@@ -20,12 +19,15 @@ end
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# chat_message_id :integer not null
|
||||
# user_id :integer not null
|
||||
# user_id :integer
|
||||
# notification_id :integer not null
|
||||
# target_id :integer
|
||||
# type :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# chat_mentions_index (chat_message_id,user_id,notification_id) UNIQUE
|
||||
# index_chat_mentions_on_chat_message_id (chat_message_id)
|
||||
# index_chat_mentions_on_target_id (target_id)
|
||||
#
|
||||
|
||||
@@ -51,6 +51,22 @@ module Chat
|
||||
dependent: :destroy,
|
||||
class_name: "Chat::Mention",
|
||||
foreign_key: :chat_message_id
|
||||
has_many :user_mentions,
|
||||
dependent: :destroy,
|
||||
class_name: "Chat::UserMention",
|
||||
foreign_key: :chat_message_id
|
||||
has_many :group_mentions,
|
||||
dependent: :destroy,
|
||||
class_name: "Chat::GroupMention",
|
||||
foreign_key: :chat_message_id
|
||||
has_one :all_mention,
|
||||
dependent: :destroy,
|
||||
class_name: "Chat::AllMention",
|
||||
foreign_key: :chat_message_id
|
||||
has_one :here_mention,
|
||||
dependent: :destroy,
|
||||
class_name: "Chat::HereMention",
|
||||
foreign_key: :chat_message_id
|
||||
|
||||
scope :in_public_channel,
|
||||
-> do
|
||||
@@ -248,14 +264,10 @@ module Chat
|
||||
end
|
||||
|
||||
def upsert_mentions
|
||||
mentioned_user_ids = parsed_mentions.all_mentioned_users_ids
|
||||
old_mentions = chat_mentions.pluck(:user_id)
|
||||
|
||||
mentioned_user_ids_to_drop = old_mentions - mentioned_user_ids
|
||||
delete_mentions(mentioned_user_ids_to_drop)
|
||||
|
||||
mentioned_user_ids_to_add = mentioned_user_ids - old_mentions
|
||||
insert_mentions(mentioned_user_ids_to_add)
|
||||
upsert_user_mentions
|
||||
upsert_group_mentions
|
||||
create_or_delete_all_mention
|
||||
create_or_delete_here_mention
|
||||
end
|
||||
|
||||
def in_thread?
|
||||
@@ -280,24 +292,16 @@ module Chat
|
||||
|
||||
private
|
||||
|
||||
def delete_mentions(user_ids)
|
||||
chat_mentions.where(user_id: user_ids).destroy_all
|
||||
def delete_mentions(mention_type, target_ids)
|
||||
chat_mentions.where(type: mention_type, target_id: target_ids).destroy_all
|
||||
end
|
||||
|
||||
def insert_mentions(user_ids)
|
||||
return if user_ids.empty?
|
||||
def insert_mentions(type, target_ids)
|
||||
return if target_ids.empty?
|
||||
|
||||
now = Time.zone.now
|
||||
mentions = []
|
||||
User
|
||||
.where(id: user_ids)
|
||||
.find_each do |user|
|
||||
mentions << {
|
||||
chat_message_id: self.id,
|
||||
user_id: user.id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
mentions =
|
||||
target_ids.map do |target_id|
|
||||
{ chat_message_id: self.id, target_id: target_id, type: type }
|
||||
end
|
||||
|
||||
Chat::Mention.insert_all(mentions)
|
||||
@@ -314,6 +318,38 @@ module Chat
|
||||
def ensure_last_editor_id
|
||||
self.last_editor_id ||= self.user_id
|
||||
end
|
||||
|
||||
def create_or_delete_all_mention
|
||||
if !parsed_mentions.has_global_mention && all_mention.present?
|
||||
all_mention.destroy!
|
||||
association(:all_mention).reload
|
||||
elsif parsed_mentions.has_global_mention && all_mention.blank?
|
||||
build_all_mention.save!
|
||||
end
|
||||
end
|
||||
|
||||
def create_or_delete_here_mention
|
||||
if !parsed_mentions.has_here_mention && here_mention.present?
|
||||
here_mention.destroy!
|
||||
association(:here_mention).reload
|
||||
elsif parsed_mentions.has_here_mention && here_mention.blank?
|
||||
build_here_mention.save!
|
||||
end
|
||||
end
|
||||
|
||||
def upsert_group_mentions
|
||||
old_mentions = group_mentions.pluck(:target_id)
|
||||
new_mentions = parsed_mentions.groups_to_mention.pluck(:id)
|
||||
delete_mentions("Chat::GroupMention", old_mentions - new_mentions)
|
||||
insert_mentions("Chat::GroupMention", new_mentions - old_mentions)
|
||||
end
|
||||
|
||||
def upsert_user_mentions
|
||||
old_mentions = user_mentions.pluck(:target_id)
|
||||
new_mentions = parsed_mentions.direct_mentions.pluck(:id)
|
||||
delete_mentions("Chat::UserMention", old_mentions - new_mentions)
|
||||
insert_mentions("Chat::UserMention", new_mentions - old_mentions)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
7
plugins/chat/app/models/chat/user_mention.rb
Normal file
7
plugins/chat/app/models/chat/user_mention.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Chat
|
||||
class UserMention < Mention
|
||||
belongs_to :user, foreign_key: :target_id
|
||||
end
|
||||
end
|
||||
@@ -87,9 +87,9 @@ module Chat
|
||||
|
||||
if SiteSetting.enable_user_status
|
||||
query = query.includes(user: :user_status)
|
||||
query = query.includes(chat_mentions: { user: :user_status })
|
||||
query = query.includes(user_mentions: { user: :user_status })
|
||||
else
|
||||
query = query.includes(chat_mentions: :user)
|
||||
query = query.includes(user_mentions: :user)
|
||||
end
|
||||
|
||||
query
|
||||
|
||||
@@ -36,7 +36,7 @@ module Chat
|
||||
|
||||
def mentioned_users
|
||||
object
|
||||
.chat_mentions
|
||||
.user_mentions
|
||||
.limit(SiteSetting.max_mentions_per_chat_message)
|
||||
.map(&:user)
|
||||
.compact
|
||||
|
||||
@@ -17,7 +17,7 @@ module Chat
|
||||
|
||||
def mentioned_users
|
||||
object
|
||||
.chat_mentions
|
||||
.user_mentions
|
||||
.limit(SiteSetting.max_mentions_per_chat_message)
|
||||
.map(&:user)
|
||||
.compact
|
||||
|
||||
Reference in New Issue
Block a user