fix: CC conditions when the last email is from someone else (#7010)
This commit is contained in:
@@ -48,17 +48,22 @@ class Messages::MessageBuilder
|
|||||||
def process_emails
|
def process_emails
|
||||||
return unless @conversation.inbox&.inbox_type == 'Email'
|
return unless @conversation.inbox&.inbox_type == 'Email'
|
||||||
|
|
||||||
cc_emails = []
|
cc_emails = process_email_string(@params[:cc_emails])
|
||||||
cc_emails = @params[:cc_emails].gsub(/\s+/, '').split(',') if @params[:cc_emails].present?
|
bcc_emails = process_email_string(@params[:bcc_emails])
|
||||||
|
to_emails = process_email_string(@params[:to_emails])
|
||||||
|
|
||||||
bcc_emails = []
|
all_email_addresses = cc_emails + bcc_emails + to_emails
|
||||||
bcc_emails = @params[:bcc_emails].gsub(/\s+/, '').split(',') if @params[:bcc_emails].present?
|
|
||||||
|
|
||||||
all_email_addresses = cc_emails + bcc_emails
|
|
||||||
validate_email_addresses(all_email_addresses)
|
validate_email_addresses(all_email_addresses)
|
||||||
|
|
||||||
@message.content_attributes[:cc_emails] = cc_emails
|
@message.content_attributes[:cc_emails] = cc_emails
|
||||||
@message.content_attributes[:bcc_emails] = bcc_emails
|
@message.content_attributes[:bcc_emails] = bcc_emails
|
||||||
|
@message.content_attributes[:to_emails] = to_emails
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_email_string(email_string)
|
||||||
|
return [] if email_string.blank?
|
||||||
|
|
||||||
|
email_string.gsub(/\s+/, '').split(',')
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_email_addresses(all_emails)
|
def validate_email_addresses(all_emails)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const buildCreatePayload = ({
|
|||||||
files,
|
files,
|
||||||
ccEmails = '',
|
ccEmails = '',
|
||||||
bccEmails = '',
|
bccEmails = '',
|
||||||
|
toEmails = '',
|
||||||
templateParams,
|
templateParams,
|
||||||
}) => {
|
}) => {
|
||||||
let payload;
|
let payload;
|
||||||
@@ -25,6 +26,9 @@ export const buildCreatePayload = ({
|
|||||||
payload.append('echo_id', echoId);
|
payload.append('echo_id', echoId);
|
||||||
payload.append('cc_emails', ccEmails);
|
payload.append('cc_emails', ccEmails);
|
||||||
payload.append('bcc_emails', bccEmails);
|
payload.append('bcc_emails', bccEmails);
|
||||||
|
if (toEmails) {
|
||||||
|
payload.append('to_emails', toEmails);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
payload = {
|
payload = {
|
||||||
content: message,
|
content: message,
|
||||||
@@ -33,6 +37,7 @@ export const buildCreatePayload = ({
|
|||||||
content_attributes: contentAttributes,
|
content_attributes: contentAttributes,
|
||||||
cc_emails: ccEmails,
|
cc_emails: ccEmails,
|
||||||
bcc_emails: bccEmails,
|
bcc_emails: bccEmails,
|
||||||
|
to_emails: toEmails,
|
||||||
template_params: templateParams,
|
template_params: templateParams,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -53,6 +58,7 @@ class MessageApi extends ApiClient {
|
|||||||
files,
|
files,
|
||||||
ccEmails = '',
|
ccEmails = '',
|
||||||
bccEmails = '',
|
bccEmails = '',
|
||||||
|
toEmails = '',
|
||||||
templateParams,
|
templateParams,
|
||||||
}) {
|
}) {
|
||||||
return axios({
|
return axios({
|
||||||
@@ -66,6 +72,7 @@ class MessageApi extends ApiClient {
|
|||||||
files,
|
files,
|
||||||
ccEmails,
|
ccEmails,
|
||||||
bccEmails,
|
bccEmails,
|
||||||
|
toEmails,
|
||||||
templateParams,
|
templateParams,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
v-if="showReplyHead"
|
v-if="showReplyHead"
|
||||||
:cc-emails.sync="ccEmails"
|
:cc-emails.sync="ccEmails"
|
||||||
:bcc-emails.sync="bccEmails"
|
:bcc-emails.sync="bccEmails"
|
||||||
|
:to-emails.sync="toEmails"
|
||||||
/>
|
/>
|
||||||
<woot-audio-recorder
|
<woot-audio-recorder
|
||||||
v-if="showAudioRecorderEditor"
|
v-if="showAudioRecorderEditor"
|
||||||
@@ -238,6 +239,7 @@ export default {
|
|||||||
hasSlashCommand: false,
|
hasSlashCommand: false,
|
||||||
bccEmails: '',
|
bccEmails: '',
|
||||||
ccEmails: '',
|
ccEmails: '',
|
||||||
|
toEmails: '',
|
||||||
doAutoSaveDraft: () => {},
|
doAutoSaveDraft: () => {},
|
||||||
showWhatsAppTemplatesModal: false,
|
showWhatsAppTemplatesModal: false,
|
||||||
updateEditorSelectionWith: '',
|
updateEditorSelectionWith: '',
|
||||||
@@ -528,7 +530,7 @@ export default {
|
|||||||
this.replyType = REPLY_EDITOR_MODES.NOTE;
|
this.replyType = REPLY_EDITOR_MODES.NOTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCCEmailFromLastChat();
|
this.setCCAndToEmailsFromLastChat();
|
||||||
},
|
},
|
||||||
conversationIdByRoute(conversationId, oldConversationId) {
|
conversationIdByRoute(conversationId, oldConversationId) {
|
||||||
if (conversationId !== oldConversationId) {
|
if (conversationId !== oldConversationId) {
|
||||||
@@ -562,7 +564,7 @@ export default {
|
|||||||
// working even if input/textarea is focussed.
|
// working even if input/textarea is focussed.
|
||||||
document.addEventListener('paste', this.onPaste);
|
document.addEventListener('paste', this.onPaste);
|
||||||
document.addEventListener('keydown', this.handleKeyEvents);
|
document.addEventListener('keydown', this.handleKeyEvents);
|
||||||
this.setCCEmailFromLastChat();
|
this.setCCAndToEmailsFromLastChat();
|
||||||
this.doAutoSaveDraft = debounce(
|
this.doAutoSaveDraft = debounce(
|
||||||
() => {
|
() => {
|
||||||
this.saveDraft(this.conversationIdByRoute, this.replyType);
|
this.saveDraft(this.conversationIdByRoute, this.replyType);
|
||||||
@@ -844,6 +846,7 @@ export default {
|
|||||||
clearEmailField() {
|
clearEmailField() {
|
||||||
this.ccEmails = '';
|
this.ccEmails = '';
|
||||||
this.bccEmails = '';
|
this.bccEmails = '';
|
||||||
|
this.toEmails = '';
|
||||||
},
|
},
|
||||||
toggleEmojiPicker() {
|
toggleEmojiPicker() {
|
||||||
this.showEmojiPicker = !this.showEmojiPicker;
|
this.showEmojiPicker = !this.showEmojiPicker;
|
||||||
@@ -1054,22 +1057,56 @@ export default {
|
|||||||
messagePayload.bccEmails = this.bccEmails;
|
messagePayload.bccEmails = this.bccEmails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.toEmails && !this.isOnPrivateNote) {
|
||||||
|
messagePayload.toEmails = this.toEmails;
|
||||||
|
}
|
||||||
|
|
||||||
return messagePayload;
|
return messagePayload;
|
||||||
},
|
},
|
||||||
setCcEmails(value) {
|
setCcEmails(value) {
|
||||||
this.bccEmails = value.bccEmails;
|
this.bccEmails = value.bccEmails;
|
||||||
this.ccEmails = value.ccEmails;
|
this.ccEmails = value.ccEmails;
|
||||||
},
|
},
|
||||||
setCCEmailFromLastChat() {
|
setCCAndToEmailsFromLastChat() {
|
||||||
if (this.lastEmail) {
|
if (!this.lastEmail) return;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
content_attributes: { email: emailAttributes = {} },
|
content_attributes: { email: emailAttributes = {} },
|
||||||
} = this.lastEmail;
|
} = this.lastEmail;
|
||||||
const cc = emailAttributes.cc || [];
|
|
||||||
const bcc = emailAttributes.bcc || [];
|
// Retrieve the email of the current conversation's sender
|
||||||
|
const conversationContact = this.currentChat?.meta?.sender?.email || '';
|
||||||
|
let cc = [...emailAttributes.cc] || [];
|
||||||
|
let to = [];
|
||||||
|
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure only unique email addresses are in the CC list
|
||||||
|
bcc = [...new Set(bcc)];
|
||||||
|
cc = [...new Set(cc)];
|
||||||
|
to = [...new Set(to)];
|
||||||
|
|
||||||
this.ccEmails = cc.join(', ');
|
this.ccEmails = cc.join(', ');
|
||||||
this.bccEmails = bcc.join(', ');
|
this.bccEmails = bcc.join(', ');
|
||||||
}
|
this.toEmails = to.join(', ');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<div v-if="toEmails">
|
||||||
|
<div class="input-group small" :class="{ error: $v.toEmailsVal.$error }">
|
||||||
|
<label class="input-group-label">
|
||||||
|
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.TO') }}
|
||||||
|
</label>
|
||||||
|
<div class="input-group-field">
|
||||||
|
<woot-input
|
||||||
|
v-model.trim="$v.toEmailsVal.$model"
|
||||||
|
type="text"
|
||||||
|
:class="{ error: $v.toEmailsVal.$error }"
|
||||||
|
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
|
||||||
|
@blur="onBlur"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="input-group-wrap">
|
<div class="input-group-wrap">
|
||||||
<div class="input-group small" :class="{ error: $v.ccEmailsVal.$error }">
|
<div class="input-group small" :class="{ error: $v.ccEmailsVal.$error }">
|
||||||
<label class="input-group-label">
|
<label class="input-group-label">
|
||||||
@@ -53,6 +69,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { validEmailsByComma } from './helpers/emailHeadHelper';
|
import { validEmailsByComma } from './helpers/emailHeadHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
ccEmails: {
|
ccEmails: {
|
||||||
@@ -63,12 +80,17 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
toEmails: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showBcc: false,
|
showBcc: false,
|
||||||
ccEmailsVal: '',
|
ccEmailsVal: '',
|
||||||
bccEmailsVal: '',
|
bccEmailsVal: '',
|
||||||
|
toEmailsVal: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -82,10 +104,16 @@ export default {
|
|||||||
this.ccEmailsVal = newVal;
|
this.ccEmailsVal = newVal;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toEmails(newVal) {
|
||||||
|
if (newVal !== this.toEmailsVal) {
|
||||||
|
this.toEmailsVal = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.ccEmailsVal = this.ccEmails;
|
this.ccEmailsVal = this.ccEmails;
|
||||||
this.bccEmailsVal = this.bccEmails;
|
this.bccEmailsVal = this.bccEmails;
|
||||||
|
this.toEmailsVal = this.toEmails;
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
ccEmailsVal: {
|
ccEmailsVal: {
|
||||||
@@ -98,6 +126,11 @@ export default {
|
|||||||
return validEmailsByComma(value);
|
return validEmailsByComma(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
toEmailsVal: {
|
||||||
|
hasValidEmails(value) {
|
||||||
|
return validEmailsByComma(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleAddBcc() {
|
handleAddBcc() {
|
||||||
@@ -107,6 +140,7 @@ export default {
|
|||||||
this.$v.$touch();
|
this.$v.$touch();
|
||||||
this.$emit('update:bccEmails', this.bccEmailsVal);
|
this.$emit('update:bccEmails', this.bccEmailsVal);
|
||||||
this.$emit('update:ccEmails', this.ccEmailsVal);
|
this.$emit('update:ccEmails', this.ccEmailsVal);
|
||||||
|
this.$emit('update:toEmails', this.toEmailsVal);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
"STOP_AUDIO_RECORDING": "Stop audio recording",
|
"STOP_AUDIO_RECORDING": "Stop audio recording",
|
||||||
"": "",
|
"": "",
|
||||||
"EMAIL_HEAD": {
|
"EMAIL_HEAD": {
|
||||||
|
"TO": "TO",
|
||||||
"ADD_BCC": "Add bcc",
|
"ADD_BCC": "Add bcc",
|
||||||
"CC": {
|
"CC": {
|
||||||
"LABEL": "CC",
|
"LABEL": "CC",
|
||||||
|
|||||||
@@ -162,6 +162,20 @@ class ConversationReplyMailer < ApplicationMailer
|
|||||||
[content_attributes[:cc_emails], content_attributes[:bcc_emails]]
|
[content_attributes[:cc_emails], content_attributes[:bcc_emails]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_emails_from_content_attributes
|
||||||
|
content_attributes = @conversation.messages.outgoing.last&.content_attributes
|
||||||
|
|
||||||
|
return [] unless content_attributes
|
||||||
|
return [] unless content_attributes[:to_emails]
|
||||||
|
|
||||||
|
content_attributes[:to_emails]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_emails
|
||||||
|
# if there is no to_emails from content_attributes, send it to @contact&.email
|
||||||
|
to_emails_from_content_attributes.presence || [@contact&.email]
|
||||||
|
end
|
||||||
|
|
||||||
def inbound_email_enabled?
|
def inbound_email_enabled?
|
||||||
@inbound_email_enabled ||= @account.feature_enabled?('inbound_emails') && @account.inbound_email_domain
|
@inbound_email_enabled ||= @account.feature_enabled?('inbound_emails') && @account.inbound_email_domain
|
||||||
.present? && @account.support_email.present?
|
.present? && @account.support_email.present?
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module ConversationReplyMailerHelper
|
module ConversationReplyMailerHelper
|
||||||
def prepare_mail(cc_bcc_enabled)
|
def prepare_mail(cc_bcc_enabled)
|
||||||
@options = {
|
@options = {
|
||||||
to: @contact&.email,
|
to: to_emails,
|
||||||
from: email_from,
|
from: email_from,
|
||||||
reply_to: email_reply_to,
|
reply_to: email_reply_to,
|
||||||
subject: mail_subject,
|
subject: mail_subject,
|
||||||
|
|||||||
Reference in New Issue
Block a user