discourse/plugins/chat/app/jobs/regular/chat/notify_mentioned.rb
Martin Brennan b2a727336e
FIX: Thread mention read state and notification links (#21385)
* FIX: Link to thread for mentions inside thread

When mentioning a user in a thread, when we send the
notification and display it in the UI we want the URL
of the notification to point to the thread URL to open
the panel, rather than the main channel which is confusing.

For now, we don't have a way to highlight the linked-to message
in the thread, we can revisit this later.

* FIX: Mark mention notifications read when thread opens

Since we have no scrolling/message visibility/thread membership
for now, when a user opens the thread panel we just want to mark
all mention notifications relating to messages in the thread
for the user as read.
2023-05-04 17:28:51 +02:00

158 lines
5.0 KiB
Ruby

# frozen_string_literal: true
module Jobs
module Chat
class NotifyMentioned < ::Jobs::Base
def execute(args = {})
@chat_message =
::Chat::Message.includes(:user, :revisions, chat_channel: :chatable).find_by(
id: args[:chat_message_id],
)
if @chat_message.nil? ||
@chat_message.revisions.where("created_at > ?", args[:timestamp]).any?
return
end
@creator = @chat_message.user
@chat_channel = @chat_message.chat_channel
@already_notified_user_ids = args[:already_notified_user_ids] || []
user_ids_to_notify = args[:to_notify_ids_map] || {}
user_ids_to_notify.each { |mention_type, ids| process_mentions(ids, mention_type.to_sym) }
end
private
def get_memberships(user_ids)
query =
::Chat::UserChatChannelMembership.includes(:user).where(
user_id: (user_ids - @already_notified_user_ids),
chat_channel_id: @chat_message.chat_channel_id,
)
query = query.where(following: true) if @chat_channel.public_channel?
query
end
def build_data_for(membership, identifier_type:)
data = {
chat_message_id: @chat_message.id,
chat_channel_id: @chat_channel.id,
mentioned_by_username: @creator.username,
is_direct_message_channel: @chat_channel.direct_message_channel?,
}
data[:chat_thread_id] = @chat_message.thread_id if @chat_message.in_thread?
if !@is_direct_message_channel
data[:chat_channel_title] = @chat_channel.title(membership.user)
data[:chat_channel_slug] = @chat_channel.slug
end
return data if identifier_type == :direct_mentions
case identifier_type
when :here_mentions
data[:identifier] = "here"
when :global_mentions
data[:identifier] = "all"
else
data[:identifier] = identifier_type if identifier_type
data[:is_group_mention] = true
end
data
end
def build_payload_for(membership, identifier_type:)
post_url =
if @chat_message.in_thread?
@chat_message.thread.relative_url
else
"#{@chat_channel.relative_url}/#{@chat_message.id}"
end
payload = {
notification_type: ::Notification.types[:chat_mention],
username: @creator.username,
tag: ::Chat::Notifier.push_notification_tag(:mention, @chat_channel.id),
excerpt: @chat_message.push_notification_excerpt,
post_url: post_url,
}
translation_prefix =
(
if @chat_channel.direct_message_channel?
"discourse_push_notifications.popup.direct_message_chat_mention"
else
"discourse_push_notifications.popup.chat_mention"
end
)
translation_suffix = identifier_type == :direct_mentions ? "direct" : "other_type"
identifier_text =
case identifier_type
when :here_mentions
"@here"
when :global_mentions
"@all"
when :direct_mentions
""
else
"@#{identifier_type}"
end
payload[:translated_title] = ::I18n.t(
"#{translation_prefix}.#{translation_suffix}",
username: @creator.username,
identifier: identifier_text,
channel: @chat_channel.title(membership.user),
)
payload
end
def create_notification!(membership, mention, mention_type)
notification_data = build_data_for(membership, identifier_type: mention_type)
is_read = ::Chat::Notifier.user_has_seen_message?(membership, @chat_message.id)
notification =
::Notification.create!(
notification_type: ::Notification.types[:chat_mention],
user_id: membership.user_id,
high_priority: true,
data: notification_data.to_json,
read: is_read,
)
mention.update!(notification: notification)
end
def send_notifications(membership, mention_type)
payload = build_payload_for(membership, identifier_type: mention_type)
if !membership.desktop_notifications_never? && !membership.muted?
::MessageBus.publish(
"/chat/notification-alert/#{membership.user_id}",
payload,
user_ids: [membership.user_id],
)
end
if !membership.mobile_notifications_never? && !membership.muted?
::PostAlerter.push_notification(membership.user, payload)
end
end
def process_mentions(user_ids, mention_type)
memberships = get_memberships(user_ids)
memberships.each do |membership|
mention = ::Chat::Mention.find_by(user: membership.user, chat_message: @chat_message)
if mention.present?
create_notification!(membership, mention, mention_type)
send_notifications(membership, mention_type)
end
end
end
end
end
end