feat(voice): WhatsApp calling UI — inbox toggle, call widget, badges
Frontend for WhatsApp WABA calling: calling status toggle in inbox settings, WhatsApp Call channel tile, voice badges, call widget redesign, and conversation header call button.
This commit is contained in:
@@ -217,9 +217,14 @@ class ActionCableConnector extends BaseActionCableConnector {
|
||||
this.app.$store.dispatch('teams/revalidate', { newKey: keys.team });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
onVoiceCallIncoming = data => {
|
||||
if (data?.provider !== 'whatsapp') return;
|
||||
// Defense in depth: the server already filters to online agent streams,
|
||||
// but if anything ever broadcasts to a broader stream (e.g. account-wide),
|
||||
// an agent who's set availability=offline/busy shouldn't ring.
|
||||
const availability = this.app.$store.getters.getCurrentUserAvailability;
|
||||
if (availability !== 'online') return;
|
||||
|
||||
useCallsStore().addCall({
|
||||
callSid: data.call_id,
|
||||
callId: data.id,
|
||||
|
||||
@@ -45,6 +45,29 @@ const shouldShowCall = ({
|
||||
return !isAssignedToAnotherAgent(assigneeId, currentUserId);
|
||||
};
|
||||
|
||||
// Offline/busy agents shouldn't get a ringing popup for inbound calls, but
|
||||
// outbound calls always belong to the initiator regardless of their status,
|
||||
// and existing (already-surfaced) calls keep going so a status change
|
||||
// mid-call doesn't yank away an active widget.
|
||||
const shouldRingInbound = (callDirection, currentUserAvailability) => {
|
||||
if (callDirection === 'outbound') return true;
|
||||
return currentUserAvailability === 'online';
|
||||
};
|
||||
|
||||
function extractCallerSnapshot(message) {
|
||||
// Snapshot caller info from the message at add-time so the widget can keep
|
||||
// rendering it after the user navigates away from a conversation list that
|
||||
// had the conversation hydrated (and Vuex evicts it from the store).
|
||||
const sender = message?.sender;
|
||||
if (!sender) return null;
|
||||
return {
|
||||
name: sender.name,
|
||||
phone: sender.phone_number,
|
||||
avatar: sender.avatar || sender.thumbnail,
|
||||
additionalAttributes: sender.additional_attributes || {},
|
||||
};
|
||||
}
|
||||
|
||||
function extractCallData(message) {
|
||||
const call = message?.call || {};
|
||||
return {
|
||||
@@ -54,12 +77,18 @@ function extractCallData(message) {
|
||||
status: call.status,
|
||||
callDirection: call.direction === 'outgoing' ? 'outbound' : 'inbound',
|
||||
conversationId: message?.conversation_id,
|
||||
inboxId: message?.inbox_id ?? message?.conversation?.inbox_id,
|
||||
assigneeId: extractAssigneeId(message?.conversation),
|
||||
senderId: message?.sender?.id,
|
||||
caller: extractCallerSnapshot(message),
|
||||
};
|
||||
}
|
||||
|
||||
export function handleVoiceCallCreated(message, currentUserId) {
|
||||
export function handleVoiceCallCreated(
|
||||
message,
|
||||
currentUserId,
|
||||
currentUserAvailability
|
||||
) {
|
||||
if (!isVoiceCallMessage(message)) return;
|
||||
|
||||
const {
|
||||
@@ -68,6 +97,7 @@ export function handleVoiceCallCreated(message, currentUserId) {
|
||||
provider,
|
||||
callDirection,
|
||||
conversationId,
|
||||
inboxId,
|
||||
assigneeId,
|
||||
senderId,
|
||||
} = extractCallData(message);
|
||||
@@ -83,25 +113,37 @@ export function handleVoiceCallCreated(message, currentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldRingInbound(callDirection, currentUserAvailability)) return;
|
||||
|
||||
const callsStore = useCallsStore();
|
||||
callsStore.addCall({
|
||||
callSid,
|
||||
callId,
|
||||
provider,
|
||||
conversationId,
|
||||
inboxId,
|
||||
callDirection,
|
||||
senderId,
|
||||
caller: extractCallerSnapshot(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function handleVoiceCallUpdated(commit, message, currentUserId) {
|
||||
export function handleVoiceCallUpdated(
|
||||
commit,
|
||||
message,
|
||||
currentUserId,
|
||||
currentUserAvailability
|
||||
) {
|
||||
if (!isVoiceCallMessage(message)) return;
|
||||
|
||||
const {
|
||||
callSid,
|
||||
callId,
|
||||
provider,
|
||||
status,
|
||||
callDirection,
|
||||
conversationId,
|
||||
inboxId,
|
||||
assigneeId,
|
||||
senderId,
|
||||
} = extractCallData(message);
|
||||
@@ -129,11 +171,17 @@ export function handleVoiceCallUpdated(commit, message, currentUserId) {
|
||||
}
|
||||
|
||||
if (status === 'ringing') {
|
||||
if (!shouldRingInbound(callDirection, currentUserAvailability)) return;
|
||||
|
||||
callsStore.addCall({
|
||||
callSid,
|
||||
callId,
|
||||
provider,
|
||||
conversationId,
|
||||
inboxId,
|
||||
callDirection,
|
||||
senderId,
|
||||
caller: extractCallerSnapshot(message),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user