Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3375fb84 | ||
|
|
58ce9bfc19 | ||
|
|
63a071f0d3 | ||
|
|
f0d3ae09d9 |
@@ -2,10 +2,6 @@
|
|||||||
transition: box-shadow 0.35s $swift-ease-out-function,
|
transition: box-shadow 0.35s $swift-ease-out-function,
|
||||||
height 2s $swift-ease-out-function;
|
height 2s $swift-ease-out-function;
|
||||||
|
|
||||||
&.is-focused {
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-box__top {
|
.reply-box__top {
|
||||||
.icon {
|
.icon {
|
||||||
color: $medium-gray;
|
color: $medium-gray;
|
||||||
@@ -47,8 +43,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.is-private {
|
&.is-private {
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
|
||||||
|
|
||||||
.reply-box__top {
|
.reply-box__top {
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
@apply bg-yellow-100 dark:bg-yellow-800;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="reply-box" :class="replyBoxClass">
|
<div
|
||||||
|
class="border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900 reply-box"
|
||||||
|
:class="{
|
||||||
|
'bg-yellow-100 dark:bg-yellow-800 is-private': isPrivate,
|
||||||
|
'shadow-sm': isFocused || hasAttachments,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<banner
|
<banner
|
||||||
v-if="showSelfAssignBanner"
|
v-if="showSelfAssignBanner"
|
||||||
action-button-variant="clear"
|
action-button-variant="clear"
|
||||||
@@ -136,48 +142,58 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Vuex
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
// Mixins
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
import CannedResponse from './CannedResponse.vue';
|
|
||||||
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
|
|
||||||
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.vue';
|
|
||||||
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue';
|
|
||||||
import ReplyEmailHead from './ReplyEmailHead.vue';
|
|
||||||
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue';
|
|
||||||
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert';
|
|
||||||
import Banner from 'dashboard/components/ui/Banner.vue';
|
|
||||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
|
||||||
import WootAudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder.vue';
|
|
||||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
|
||||||
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
|
||||||
import {
|
|
||||||
getMessageVariables,
|
|
||||||
getUndefinedVariablesInMessage,
|
|
||||||
replaceVariablesInMessage,
|
|
||||||
} from '@chatwoot/utils';
|
|
||||||
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
|
|
||||||
import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
|
|
||||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
|
||||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||||
import { trimContent, debounce } from '@chatwoot/utils';
|
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
|
||||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
|
||||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
|
||||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||||
|
import replyDraftMixin from 'dashboard/mixins/replyDraftMixin';
|
||||||
|
import audioRecordingMixin from 'dashboard/mixins/audioRecordingMixin';
|
||||||
|
import emailEditorMixin from 'dashboard/mixins/emailEditorMixin';
|
||||||
|
import attachmentHandlerMixin from 'dashboard/mixins/attachmentHandlerMixin';
|
||||||
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
|
||||||
|
// constants
|
||||||
|
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||||
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
|
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||||
|
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
|
||||||
|
// components
|
||||||
|
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.vue';
|
||||||
|
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||||
|
import CannedResponse from './CannedResponse.vue';
|
||||||
|
import EmojiInput from 'shared/components/emoji/EmojiInput.vue';
|
||||||
|
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert.vue';
|
||||||
|
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue';
|
||||||
|
import ReplyEmailHead from './ReplyEmailHead.vue';
|
||||||
|
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue';
|
||||||
|
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
|
||||||
|
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
|
||||||
|
import WootAudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder.vue';
|
||||||
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
|
||||||
|
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||||
import {
|
import {
|
||||||
appendSignature,
|
appendSignature,
|
||||||
removeSignature,
|
removeSignature,
|
||||||
replaceSignature,
|
replaceSignature,
|
||||||
extractTextFromMarkdown,
|
extractTextFromMarkdown,
|
||||||
} from 'dashboard/helper/editorHelper';
|
} from 'dashboard/helper/editorHelper';
|
||||||
|
import {
|
||||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
getMessageVariables,
|
||||||
|
getUndefinedVariablesInMessage,
|
||||||
|
replaceVariablesInMessage,
|
||||||
|
debounce,
|
||||||
|
} from '@chatwoot/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -202,6 +218,10 @@ export default {
|
|||||||
messageFormatterMixin,
|
messageFormatterMixin,
|
||||||
rtlMixin,
|
rtlMixin,
|
||||||
fileUploadMixin,
|
fileUploadMixin,
|
||||||
|
replyDraftMixin,
|
||||||
|
audioRecordingMixin,
|
||||||
|
emailEditorMixin,
|
||||||
|
attachmentHandlerMixin,
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
selectedTweet: {
|
selectedTweet: {
|
||||||
@@ -222,17 +242,10 @@ export default {
|
|||||||
message: '',
|
message: '',
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
attachedFiles: [],
|
|
||||||
isRecordingAudio: false,
|
|
||||||
recordingAudioState: '',
|
|
||||||
recordingAudioDurationText: '',
|
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
replyType: REPLY_EDITOR_MODES.REPLY,
|
replyType: REPLY_EDITOR_MODES.REPLY,
|
||||||
mentionSearchKey: '',
|
mentionSearchKey: '',
|
||||||
hasSlashCommand: false,
|
hasSlashCommand: false,
|
||||||
bccEmails: '',
|
|
||||||
ccEmails: '',
|
|
||||||
toEmails: '',
|
|
||||||
doAutoSaveDraft: () => {},
|
doAutoSaveDraft: () => {},
|
||||||
showWhatsAppTemplatesModal: false,
|
showWhatsAppTemplatesModal: false,
|
||||||
updateEditorSelectionWith: '',
|
updateEditorSelectionWith: '',
|
||||||
@@ -389,15 +402,6 @@ export default {
|
|||||||
: '(↵)';
|
: '(↵)';
|
||||||
return `${sendMessageText} ${keyLabel}`;
|
return `${sendMessageText} ${keyLabel}`;
|
||||||
},
|
},
|
||||||
replyBoxClass() {
|
|
||||||
return {
|
|
||||||
'is-private': this.isPrivate,
|
|
||||||
'is-focused': this.isFocused || this.hasAttachments,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
hasAttachments() {
|
|
||||||
return this.attachedFiles.length;
|
|
||||||
},
|
|
||||||
hasRecordedAudio() {
|
hasRecordedAudio() {
|
||||||
return (
|
return (
|
||||||
this.$refs.audioRecorderInput &&
|
this.$refs.audioRecorderInput &&
|
||||||
@@ -407,12 +411,6 @@ export default {
|
|||||||
isRichEditorEnabled() {
|
isRichEditorEnabled() {
|
||||||
return this.isAWebWidgetInbox || this.isAnEmailChannel;
|
return this.isAWebWidgetInbox || this.isAnEmailChannel;
|
||||||
},
|
},
|
||||||
showAudioRecorder() {
|
|
||||||
return !this.isOnPrivateNote && this.showFileUpload;
|
|
||||||
},
|
|
||||||
showAudioRecorderEditor() {
|
|
||||||
return this.showAudioRecorder && this.isRecordingAudio;
|
|
||||||
},
|
|
||||||
isOnPrivateNote() {
|
isOnPrivateNote() {
|
||||||
return this.replyType === REPLY_EDITOR_MODES.NOTE;
|
return this.replyType === REPLY_EDITOR_MODES.NOTE;
|
||||||
},
|
},
|
||||||
@@ -492,12 +490,6 @@ export default {
|
|||||||
editorStateId() {
|
editorStateId() {
|
||||||
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||||
},
|
},
|
||||||
audioRecordFormat() {
|
|
||||||
if (this.isAWhatsAppChannel || this.isAPIInbox) {
|
|
||||||
return AUDIO_FORMATS.OGG;
|
|
||||||
}
|
|
||||||
return AUDIO_FORMATS.WAV;
|
|
||||||
},
|
|
||||||
messageVariables() {
|
messageVariables() {
|
||||||
const variables = getMessageVariables({
|
const variables = getMessageVariables({
|
||||||
conversation: this.currentChat,
|
conversation: this.currentChat,
|
||||||
@@ -595,31 +587,6 @@ export default {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveDraft(conversationId, replyType) {
|
|
||||||
if (this.message || this.message === '') {
|
|
||||||
const key = `draft-${conversationId}-${replyType}`;
|
|
||||||
const draftToSave = trimContent(this.message || '');
|
|
||||||
|
|
||||||
this.$store.dispatch('draftMessages/set', {
|
|
||||||
key,
|
|
||||||
message: draftToSave,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setToDraft(conversationId, replyType) {
|
|
||||||
this.saveDraft(conversationId, replyType);
|
|
||||||
this.message = '';
|
|
||||||
},
|
|
||||||
getFromDraft() {
|
|
||||||
if (this.conversationIdByRoute) {
|
|
||||||
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
|
||||||
const messageFromStore =
|
|
||||||
this.$store.getters['draftMessages/get'](key) || '';
|
|
||||||
|
|
||||||
// ensure that the message has signature set based on the ui setting
|
|
||||||
this.message = this.toggleSignatureForDraft(messageFromStore);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleSignatureForDraft(message) {
|
toggleSignatureForDraft(message) {
|
||||||
if (this.isPrivate) {
|
if (this.isPrivate) {
|
||||||
return message;
|
return message;
|
||||||
@@ -629,12 +596,6 @@ export default {
|
|||||||
? appendSignature(message, this.signatureToApply)
|
? appendSignature(message, this.signatureToApply)
|
||||||
: removeSignature(message, this.signatureToApply);
|
: removeSignature(message, this.signatureToApply);
|
||||||
},
|
},
|
||||||
removeFromDraft() {
|
|
||||||
if (this.conversationIdByRoute) {
|
|
||||||
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
|
||||||
this.$store.dispatch('draftMessages/delete', { key });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleKeyEvents(e) {
|
handleKeyEvents(e) {
|
||||||
const keyCode = buildHotKeys(e);
|
const keyCode = buildHotKeys(e);
|
||||||
if (keyCode === 'escape') {
|
if (keyCode === 'escape') {
|
||||||
@@ -856,32 +817,9 @@ export default {
|
|||||||
this.attachedFiles = [];
|
this.attachedFiles = [];
|
||||||
this.isRecordingAudio = false;
|
this.isRecordingAudio = false;
|
||||||
},
|
},
|
||||||
clearEmailField() {
|
|
||||||
this.ccEmails = '';
|
|
||||||
this.bccEmails = '';
|
|
||||||
this.toEmails = '';
|
|
||||||
},
|
|
||||||
toggleEmojiPicker() {
|
toggleEmojiPicker() {
|
||||||
this.showEmojiPicker = !this.showEmojiPicker;
|
this.showEmojiPicker = !this.showEmojiPicker;
|
||||||
},
|
},
|
||||||
toggleAudioRecorder() {
|
|
||||||
this.isRecordingAudio = !this.isRecordingAudio;
|
|
||||||
this.isRecorderAudioStopped = !this.isRecordingAudio;
|
|
||||||
if (!this.isRecordingAudio) {
|
|
||||||
this.clearMessage();
|
|
||||||
this.clearEmailField();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleAudioRecorderPlayPause() {
|
|
||||||
if (this.isRecordingAudio) {
|
|
||||||
if (!this.isRecorderAudioStopped) {
|
|
||||||
this.isRecorderAudioStopped = true;
|
|
||||||
this.$refs.audioRecorderInput.stopAudioRecording();
|
|
||||||
} else if (this.isRecorderAudioStopped) {
|
|
||||||
this.$refs.audioRecorderInput.playPause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hideEmojiPicker() {
|
hideEmojiPicker() {
|
||||||
if (this.showEmojiPicker) {
|
if (this.showEmojiPicker) {
|
||||||
this.toggleEmojiPicker();
|
this.toggleEmojiPicker();
|
||||||
@@ -903,18 +841,6 @@ export default {
|
|||||||
onFocus() {
|
onFocus() {
|
||||||
this.isFocused = true;
|
this.isFocused = true;
|
||||||
},
|
},
|
||||||
onStateProgressRecorderChanged(duration) {
|
|
||||||
this.recordingAudioDurationText = duration;
|
|
||||||
},
|
|
||||||
onStateRecorderChanged(state) {
|
|
||||||
this.recordingAudioState = state;
|
|
||||||
if (state && 'notallowederror'.includes(state)) {
|
|
||||||
this.toggleAudioRecorder();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFinishRecorder(file) {
|
|
||||||
return file && this.onFileUpload(file);
|
|
||||||
},
|
|
||||||
toggleTyping(status) {
|
toggleTyping(status) {
|
||||||
const conversationId = this.currentChat.id;
|
const conversationId = this.currentChat.id;
|
||||||
const isPrivate = this.isPrivate;
|
const isPrivate = this.isPrivate;
|
||||||
@@ -929,24 +855,6 @@ export default {
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
attachFile({ blob, file }) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file.file);
|
|
||||||
reader.onloadend = () => {
|
|
||||||
this.attachedFiles.push({
|
|
||||||
currentChatId: this.currentChat.id,
|
|
||||||
resource: blob || file,
|
|
||||||
isPrivate: this.isPrivate,
|
|
||||||
thumb: reader.result,
|
|
||||||
blobSignedId: blob ? blob.signed_id : undefined,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
},
|
|
||||||
removeAttachment(itemIndex) {
|
|
||||||
this.attachedFiles = this.attachedFiles.filter(
|
|
||||||
(item, index) => itemIndex !== index
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getMessagePayloadForWhatsapp(message) {
|
getMessagePayloadForWhatsapp(message) {
|
||||||
const multipleMessagePayload = [];
|
const multipleMessagePayload = [];
|
||||||
|
|
||||||
@@ -1015,51 +923,6 @@ export default {
|
|||||||
|
|
||||||
return messagePayload;
|
return messagePayload;
|
||||||
},
|
},
|
||||||
setCcEmails(value) {
|
|
||||||
this.bccEmails = value.bccEmails;
|
|
||||||
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 = [];
|
|
||||||
|
|
||||||
// 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.bccEmails = bcc.join(', ');
|
|
||||||
this.toEmails = to.join(', ');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -1077,13 +940,6 @@ export default {
|
|||||||
@apply bg-transparent py-0 px-4;
|
@apply bg-transparent py-0 px-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-box {
|
|
||||||
@apply border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
|
|
||||||
|
|
||||||
&.is-private {
|
|
||||||
@apply bg-yellow-50 dark:bg-yellow-200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.send-button {
|
.send-button {
|
||||||
@apply mb-0;
|
@apply mb-0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachedFiles: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasAttachments() {
|
||||||
|
return this.attachedFiles.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
attachFile({ blob, file }) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file.file);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
this.attachedFiles.push({
|
||||||
|
currentChatId: this.currentChat.id,
|
||||||
|
resource: blob || file,
|
||||||
|
isPrivate: this.isPrivate,
|
||||||
|
thumb: reader.result,
|
||||||
|
blobSignedId: blob ? blob.signed_id : undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
removeAttachment(itemIndex) {
|
||||||
|
this.attachedFiles = this.attachedFiles.filter(
|
||||||
|
(item, index) => itemIndex !== index
|
||||||
|
);
|
||||||
|
},
|
||||||
|
clearAttachments() {
|
||||||
|
this.attachedFiles = [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isRecordingAudio: false,
|
||||||
|
isRecorderAudioStopped: false,
|
||||||
|
recordingAudioState: '',
|
||||||
|
recordingAudioDurationText: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
audioRecordFormat() {
|
||||||
|
if (this.isAWhatsAppChannel || this.isAPIInbox) {
|
||||||
|
return AUDIO_FORMATS.OGG;
|
||||||
|
}
|
||||||
|
return AUDIO_FORMATS.WAV;
|
||||||
|
},
|
||||||
|
showAudioRecorder() {
|
||||||
|
return !this.isOnPrivateNote && this.showFileUpload;
|
||||||
|
},
|
||||||
|
showAudioRecorderEditor() {
|
||||||
|
return this.showAudioRecorder && this.isRecordingAudio;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleAudioRecorder() {
|
||||||
|
this.isRecordingAudio = !this.isRecordingAudio;
|
||||||
|
this.isRecorderAudioStopped = !this.isRecordingAudio;
|
||||||
|
if (!this.isRecordingAudio) {
|
||||||
|
this.clearMessage();
|
||||||
|
this.clearEmailField();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleAudioRecorderPlayPause() {
|
||||||
|
if (this.isRecordingAudio) {
|
||||||
|
if (!this.isRecorderAudioStopped) {
|
||||||
|
this.isRecorderAudioStopped = true;
|
||||||
|
this.$refs.audioRecorderInput.stopAudioRecording();
|
||||||
|
} else if (this.isRecorderAudioStopped) {
|
||||||
|
this.$refs.audioRecorderInput.playPause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStateProgressRecorderChanged(duration) {
|
||||||
|
this.recordingAudioDurationText = duration;
|
||||||
|
},
|
||||||
|
onStateRecorderChanged(state) {
|
||||||
|
this.recordingAudioState = state;
|
||||||
|
if (state && 'notallowederror'.includes(state)) {
|
||||||
|
this.toggleAudioRecorder();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFinishRecorder(file) {
|
||||||
|
return file && this.onFileUpload(file);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// emailHandlingMixin.js
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ccEmails: '',
|
||||||
|
bccEmails: '',
|
||||||
|
toEmails: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setCcEmails(value) {
|
||||||
|
this.bccEmails = value.bccEmails;
|
||||||
|
this.ccEmails = value.ccEmails;
|
||||||
|
},
|
||||||
|
setCCAndToEmailsFromLastChat() {
|
||||||
|
if (!this.lastEmail) return;
|
||||||
|
|
||||||
|
const {
|
||||||
|
content_attributes: { email: emailAttributes = {} },
|
||||||
|
} = this.lastEmail;
|
||||||
|
|
||||||
|
const conversationContact = this.currentChat?.meta?.sender?.email || '';
|
||||||
|
let cc = emailAttributes.cc ? [...emailAttributes.cc] : [];
|
||||||
|
let to = [];
|
||||||
|
|
||||||
|
if (cc.includes(conversationContact)) {
|
||||||
|
cc = cc.filter(email => email !== conversationContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailAttributes.from.includes(conversationContact)) {
|
||||||
|
to.push(...emailAttributes.from);
|
||||||
|
cc.push(conversationContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bcc = (emailAttributes.bcc || []).filter(
|
||||||
|
email => email !== conversationContact
|
||||||
|
);
|
||||||
|
|
||||||
|
bcc = [...new Set(bcc)];
|
||||||
|
cc = [...new Set(cc)];
|
||||||
|
to = [...new Set(to)];
|
||||||
|
|
||||||
|
this.ccEmails = cc.join(', ');
|
||||||
|
this.bccEmails = bcc.join(', ');
|
||||||
|
this.toEmails = to.join(', ');
|
||||||
|
},
|
||||||
|
clearEmailField() {
|
||||||
|
this.ccEmails = '';
|
||||||
|
this.bccEmails = '';
|
||||||
|
this.toEmails = '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { trimContent } from '@chatwoot/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
getFromDraft() {
|
||||||
|
if (this.conversationIdByRoute) {
|
||||||
|
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||||
|
const messageFromStore =
|
||||||
|
this.$store.getters['draftMessages/get'](key) || '';
|
||||||
|
|
||||||
|
// ensure that the message has signature set based on the ui setting
|
||||||
|
this.message = this.toggleSignatureForDraft(messageFromStore);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeFromDraft() {
|
||||||
|
if (this.conversationIdByRoute) {
|
||||||
|
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||||
|
this.$store.dispatch('draftMessages/delete', { key });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveDraft(conversationId, replyType) {
|
||||||
|
if (this.message || this.message === '') {
|
||||||
|
const key = `draft-${conversationId}-${replyType}`;
|
||||||
|
const draftToSave = trimContent(this.message || '');
|
||||||
|
|
||||||
|
this.$store.dispatch('draftMessages/set', {
|
||||||
|
key,
|
||||||
|
message: draftToSave,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setToDraft(conversationId, replyType) {
|
||||||
|
this.saveDraft(conversationId, replyType);
|
||||||
|
this.message = '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user