mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-31 19:17:48 +00:00
- Update Schema Information headers for AssignmentPolicy, Campaign, Notification - Reflect schema change for Campaign.template_params (not null with default) - Keep annotations consistent to avoid drift
199 lines
7.0 KiB
Ruby
199 lines
7.0 KiB
Ruby
# == Schema Information
|
|
#
|
|
# Table name: notifications
|
|
#
|
|
# id :bigint not null, primary key
|
|
# last_activity_at :datetime
|
|
# meta :jsonb
|
|
# notification_type :integer not null
|
|
# primary_actor_type :string not null
|
|
# read_at :datetime
|
|
# secondary_actor_type :string
|
|
# snoozed_until :datetime
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# account_id :bigint not null
|
|
# primary_actor_id :bigint not null
|
|
# secondary_actor_id :bigint
|
|
# user_id :bigint not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# idx_notifications_performance (user_id,account_id,snoozed_until,read_at)
|
|
# index_notifications_on_account_id (account_id)
|
|
# index_notifications_on_last_activity_at (last_activity_at)
|
|
# index_notifications_on_user_id (user_id)
|
|
# uniq_primary_actor_per_account_notifications (primary_actor_type,primary_actor_id)
|
|
# uniq_secondary_actor_per_account_notifications (secondary_actor_type,secondary_actor_id)
|
|
#
|
|
|
|
class Notification < ApplicationRecord
|
|
include MessageFormatHelper
|
|
belongs_to :account
|
|
belongs_to :user
|
|
|
|
belongs_to :primary_actor, polymorphic: true
|
|
belongs_to :secondary_actor, polymorphic: true, optional: true
|
|
|
|
NOTIFICATION_TYPES = {
|
|
conversation_creation: 1,
|
|
conversation_assignment: 2,
|
|
assigned_conversation_new_message: 3,
|
|
conversation_mention: 4,
|
|
participating_conversation_new_message: 5,
|
|
sla_missed_first_response: 6,
|
|
sla_missed_next_response: 7,
|
|
sla_missed_resolution: 8
|
|
}.freeze
|
|
|
|
enum notification_type: NOTIFICATION_TYPES
|
|
|
|
before_create :set_last_activity_at
|
|
after_create_commit :process_notification_delivery, :dispatch_create_event
|
|
after_destroy_commit :dispatch_destroy_event
|
|
after_update_commit :dispatch_update_event
|
|
|
|
PRIMARY_ACTORS = ['Conversation'].freeze
|
|
|
|
def push_event_data
|
|
# Secondary actor could be nil for cases like system assigning conversation
|
|
payload = {
|
|
id: id,
|
|
notification_type: notification_type,
|
|
primary_actor_type: primary_actor_type,
|
|
primary_actor_id: primary_actor_id,
|
|
read_at: read_at,
|
|
secondary_actor: secondary_actor&.push_event_data,
|
|
user: user&.push_event_data,
|
|
created_at: created_at.to_i,
|
|
last_activity_at: last_activity_at.to_i,
|
|
snoozed_until: snoozed_until,
|
|
meta: meta,
|
|
account_id: account_id
|
|
}
|
|
payload.merge!(primary_actor_data) if primary_actor.present?
|
|
payload
|
|
end
|
|
|
|
def fcm_push_data
|
|
{
|
|
id: id,
|
|
notification_type: notification_type,
|
|
primary_actor_id: primary_actor_id,
|
|
primary_actor_type: primary_actor_type,
|
|
primary_actor: primary_actor.push_event_data.with_indifferent_access.slice('conversation_id', 'id')
|
|
}
|
|
end
|
|
|
|
# rubocop:disable Metrics/MethodLength
|
|
def push_message_title
|
|
notification_title_map = {
|
|
'conversation_creation' => 'notifications.notification_title.conversation_creation',
|
|
'conversation_assignment' => 'notifications.notification_title.conversation_assignment',
|
|
'assigned_conversation_new_message' => 'notifications.notification_title.assigned_conversation_new_message',
|
|
'participating_conversation_new_message' => 'notifications.notification_title.assigned_conversation_new_message',
|
|
'conversation_mention' => 'notifications.notification_title.conversation_mention',
|
|
'sla_missed_first_response' => 'notifications.notification_title.sla_missed_first_response',
|
|
'sla_missed_next_response' => 'notifications.notification_title.sla_missed_next_response',
|
|
'sla_missed_resolution' => 'notifications.notification_title.sla_missed_resolution'
|
|
}
|
|
|
|
i18n_key = notification_title_map[notification_type]
|
|
return '' unless i18n_key
|
|
|
|
if notification_type == 'conversation_creation'
|
|
I18n.t(i18n_key, display_id: conversation.display_id, inbox_name: primary_actor.inbox.name)
|
|
elsif %w[conversation_assignment assigned_conversation_new_message participating_conversation_new_message
|
|
conversation_mention].include?(notification_type)
|
|
I18n.t(i18n_key, display_id: conversation.display_id)
|
|
else
|
|
I18n.t(i18n_key, display_id: primary_actor.display_id)
|
|
end
|
|
end
|
|
# rubocop:enable Metrics/MethodLength
|
|
|
|
def push_message_body
|
|
case notification_type
|
|
when 'conversation_creation', 'sla_missed_first_response'
|
|
message_body(conversation.messages.first)
|
|
when 'assigned_conversation_new_message', 'participating_conversation_new_message', 'conversation_mention'
|
|
message_body(secondary_actor)
|
|
when 'conversation_assignment', 'sla_missed_next_response', 'sla_missed_resolution'
|
|
message_body((conversation.messages.incoming.last || conversation.messages.outgoing.last))
|
|
else
|
|
''
|
|
end
|
|
end
|
|
|
|
def conversation
|
|
primary_actor
|
|
end
|
|
|
|
private
|
|
|
|
def message_body(actor)
|
|
sender_name = sender_name(actor)
|
|
content = message_content(actor)
|
|
"#{sender_name}: #{content}"
|
|
end
|
|
|
|
def sender_name(actor)
|
|
actor.try(:sender)&.name || ''
|
|
end
|
|
|
|
def message_content(actor)
|
|
content = actor.try(:content)
|
|
attachments = actor.try(:attachments)
|
|
|
|
if content.present?
|
|
transform_user_mention_content(content.truncate_words(10))
|
|
else
|
|
attachments.present? ? I18n.t('notifications.attachment') : I18n.t('notifications.no_content')
|
|
end
|
|
end
|
|
|
|
def process_notification_delivery
|
|
Notification::PushNotificationJob.perform_later(self) if user_subscribed_to_notification?('push')
|
|
|
|
# Should we do something about the case where user subscribed to both push and email ?
|
|
# In future, we could probably add condition here to enqueue the job for 30 seconds later
|
|
# when push enabled and then check in email job whether notification has been read already.
|
|
Notification::EmailNotificationJob.perform_later(self) if user_subscribed_to_notification?('email')
|
|
|
|
Notification::RemoveDuplicateNotificationJob.perform_later(self)
|
|
end
|
|
|
|
def user_subscribed_to_notification?(delivery_type)
|
|
notification_setting = user.notification_settings.find_by(account_id: account.id)
|
|
return false if notification_setting.blank?
|
|
|
|
# Check if the user has subscribed to the specified type of notification
|
|
notification_setting.public_send("#{delivery_type}_#{notification_type}?")
|
|
end
|
|
|
|
def dispatch_create_event
|
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_CREATED, Time.zone.now, notification: self)
|
|
end
|
|
|
|
def dispatch_update_event
|
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_UPDATED, Time.zone.now, notification: self)
|
|
end
|
|
|
|
def dispatch_destroy_event
|
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_DELETED, Time.zone.now, notification: self)
|
|
end
|
|
|
|
def set_last_activity_at
|
|
self.last_activity_at = created_at
|
|
end
|
|
|
|
def primary_actor_data
|
|
{
|
|
primary_actor: primary_actor&.push_event_data,
|
|
# TODO: Rename push_message_title to push_message_body
|
|
push_message_title: push_message_body,
|
|
push_message_body: push_message_body
|
|
}
|
|
end
|
|
end
|