feat: Support variables in canned response (#6077)
- Added the option to insert variables in canned responses. - Populate variables on selecting a canned response. - Show a warning if there are any undefined variables in the message before sending a message.
This commit is contained in:
@@ -6,10 +6,15 @@
|
||||
@click="insertMentionNode"
|
||||
/>
|
||||
<canned-response
|
||||
v-if="showCannedMenu && !isPrivate"
|
||||
v-if="shouldShowCannedResponses"
|
||||
:search-key="cannedSearchTerm"
|
||||
@click="insertCannedResponse"
|
||||
/>
|
||||
<variable-list
|
||||
v-if="shouldShowVariables"
|
||||
:search-key="variableSearchTerm"
|
||||
@click="insertVariable"
|
||||
/>
|
||||
<div ref="editor" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -31,6 +36,7 @@ import {
|
||||
|
||||
import TagAgents from '../conversation/TagAgents';
|
||||
import CannedResponse from '../conversation/CannedResponse';
|
||||
import VariableList from '../conversation/VariableList';
|
||||
|
||||
const TYPING_INDICATOR_IDLE_TIME = 4000;
|
||||
|
||||
@@ -43,6 +49,7 @@ import {
|
||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||
import { replaceVariablesInMessage } from 'dashboard/helper/messageHelper';
|
||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
|
||||
const createState = (content, placeholder, plugins = []) => {
|
||||
@@ -58,7 +65,7 @@ const createState = (content, placeholder, plugins = []) => {
|
||||
|
||||
export default {
|
||||
name: 'WootMessageEditor',
|
||||
components: { TagAgents, CannedResponse },
|
||||
components: { TagAgents, CannedResponse, VariableList },
|
||||
mixins: [eventListenerMixins, uiSettingsMixin],
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
@@ -68,13 +75,18 @@ export default {
|
||||
enableSuggestions: { type: Boolean, default: true },
|
||||
overrideLineBreaks: { type: Boolean, default: false },
|
||||
updateSelectionWith: { type: String, default: '' },
|
||||
enableVariables: { type: Boolean, default: false },
|
||||
enableCannedResponses: { type: Boolean, default: true },
|
||||
variables: { type: Object, default: () => ({}) },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showUserMentions: false,
|
||||
showCannedMenu: false,
|
||||
showVariables: false,
|
||||
mentionSearchKey: '',
|
||||
cannedSearchTerm: '',
|
||||
variableSearchTerm: '',
|
||||
editorView: null,
|
||||
range: null,
|
||||
state: undefined,
|
||||
@@ -84,6 +96,14 @@ export default {
|
||||
contentFromEditor() {
|
||||
return MessageMarkdownSerializer.serialize(this.editorView.state.doc);
|
||||
},
|
||||
shouldShowVariables() {
|
||||
return this.enableVariables && this.showVariables && !this.isPrivate;
|
||||
},
|
||||
shouldShowCannedResponses() {
|
||||
return (
|
||||
this.enableCannedResponses && this.showCannedMenu && !this.isPrivate
|
||||
);
|
||||
},
|
||||
plugins() {
|
||||
if (!this.enableSuggestions) {
|
||||
return [];
|
||||
@@ -103,6 +123,7 @@ export default {
|
||||
this.range = args.range;
|
||||
|
||||
this.mentionSearchKey = args.text.replace('@', '');
|
||||
|
||||
return false;
|
||||
},
|
||||
onExit: () => {
|
||||
@@ -142,6 +163,34 @@ export default {
|
||||
return event.keyCode === 13 && this.showCannedMenu;
|
||||
},
|
||||
}),
|
||||
suggestionsPlugin({
|
||||
matcher: triggerCharacters('{{'),
|
||||
suggestionClass: '',
|
||||
onEnter: args => {
|
||||
if (this.isPrivate) {
|
||||
return false;
|
||||
}
|
||||
this.showVariables = true;
|
||||
this.range = args.range;
|
||||
this.editorView = args.view;
|
||||
return false;
|
||||
},
|
||||
onChange: args => {
|
||||
this.editorView = args.view;
|
||||
this.range = args.range;
|
||||
|
||||
this.variableSearchTerm = args.text.replace('{{', '');
|
||||
return false;
|
||||
},
|
||||
onExit: () => {
|
||||
this.variableSearchTerm = '';
|
||||
this.showVariables = false;
|
||||
return false;
|
||||
},
|
||||
onKeyDown: ({ event }) => {
|
||||
return event.keyCode === 13 && this.showVariables;
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
},
|
||||
@@ -152,6 +201,9 @@ export default {
|
||||
showCannedMenu(updatedValue) {
|
||||
this.$emit('toggle-canned-menu', !this.isPrivate && updatedValue);
|
||||
},
|
||||
showVariables(updatedValue) {
|
||||
this.$emit('toggle-variables-menu', !this.isPrivate && updatedValue);
|
||||
},
|
||||
value(newValue = '') {
|
||||
if (newValue !== this.contentFromEditor) {
|
||||
this.reloadState();
|
||||
@@ -267,19 +319,22 @@ export default {
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
insertCannedResponse(cannedItem) {
|
||||
const updatedMessage = replaceVariablesInMessage({
|
||||
message: cannedItem,
|
||||
variables: this.variables,
|
||||
});
|
||||
if (!this.editorView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let from = this.range.from - 1;
|
||||
let node = new MessageMarkdownTransformer(messageSchema).parse(
|
||||
cannedItem
|
||||
updatedMessage
|
||||
);
|
||||
|
||||
if (node.textContent === cannedItem) {
|
||||
node = this.editorView.state.schema.text(cannedItem);
|
||||
if (node.textContent === updatedMessage) {
|
||||
node = this.editorView.state.schema.text(updatedMessage);
|
||||
from = this.range.from;
|
||||
}
|
||||
|
||||
@@ -296,6 +351,29 @@ export default {
|
||||
this.$track(CONVERSATION_EVENTS.INSERTED_A_CANNED_RESPONSE);
|
||||
return false;
|
||||
},
|
||||
insertVariable(variable) {
|
||||
if (!this.editorView) {
|
||||
return null;
|
||||
}
|
||||
let node = this.editorView.state.schema.text(`{{${variable}}}`);
|
||||
const from = this.range.from;
|
||||
|
||||
const tr = this.editorView.state.tr.replaceWith(
|
||||
from,
|
||||
this.range.to,
|
||||
node
|
||||
);
|
||||
|
||||
this.state = this.editorView.state.apply(tr);
|
||||
this.emitOnChange();
|
||||
|
||||
// The `{{ }}` are added to the message, but the cursor is placed
|
||||
// and onExit of suggestionsPlugin is not called. So we need to manually hide
|
||||
this.showVariables = false;
|
||||
this.$track(CONVERSATION_EVENTS.INSERTED_A_VARIABLE);
|
||||
tr.scrollIntoView();
|
||||
return false;
|
||||
},
|
||||
|
||||
emitOnChange() {
|
||||
this.editorView.updateState(this.state);
|
||||
|
||||
Reference in New Issue
Block a user