Merge branch 'develop' into fix/next-message-bubbles

This commit is contained in:
Sivin Varghese
2025-02-11 22:52:44 +05:30
committed by GitHub
23 changed files with 205 additions and 91 deletions
@@ -10,6 +10,7 @@ defineProps({
iconBgColor: { type: String, default: 'bg-n-alpha-3' },
senderTranslationKey: { type: String, required: true },
content: { type: String, required: true },
title: { type: String, default: '' }, // Title can be any name, description, etc
action: {
type: Object,
required: true,
@@ -48,6 +49,9 @@ const senderName = computed(() => {
}}
</div>
<slot>
<div v-if="title" class="truncate text-sm text-n-slate-12">
{{ title }}
</div>
<div v-if="content" class="truncate text-sm text-n-slate-11">
{{ content }}
</div>
@@ -11,7 +11,7 @@ import {
ExceptionWithMessage,
} from 'shared/helpers/CustomErrors';
const { content, attachments } = useMessageContext();
const { attachments } = useMessageContext();
const $store = useStore();
const { t } = useI18n();
@@ -24,6 +24,12 @@ const phoneNumber = computed(() => {
return attachment.value.fallbackTitle;
});
const contactName = computed(() => {
const { meta } = attachment.value ?? {};
const { firstName, lastName } = meta ?? {};
return `${firstName ?? ''} ${lastName ?? ''}`.trim();
});
const formattedPhoneNumber = computed(() => {
return phoneNumber.value.replace(/\s|-|[A-Za-z]/g, '');
});
@@ -32,13 +38,9 @@ const rawPhoneNumber = computed(() => {
return phoneNumber.value.replace(/\D/g, '');
});
const name = computed(() => {
return content.value;
});
function getContactObject() {
const contactItem = {
name: name.value,
name: contactName.value,
phone_number: `+${rawPhoneNumber.value}`,
};
return contactItem;
@@ -99,6 +101,7 @@ const action = computed(() => ({
icon="i-teenyicons-user-circle-solid"
icon-bg-color="bg-[#D6409F]"
sender-translation-key="CONVERSATION.SHARED_ATTACHMENT.CONTACT"
:title="contactName"
:content="phoneNumber"
:action="formattedPhoneNumber ? action : null"
/>
@@ -57,7 +57,7 @@ useEventListener(document.body, 'mouseup', onMouseUp);
useEventListener(document, 'keydown', onKeydown);
onMounted(() => {
if (onClose && typeof onClose === 'function') {
if (import.meta.env.DEV && onClose && typeof onClose === 'function') {
// eslint-disable-next-line no-console
console.warn(
"[DEPRECATED] The 'onClose' prop is deprecated. Please use the 'close' event instead."
@@ -28,10 +28,12 @@ export default {
},
},
created() {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] This component has been deprecated and will be removed soon. Please use v3/components/Form/Button.vue instead'
);
if (import.meta.env.DEV) {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] This component has been deprecated and will be removed soon. Please use v3/components/Form/Button.vue instead'
);
}
},
};
</script>
@@ -30,7 +30,7 @@ import {
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
import { trimContent, debounce } from '@chatwoot/utils';
import { trimContent, debounce, getRecipients } from '@chatwoot/utils';
import wootConstants from 'dashboard/constants/globals';
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
@@ -388,7 +388,6 @@ export default {
watch: {
currentChat(conversation) {
const { can_reply: canReply } = conversation;
this.setCCAndToEmailsFromLastChat();
if (this.isOnPrivateNote) {
@@ -403,6 +402,19 @@ export default {
this.fetchAndSetReplyTo();
},
// When moving from one conversation to another, the store may not have the
// list of all the messages. A fetch is subsequently made to get the messages.
// However, this update does not trigger the `currentChat` watcher.
// We can add a deep watcher to it, but then, that would be too broad of a net to cast
// And would impact performance too. So we watch the messages directly.
// The watcher here is `deep` too, because the messages array is mutated and
// not replaced. So, a shallow watcher would not catch the change.
'currentChat.messages': {
handler() {
this.setCCAndToEmailsFromLastChat();
},
deep: true,
},
conversationIdByRoute(conversationId, oldConversationId) {
if (conversationId !== oldConversationId) {
this.setToDraft(oldConversationId, this.replyType);
@@ -989,45 +1001,20 @@ export default {
this.ccEmails = value.ccEmails;
},
setCCAndToEmailsFromLastChat() {
if (!this.lastEmail) return;
const {
content_attributes: { email: emailAttributes = {} },
} = this.lastEmail;
// Retrieve the email of the current conversation's sender
const conversationContact = this.currentChat?.meta?.sender?.email || '';
let cc = emailAttributes.cc ? [...emailAttributes.cc] : [];
let to = [];
const { email: inboxEmail, forward_to_email: forwardToEmail } =
this.inbox;
// there might be a situation where the current conversation will include a message from a third person,
// and the current conversation contact is in CC.
// This is an edge-case, reported here: CW-1511 [ONLY FOR INTERNAL REFERENCE]
// So we remove the current conversation contact's email from the CC list if present
if (cc.includes(conversationContact)) {
cc = cc.filter(email => email !== conversationContact);
}
// If the last incoming message sender is different from the conversation contact, add them to the "to"
// and add the conversation contact to the CC
if (!emailAttributes.from.includes(conversationContact)) {
to.push(...emailAttributes.from);
cc.push(conversationContact);
}
// Remove the conversation contact's email from the BCC list if present
let bcc = (emailAttributes.bcc || []).filter(
email => email !== conversationContact
const { cc, bcc, to } = getRecipients(
this.lastEmail,
conversationContact,
inboxEmail,
forwardToEmail
);
// Ensure only unique email addresses are in the CC list
bcc = [...new Set(bcc)];
cc = [...new Set(cc)];
to = [...new Set(to)];
this.toEmails = to.join(', ');
this.ccEmails = cc.join(', ');
this.bccEmails = bcc.join(', ');
this.toEmails = to.join(', ');
},
fetchAndSetReplyTo() {
const replyStorageKey = LOCAL_STORAGE_KEYS.MESSAGE_REPLY_TO;
@@ -40,10 +40,12 @@ export default {
},
emits: ['update:modelValue', 'input', 'blur'],
mounted() {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] <WootInput> has be deprecated and will be removed soon. Please use v3/components/Form/Input.vue instead'
);
if (import.meta.env.DEV) {
// eslint-disable-next-line no-console
console.warn(
'[DEPRECATED] <WootInput> has be deprecated and will be removed soon. Please use v3/components/Form/Input.vue instead'
);
}
},
methods: {
onChange(e) {
@@ -152,7 +152,10 @@ export class DashboardAudioNotificationHelper {
const shouldPlayAudio = [];
if (audioAlertType.includes(EVENT_TYPES.ASSIGNED)) {
if (
audioAlertType.includes(EVENT_TYPES.ASSIGNED) ||
audioAlertType.includes('mine')
) {
shouldPlayAudio.push(assignedToMe);
}
if (audioAlertType.includes(EVENT_TYPES.UNASSIGNED)) {
@@ -27,18 +27,12 @@ const getters = {
const selectedChat = _getters.getSelectedChat;
const { messages = [] } = selectedChat;
const lastEmail = [...messages].reverse().find(message => {
const {
content_attributes: contentAttributes = {},
message_type: messageType,
} = message;
const { email = {} } = contentAttributes;
const isIncomingOrOutgoing =
messageType === MESSAGE_TYPE.OUTGOING ||
messageType === MESSAGE_TYPE.INCOMING;
if (email.from && isIncomingOrOutgoing) {
return true;
}
return false;
const { message_type: messageType } = message;
if (message.private) return false;
return [MESSAGE_TYPE.OUTGOING, MESSAGE_TYPE.INCOMING].includes(
messageType
);
});
return lastEmail;
@@ -40,10 +40,12 @@ export default {
},
},
created() {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] This component has been deprecated and will be removed soon. Please use v3/components/Form/Button.vue instead'
);
if (import.meta.env.DEV) {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] This component has been deprecated and will be removed soon. Please use v3/components/Form/Button.vue instead'
);
}
},
};
</script>