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:
Muhsin Keloth
2023-01-24 13:06:50 +05:30
committed by GitHub
parent cab409f3ef
commit d9a1154977
13 changed files with 479 additions and 31 deletions

View File

@@ -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);