feat(voice): embed Call in voice_call message payload
Adds a nested `call` object to the voice_call message JSON / push payload so the frontend can read call state directly from message.call instead of duplicating it in content_attributes.data. - Message gains has_one :call (inverse of existing Call#belongs_to :message) via enterprise module prepend. - Message serializer (_message.json.jbuilder) emits a call block for voice_call messages when the Call is linked. - Message#push_event_data mirrors the payload into socket broadcasts. - MessageFinder eager-loads the call (+ contact + inbox.channel) to avoid N+1 when listing conversation messages. - Call gains helper methods: display_status, from_number, to_number, recording_url, push_event_data. These centralize the direction- and status-aware derivations so jbuilder and push_event_data stay in sync. Frontend continues to read content_attributes.data — this commit only adds the new shape; a follow-up swaps readers to message.call.
This commit is contained in:
@@ -11,7 +11,9 @@ class MessageFinder
|
||||
private
|
||||
|
||||
def conversation_messages
|
||||
@conversation.messages.includes(:attachments, :sender, sender: { avatar_attachment: [:blob] })
|
||||
scope = @conversation.messages.includes(:attachments, :sender, sender: { avatar_attachment: [:blob] })
|
||||
scope = scope.includes(call: [:contact, { inbox: :channel }]) if Message.reflect_on_association(:call)
|
||||
scope
|
||||
end
|
||||
|
||||
def messages
|
||||
|
||||
@@ -152,9 +152,14 @@ class Message < ApplicationRecord
|
||||
)
|
||||
data[:echo_id] = echo_id if echo_id.present?
|
||||
data[:attachments] = attachments.map(&:push_event_data) if attachments.present?
|
||||
data[:call] = call.push_event_data if voice_call? && respond_to?(:call) && call.present?
|
||||
merge_sender_attributes(data)
|
||||
end
|
||||
|
||||
def voice_call?
|
||||
content_type == 'voice_call'
|
||||
end
|
||||
|
||||
def conversation_push_event_data
|
||||
{
|
||||
assignee_id: conversation.assignee_id,
|
||||
|
||||
@@ -12,3 +12,23 @@ json.private message.private
|
||||
json.source_id message.source_id
|
||||
json.sender message.sender.push_event_data if message.sender
|
||||
json.attachments message.attachments.map(&:push_event_data) if message.attachments.present?
|
||||
|
||||
if message.content_type == 'voice_call' && message.respond_to?(:call) && message.call.present?
|
||||
call = message.call
|
||||
json.call do
|
||||
json.id call.id
|
||||
json.provider_call_id call.provider_call_id
|
||||
json.provider call.provider
|
||||
json.direction call.direction
|
||||
json.status call.display_status
|
||||
json.duration_seconds call.duration_seconds
|
||||
json.conference_sid call.conference_sid
|
||||
json.accepted_by_agent_id call.accepted_by_agent_id
|
||||
json.started_at call.started_at&.to_i
|
||||
json.ended_at call.ended_at
|
||||
json.from_number call.from_number
|
||||
json.to_number call.to_number
|
||||
json.recording_url call.recording_url
|
||||
json.transcript call.transcript
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,7 +45,7 @@ class Call < ApplicationRecord
|
||||
belongs_to :inbox
|
||||
belongs_to :conversation
|
||||
belongs_to :contact
|
||||
belongs_to :message, optional: true
|
||||
belongs_to :message, optional: true, inverse_of: :call
|
||||
belongs_to :accepted_by_agent, class_name: 'User', optional: true
|
||||
|
||||
has_one_attached :recording
|
||||
@@ -70,4 +70,41 @@ class Call < ApplicationRecord
|
||||
def display_direction
|
||||
DISPLAY_DIRECTION[direction]
|
||||
end
|
||||
|
||||
def display_status
|
||||
status.to_s.tr('_', '-')
|
||||
end
|
||||
|
||||
def from_number
|
||||
incoming? ? contact.phone_number : inbox.channel&.phone_number
|
||||
end
|
||||
|
||||
def to_number
|
||||
incoming? ? inbox.channel&.phone_number : contact.phone_number
|
||||
end
|
||||
|
||||
def recording_url
|
||||
return nil unless recording.attached?
|
||||
|
||||
Rails.application.routes.url_helpers.rails_blob_url(recording)
|
||||
end
|
||||
|
||||
def push_event_data
|
||||
{
|
||||
id: id,
|
||||
provider_call_id: provider_call_id,
|
||||
provider: provider,
|
||||
direction: direction,
|
||||
status: display_status,
|
||||
duration_seconds: duration_seconds,
|
||||
conference_sid: conference_sid,
|
||||
accepted_by_agent_id: accepted_by_agent_id,
|
||||
started_at: started_at&.to_i,
|
||||
ended_at: ended_at,
|
||||
from_number: from_number,
|
||||
to_number: to_number,
|
||||
recording_url: recording_url,
|
||||
transcript: transcript
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
module Enterprise::Message
|
||||
def self.prepended(base)
|
||||
base.class_eval do
|
||||
has_one :call, class_name: 'Call', foreign_key: :message_id, dependent: :nullify, inverse_of: :message
|
||||
|
||||
scope :with_call, -> { includes(call: [:contact, { inbox: :channel }]) }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mark_pending_conversation_as_open_for_human_response
|
||||
|
||||
Reference in New Issue
Block a user