diff --git a/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue b/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue index c3a12999f..4c8e22522 100644 --- a/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue +++ b/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue @@ -42,12 +42,20 @@ const getCallInfo = call => { const conversation = store.getters.getConversationById(call?.conversationId); const inbox = store.getters['inboxes/getInbox'](conversation?.inbox_id); const sender = conversation?.meta?.sender; + // Inbound WhatsApp calls stash caller info on the call record (from the cable + // payload) so the widget has something to show before the conversation lands. + const caller = call?.caller; return { conversation, inbox, - contactName: sender?.name || sender?.phone_number || 'Unknown caller', + contactName: + sender?.name || + sender?.phone_number || + caller?.name || + caller?.phone || + 'Unknown caller', inboxName: inbox?.name || 'Customer support', - avatar: sender?.avatar || sender?.thumbnail, + avatar: sender?.avatar || sender?.thumbnail || caller?.avatar, }; }; diff --git a/app/javascript/dashboard/helper/actionCable.js b/app/javascript/dashboard/helper/actionCable.js index ae66ac52c..2b2dd70cb 100644 --- a/app/javascript/dashboard/helper/actionCable.js +++ b/app/javascript/dashboard/helper/actionCable.js @@ -225,6 +225,9 @@ class ActionCableConnector extends BaseActionCableConnector { provider: 'whatsapp', sdpOffer: data.sdp_offer, iceServers: data.ice_servers, + // Caller info for the FloatingCallWidget so it doesn't show "Unknown caller" + // before the conversation/contact has loaded into the store. + caller: data.caller, }); }; diff --git a/app/javascript/dashboard/helper/voice.js b/app/javascript/dashboard/helper/voice.js index d6a6af6bd..0c798164f 100644 --- a/app/javascript/dashboard/helper/voice.js +++ b/app/javascript/dashboard/helper/voice.js @@ -26,6 +26,8 @@ function extractCallData(message) { const call = message?.call || {}; return { callSid: call.provider_call_id, + callId: call.id, + provider: call.provider, status: call.status, callDirection: call.direction === 'outgoing' ? 'outbound' : 'inbound', conversationId: message?.conversation_id, @@ -36,7 +38,7 @@ function extractCallData(message) { export function handleVoiceCallCreated(message, currentUserId) { if (!isVoiceCallMessage(message)) return; - const { callSid, callDirection, conversationId, senderId } = + const { callSid, callId, provider, callDirection, conversationId, senderId } = extractCallData(message); if (shouldSkipCall(callDirection, senderId, currentUserId)) return; @@ -44,6 +46,8 @@ export function handleVoiceCallCreated(message, currentUserId) { const callsStore = useCallsStore(); callsStore.addCall({ callSid, + callId, + provider, conversationId, callDirection, senderId, diff --git a/app/javascript/dashboard/stores/calls.js b/app/javascript/dashboard/stores/calls.js index d8e5b1595..717b85efe 100644 --- a/app/javascript/dashboard/stores/calls.js +++ b/app/javascript/dashboard/stores/calls.js @@ -32,8 +32,13 @@ export const useCallsStore = defineStore('calls', { addCall(callData) { if (!callData?.callSid) return; - const exists = this.calls.some(call => call.callSid === callData.callSid); - if (exists) return; + const existing = this.calls.find(c => c.callSid === callData.callSid); + if (existing) { + // Merge so a later cable event with sdp_offer/provider/caller fills in + // gaps left by the earlier message.created path (and vice versa). + Object.assign(existing, callData, { isActive: existing.isActive }); + return; + } this.calls.push({ ...callData,