diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue index d3675dbac..7a876aaa6 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue @@ -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; diff --git a/app/javascript/dashboard/store/modules/conversations/getters.js b/app/javascript/dashboard/store/modules/conversations/getters.js index 085766bfd..3695de6a1 100644 --- a/app/javascript/dashboard/store/modules/conversations/getters.js +++ b/app/javascript/dashboard/store/modules/conversations/getters.js @@ -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; diff --git a/package.json b/package.json index b3fbda8f0..aefa5439d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@breezystack/lamejs": "^1.2.7", "@chatwoot/ninja-keys": "1.2.3", "@chatwoot/prosemirror-schema": "1.1.1-next", - "@chatwoot/utils": "^0.0.35", + "@chatwoot/utils": "^0.0.38", "@formkit/core": "^1.6.7", "@formkit/vue": "^1.6.7", "@hcaptcha/vue3-hcaptcha": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20142653e..6bbf22593 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,8 +23,8 @@ importers: specifier: 1.1.1-next version: 1.1.1-next '@chatwoot/utils': - specifier: ^0.0.35 - version: 0.0.35 + specifier: ^0.0.38 + version: 0.0.38 '@formkit/core': specifier: ^1.6.7 version: 1.6.7 @@ -376,6 +376,10 @@ packages: resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.26.7': + resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.26.0': resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} @@ -393,8 +397,8 @@ packages: '@chatwoot/prosemirror-schema@1.1.1-next': resolution: {integrity: sha512-/M2qZ+ZF7GlQNt1riwVP499fvp3hxSqd5iy8hxyF9pkj9qQ+OKYn5JK+v3qwwqQY3IxhmNOn1Lp6tm7vstrd9Q==} - '@chatwoot/utils@0.0.35': - resolution: {integrity: sha512-uSRbd3pFp+IcEhsRtK1XGcFGFJc+X/YIwQnQrVDXsvsX3Mm7HEANj+Yz6J2clfHotajniwJwH2u5/y48+JrTyA==} + '@chatwoot/utils@0.0.38': + resolution: {integrity: sha512-6CTvuueBQLZJcm++pI2ZBY8Pp7OP3WzPCYyXoCagl8ZLpOfpjyVkLx9fc81falOoaVa/r+7EZ85Cv7vkT0ZyQw==} engines: {node: '>=10'} '@codemirror/commands@6.7.0': @@ -2372,8 +2376,8 @@ packages: resolution: {integrity: sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA==} engines: {node: '>=0.11'} - date-fns@2.29.3: - resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} date-format-parse@0.2.7: @@ -5092,6 +5096,10 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.26.7': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/types@7.26.0': dependencies: '@babel/helper-string-parser': 7.25.9 @@ -5125,9 +5133,9 @@ snapshots: prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3) prosemirror-view: 1.34.1 - '@chatwoot/utils@0.0.35': + '@chatwoot/utils@0.0.38': dependencies: - date-fns: 2.29.3 + date-fns: 2.30.0 '@codemirror/commands@6.7.0': dependencies: @@ -7380,7 +7388,9 @@ snapshots: date-fns@2.21.1: {} - date-fns@2.29.3: {} + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.26.7 date-format-parse@0.2.7: {}