refactor(voice): read call state from message.call on the frontend

- VoiceCall bubble, voice.js helper, ConversationCard, and the store
  mutation all switch from message.content_attributes.data to message.call.
- Message.vue gains a `call` prop that's provided through
  useMessageContext for the bubble.
- UPDATE_MESSAGE_CALL_STATUS mutation now updates message.call.status,
  matched by provider_call_id.
- content_attributes.data is still emitted by the backend for this
  commit; backend-side trim lands in a follow-up.
This commit is contained in:
Muhsin
2026-04-20 13:36:26 +04:00
parent 5ce77eedbe
commit c2a851c756
6 changed files with 59 additions and 39 deletions
@@ -317,15 +317,11 @@ export const mutations = {
const message = (chat.messages || []).find(
m =>
m.content_type === CONTENT_TYPES.VOICE_CALL &&
m.content_attributes?.data?.call_sid === callSid
m.call?.provider_call_id === callSid
);
if (!message) return;
if (!message?.call) return;
message.content_attributes ??= {};
message.content_attributes.data = {
...message.content_attributes.data,
status: callStatus,
};
message.call = { ...message.call, status: callStatus };
},
[types.SET_ACTIVE_INBOX](_state, inboxId) {
@@ -39,16 +39,12 @@ describe('#mutations', () => {
{
id: 1,
content_type: 'voice_call',
content_attributes: {
data: { call_sid: 'CA111', status: 'ringing' },
},
call: { provider_call_id: 'CA111', status: 'ringing' },
},
{
id: 2,
content_type: 'voice_call',
content_attributes: {
data: { call_sid: 'CA222', status: 'ringing' },
},
call: { provider_call_id: 'CA222', status: 'ringing' },
},
],
},
@@ -59,15 +55,13 @@ describe('#mutations', () => {
callStatus: 'in-progress',
callSid: 'CA111',
});
expect(
state.allConversations[0].messages[0].content_attributes.data.status
).toBe('in-progress');
expect(
state.allConversations[0].messages[1].content_attributes.data.status
).toBe('ringing');
expect(state.allConversations[0].messages[0].call.status).toBe(
'in-progress'
);
expect(state.allConversations[0].messages[1].call.status).toBe('ringing');
});
it('preserves existing data in content_attributes.data', () => {
it('preserves existing call fields when updating status', () => {
const state = {
allConversations: [
{
@@ -76,8 +70,11 @@ describe('#mutations', () => {
{
id: 1,
content_type: 'voice_call',
content_attributes: {
data: { call_sid: 'CA123', status: 'ringing' },
call: {
provider_call_id: 'CA123',
status: 'ringing',
direction: 'incoming',
duration_seconds: null,
},
},
],
@@ -89,11 +86,11 @@ describe('#mutations', () => {
callStatus: 'in-progress',
callSid: 'CA123',
});
expect(
state.allConversations[0].messages[0].content_attributes.data
).toEqual({
call_sid: 'CA123',
expect(state.allConversations[0].messages[0].call).toEqual({
provider_call_id: 'CA123',
status: 'in-progress',
direction: 'incoming',
duration_seconds: null,
});
});
@@ -108,5 +105,30 @@ describe('#mutations', () => {
});
expect(state.allConversations[0].messages).toEqual([]);
});
it('does nothing if matching message has no call object yet', () => {
const state = {
allConversations: [
{
id: 1,
messages: [
{
id: 1,
content_type: 'voice_call',
call: { provider_call_id: 'CA-OTHER' },
},
],
},
],
};
mutations[types.UPDATE_MESSAGE_CALL_STATUS](state, {
conversationId: 1,
callStatus: 'completed',
callSid: 'CA-MISSING',
});
expect(state.allConversations[0].messages[0].call).toEqual({
provider_call_id: 'CA-OTHER',
});
});
});
});