diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue index 560d471b4..5224c04b1 100644 --- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue +++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue @@ -2,7 +2,6 @@ import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useMessageFormatter } from 'shared/composables/useMessageFormatter'; -import { useVoiceCallHelpers } from 'dashboard/composables/useVoiceCallHelpers'; import Avatar from 'dashboard/components-next/avatar/Avatar.vue'; @@ -16,197 +15,84 @@ const props = defineProps({ const { t } = useI18n(); const { getPlainText } = useMessageFormatter(); -// Use our shared voice call helper -const { - isVoiceChannelConversation, - hasArrow: checkHasArrow, - isIncomingCall: checkIsIncoming, - normalizeCallStatus, - getCallIconName, - getStatusText, - processArrowContent, -} = useVoiceCallHelpers(props, { t }); - -// Utility function to find the last voice call message in conversation -const findVoiceCallMessage = (conversation) => { - // If conversation has messages property, look for voice call messages - if (conversation && conversation.messages && Array.isArray(conversation.messages)) { - // Look through messages in reverse to find the latest voice call - for (let i = conversation.messages.length - 1; i >= 0; i--) { - const msg = conversation.messages[i]; - if (msg.content_type === 'voice_call' || msg.content_type === 'voice') { - return msg; - } - } - } - - // If no voice call found in messages or messages not available, check lastNonActivityMessage - const { lastNonActivityMessage } = conversation || {}; - - if (lastNonActivityMessage?.content_type === 'voice_call' || - lastNonActivityMessage?.content_type === 'voice') { - return lastNonActivityMessage; - } - - // Check if conversation has a call_status in additional_attributes - // This is a strong indicator of a voice call conversation - if (conversation?.additional_attributes?.call_status) { - // If we have a call status but no voice call message, the lastNonActivityMessage - // might still be related to the call (even if it doesn't have the right content_type) - if (lastNonActivityMessage) { - return lastNonActivityMessage; - } - } - - // As a fallback, check if last message content includes "Voice Call" or common call-related terms - if (lastNonActivityMessage?.content && - typeof lastNonActivityMessage.content === 'string') { - const content = lastNonActivityMessage.content.toLowerCase(); - if (content.includes('voice call') || - content.includes('call ') || - content.includes('missed call') || - content.includes('incoming call') || - content.includes('outgoing call') || - content.startsWith('←') || - content.startsWith('→')) { - return lastNonActivityMessage; - } - } - - return null; -}; - -const voiceCallMessage = computed(() => { - return findVoiceCallMessage(props.conversation); -}); - -const isVoiceCall = computed(() => { - // Force voice call view for voice channel conversations - if (isVoiceChannelConversation.value) { - return true; - } - - // Check if conversation has a call_status in additional_attributes - if (props.conversation?.additional_attributes?.call_status) { - return true; - } - - // Check for voice call message - return !!voiceCallMessage.value; -}); - -const callData = computed(() => { - if (!isVoiceCall.value) return {}; - - // First check for data directly in conversation attributes - const conversationAttributes = props.conversation?.custom_attributes || - props.conversation?.additional_attributes || {}; - if (conversationAttributes.call_data) { - return conversationAttributes.call_data; - } - - // Then check message content attributes - if (voiceCallMessage.value?.content_attributes?.data) { - return voiceCallMessage.value.content_attributes.data; - } - - return {}; -}); - -const hasArrow = computed(() => { - return checkHasArrow(voiceCallMessage.value); +// Simple check: Is this a voice channel conversation? +const isVoiceChannel = computed(() => { + return props.conversation?.meta?.inbox?.channel_type === 'Channel::Voice'; }); +// Get call direction: inbound or outbound const isIncomingCall = computed(() => { - if (!isVoiceCall.value) return null; + if (!isVoiceChannel.value) return false; - // Get the conversation call_status - const conversationCallStatus = props.conversation?.additional_attributes?.call_status; - - return checkIsIncoming(callData.value, voiceCallMessage.value); + const direction = props.conversation?.additional_attributes?.call_direction; + return direction === 'inbound'; }); +// Simple function to normalize call status const normalizedCallStatus = computed(() => { - if (!isVoiceCall.value) return ''; + if (!isVoiceChannel.value) return null; - // First check for direct call_status in the conversation additional_attributes - // This is the most authoritative source for call status - const conversationCallStatus = props.conversation?.additional_attributes?.call_status; - if (conversationCallStatus) { - return normalizeCallStatus(conversationCallStatus, isIncomingCall.value); - } + // Get the raw status directly from conversation + const status = props.conversation?.additional_attributes?.call_status; - // If there's an arrow in the message, this is a legacy format message - if (hasArrow.value) { - const content = voiceCallMessage.value?.content || ''; - if (content.includes('ended') || content.includes('Call ended')) { - return 'ended'; - } - if (content.includes('missed') || content.includes('Missed call') || content.includes('no answer')) { - return isIncomingCall.value ? 'missed' : 'no-answer'; - } - if (content.includes('in progress') || content.includes('active') || content.includes('answered')) { - return 'active'; - } - - // For voice channel conversations, default to ended for better display - if (isVoiceChannelConversation.value) { - return 'ended'; - } - - // Default to ended for legacy messages - return 'ended'; - } + // Simple mapping of call statuses + if (status === 'in-progress') return 'active'; + if (status === 'completed') return 'ended'; + if (status === 'canceled') return 'ended'; + if (status === 'failed') return 'ended'; + if (status === 'busy') return 'no-answer'; + if (status === 'no-answer') return isIncomingCall.value ? 'missed' : 'no-answer'; - // Apply the same status mapping logic as VoiceCall component - const callStatus = callData.value?.status; - if (callStatus) { - return normalizeCallStatus(callStatus, isIncomingCall.value); - } + // Return the status as is for explicit values + if (status === 'active') return 'active'; + if (status === 'missed') return 'missed'; + if (status === 'ended') return 'ended'; + if (status === 'ringing') return 'ringing'; - // Determine status from timestamps - if (callData.value?.ended_at) { - return 'ended'; - } - if (callData.value?.missed) { - return isIncomingCall.value ? 'missed' : 'no-answer'; - } - if (callData.value?.started_at || props.conversation?.additional_attributes?.call_started_at) { - return 'active'; - } - - // For voice channel conversations, default to ended for better display - if (isVoiceChannelConversation.value) { - return 'ended'; - } - - // Default to ended for any remaining cases to avoid showing incorrect status + // If no status is set, default to 'ended' return 'ended'; }); -const callIconName = computed(() => { - return getCallIconName(normalizedCallStatus.value, isIncomingCall.value); -}); - +// Get formatted call status text for voice channel conversations const callStatusText = computed(() => { - if (!isVoiceCall.value) return ''; + if (!isVoiceChannel.value) return ''; - // For voice channel conversations, force more descriptive text - if (isVoiceChannelConversation.value) { - return getStatusText(normalizedCallStatus.value, isIncomingCall.value); + const status = normalizedCallStatus.value; + const isIncoming = isIncomingCall.value; + + if (status === 'active') { + return t('CONVERSATION.VOICE_CALL.CALL_IN_PROGRESS'); } - // For legacy messages with arrows, just use a cleaner version of the content - if (hasArrow.value && voiceCallMessage.value?.content) { - return processArrowContent( - voiceCallMessage.value.content, - isIncomingCall.value, - normalizedCallStatus.value - ); + if (isIncoming) { + if (status === 'ringing') { + return t('CONVERSATION.VOICE_CALL.INCOMING_CALL'); + } + + if (status === 'missed') { + return t('CONVERSATION.VOICE_CALL.MISSED_CALL'); + } + + if (status === 'ended') { + return t('CONVERSATION.VOICE_CALL.CALL_ENDED'); + } + } else { + if (status === 'ringing') { + return t('CONVERSATION.VOICE_CALL.OUTGOING_CALL'); + } + + if (status === 'no-answer') { + return t('CONVERSATION.VOICE_CALL.NO_ANSWER'); + } + + if (status === 'ended') { + return t('CONVERSATION.VOICE_CALL.CALL_ENDED'); + } } - // Generate the correct status text based on call status and direction - return getStatusText(normalizedCallStatus.value, isIncomingCall.value); + return isIncoming + ? t('CONVERSATION.VOICE_CALL.INCOMING_CALL') + : t('CONVERSATION.VOICE_CALL.OUTGOING_CALL'); }); // Return proper message content based on message type @@ -216,7 +102,7 @@ const lastNonActivityMessageContent = computed(() => { const { email: { subject } = {} } = customAttributes; // Return special formatting for voice calls - if (isVoiceCall.value) { + if (isVoiceChannel.value) { return callStatusText.value; } @@ -244,7 +130,7 @@ const unreadMessagesCount = computed(() => {