chore: message previews in conversation list

This commit is contained in:
Sojan
2025-05-04 06:02:51 -07:00
parent 4ea22f7f36
commit c9daf70655
6 changed files with 521 additions and 383 deletions
@@ -306,6 +306,7 @@ export default {
<MessagePreview
v-if="lastMessageInChat"
:message="lastMessageInChat"
:conversation="chat"
class="conversation--message my-0 mx-2 leading-6 h-6 max-w-[96%] w-[16.875rem] text-sm"
:class="hasUnread ? 'font-medium text-n-slate-12' : 'text-n-slate-11'"
/>
@@ -10,6 +10,10 @@ export default {
type: Object,
required: true,
},
conversation: {
type: Object,
default: () => ({}),
},
showMessageType: {
type: Boolean,
default: true,
@@ -38,7 +42,127 @@ export default {
const { private: isPrivate } = this.message;
return isPrivate;
},
// Simple check: Is this a voice channel conversation?
isVoiceChannel() {
return this.conversation?.meta?.inbox?.channel_type === 'Channel::Voice';
},
// Check if this is a voice call message
isVoiceCall() {
return (
this.message?.content_type === 'voice_call' ||
this.message?.content_attributes?.type === 'voice_call' ||
this.message?.content_attributes?.data?.callType === 'voice_call' ||
this.isVoiceChannel
);
},
// Get call direction for voice calls
isIncomingCall() {
if (!this.isVoiceCall) return false;
// First check conversation attributes
const direction = this.conversation?.additional_attributes?.call_direction;
if (direction) {
return direction === 'inbound';
}
// Then fall back to message type
return this.message.message_type === MESSAGE_TYPE.INCOMING;
},
// Get normalized call status
callStatus() {
if (!this.isVoiceCall) return null;
// Get raw status from conversation
const status = this.conversation?.additional_attributes?.call_status;
// Map status to normalized values
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 this.isIncomingCall ? 'missed' : 'no-answer';
// Return explicit status values as-is
if (status === 'active') return 'active';
if (status === 'missed') return 'missed';
if (status === 'ended') return 'ended';
if (status === 'ringing') return 'ringing';
// Default status
return 'ended';
},
// Voice call icon based on status
voiceCallIcon() {
if (!this.isVoiceCall) return null;
const status = this.callStatus;
const isIncoming = this.isIncomingCall;
if (status === 'missed' || status === 'no-answer') {
return 'phone-missed-call';
}
if (status === 'active') {
return 'phone-in-talk';
}
if (status === 'ended') {
return isIncoming ? 'phone-incoming' : 'phone-outgoing';
}
if (status === 'ringing') {
return isIncoming ? 'phone-incoming' : 'phone-outgoing';
}
// Default based on direction
return isIncoming ? 'phone-incoming' : 'phone-outgoing';
},
parsedLastMessage() {
// For voice calls, return status text
if (this.isVoiceCall) {
// Get status-based text
const status = this.callStatus;
const isIncoming = this.isIncomingCall;
// Return appropriate status text based on call status and direction
if (status === 'active') {
return this.$t('CONVERSATION.VOICE_CALL.CALL_IN_PROGRESS');
}
if (isIncoming) {
if (status === 'ringing') {
return this.$t('CONVERSATION.VOICE_CALL.INCOMING_CALL');
}
if (status === 'missed') {
return this.$t('CONVERSATION.VOICE_CALL.MISSED_CALL');
}
if (status === 'ended') {
return this.$t('CONVERSATION.VOICE_CALL.CALL_ENDED');
}
} else {
if (status === 'ringing') {
return this.$t('CONVERSATION.VOICE_CALL.OUTGOING_CALL');
}
if (status === 'no-answer') {
return this.$t('CONVERSATION.VOICE_CALL.NO_ANSWER');
}
if (status === 'ended') {
return this.$t('CONVERSATION.VOICE_CALL.CALL_ENDED');
}
}
// Default fallback based on direction
return isIncoming
? this.$t('CONVERSATION.VOICE_CALL.INCOMING_CALL')
: this.$t('CONVERSATION.VOICE_CALL.OUTGOING_CALL');
}
// Default behavior for non-voice calls
const { content_attributes: contentAttributes } = this.message;
const { email: { subject } = {} } = contentAttributes || {};
return this.getPlainText(subject || this.message.content);
@@ -69,6 +193,34 @@ export default {
class="-mt-0.5 align-middle text-slate-600 dark:text-slate-300 inline-block"
icon="lock-closed"
/>
<!-- Voice calls with phosphor icons (non-filled variants) -->
<span
v-else-if="isVoiceCall"
class="-mt-0.5 align-middle inline-block mr-1"
:class="{
'text-red-600 dark:text-red-400': callStatus === 'missed' || callStatus === 'no-answer',
'text-green-600 dark:text-green-400': callStatus === 'active' || callStatus === 'ringing',
'text-slate-600 dark:text-slate-300': callStatus === 'ended'
}"
>
<!-- Missed call icon -->
<i v-if="callStatus === 'missed' || callStatus === 'no-answer'"
class="i-ph-phone-x text-base"></i>
<!-- Active call icon -->
<i v-else-if="callStatus === 'active'"
class="i-ph-phone-call text-base"></i>
<!-- Incoming call icon -->
<i v-else-if="(callStatus === 'ended' && isIncomingCall) || (isIncomingCall)"
class="i-ph-phone-incoming text-base"></i>
<!-- Outgoing call icon -->
<i v-else
class="i-ph-phone-outgoing text-base"></i>
</span>
<fluent-icon
v-else-if="messageByAgent"
size="16"
@@ -90,7 +242,7 @@ export default {
/>
{{ $t('CHAT_LIST.ATTACHMENTS.image.CONTENT') }}
</span>
<span v-else-if="message.content">
<span v-else-if="message.content || isVoiceCall">
{{ parsedLastMessage }}
</span>
<span v-else-if="message.attachments">