fix: Avoid editor formatting issues when a canned response is edited (#5533)

This commit is contained in:
Nithin David Thomas 2022-10-01 03:33:33 +05:30 committed by GitHub
parent 7b54990ae6
commit 705d06ac3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 57 deletions

View file

@ -28,7 +28,7 @@ import {
suggestionsPlugin, suggestionsPlugin,
triggerCharacters, triggerCharacters,
} from '@chatwoot/prosemirror-schema/src/mentions/plugin'; } from '@chatwoot/prosemirror-schema/src/mentions/plugin';
import { EditorState } from 'prosemirror-state'; import { EditorState, Selection } from 'prosemirror-state';
import { defaultMarkdownParser } from 'prosemirror-markdown'; import { defaultMarkdownParser } from 'prosemirror-markdown';
import { wootWriterSetup } from '@chatwoot/prosemirror-schema'; import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
@ -61,23 +61,28 @@ export default {
mixins: [eventListenerMixins], mixins: [eventListenerMixins],
props: { props: {
value: { type: String, default: '' }, value: { type: String, default: '' },
editorId: { type: String, default: '' },
placeholder: { type: String, default: '' }, placeholder: { type: String, default: '' },
isPrivate: { type: Boolean, default: false }, isPrivate: { type: Boolean, default: false },
isFormatMode: { type: Boolean, default: false },
enableSuggestions: { type: Boolean, default: true }, enableSuggestions: { type: Boolean, default: true },
}, },
data() { data() {
return { return {
lastValue: null,
showUserMentions: false, showUserMentions: false,
showCannedMenu: false, showCannedMenu: false,
mentionSearchKey: '', mentionSearchKey: '',
cannedSearchTerm: '', cannedSearchTerm: '',
editorView: null, editorView: null,
range: null, range: null,
state: undefined,
}; };
}, },
computed: { computed: {
contentFromEditor() {
return addMentionsToMarkdownSerializer(
defaultMarkdownSerializer
).serialize(this.editorView.state.doc);
},
plugins() { plugins() {
if (!this.enableSuggestions) { if (!this.enableSuggestions) {
return []; return [];
@ -102,7 +107,6 @@ export default {
onExit: () => { onExit: () => {
this.mentionSearchKey = ''; this.mentionSearchKey = '';
this.showUserMentions = false; this.showUserMentions = false;
this.editorView = null;
return false; return false;
}, },
onKeyDown: ({ event }) => { onKeyDown: ({ event }) => {
@ -131,7 +135,6 @@ export default {
onExit: () => { onExit: () => {
this.cannedSearchTerm = ''; this.cannedSearchTerm = '';
this.showCannedMenu = false; this.showCannedMenu = false;
this.editorView = null;
return false; return false;
}, },
onKeyDown: ({ event }) => { onKeyDown: ({ event }) => {
@ -149,28 +152,33 @@ export default {
this.$emit('toggle-canned-menu', !this.isPrivate && updatedValue); this.$emit('toggle-canned-menu', !this.isPrivate && updatedValue);
}, },
value(newValue = '') { value(newValue = '') {
if (newValue !== this.lastValue) { if (newValue !== this.contentFromEditor) {
const { tr } = this.state; this.reloadState();
if (this.isFormatMode) {
this.state = createState(
newValue,
this.placeholder,
this.plugins,
this.isFormatMode
);
} else {
tr.insertText(newValue, 0, tr.doc.content.size);
this.state = this.view.state.apply(tr);
}
this.view.updateState(this.state);
} }
}, },
editorId() {
this.reloadState();
},
isPrivate() {
this.reloadState();
},
}, },
created() { created() {
this.state = createState(this.value, this.placeholder, this.plugins); this.state = createState(this.value, this.placeholder, this.plugins);
}, },
mounted() { mounted() {
this.view = new EditorView(this.$refs.editor, { this.createEditorView();
this.editorView.updateState(this.state);
this.focusEditorInputField();
},
methods: {
reloadState() {
this.state = createState(this.value, this.placeholder, this.plugins);
this.editorView.updateState(this.state);
this.focusEditorInputField();
},
createEditorView() {
this.editorView = new EditorView(this.$refs.editor, {
state: this.state, state: this.state,
dispatchTransaction: tx => { dispatchTransaction: tx => {
this.state = this.state.apply(tx); this.state = this.state.apply(tx);
@ -194,9 +202,7 @@ export default {
}, },
}, },
}); });
this.focusEditorInputField();
}, },
methods: {
handleKeyEvents(e) { handleKeyEvents(e) {
if (hasPressedAltAndPKey(e)) { if (hasPressedAltAndPKey(e)) {
this.focusEditorInputField(); this.focusEditorInputField();
@ -206,47 +212,59 @@ export default {
} }
}, },
focusEditorInputField() { focusEditorInputField() {
this.$refs.editor.querySelector('div.ProseMirror-woot-style').focus(); const { tr } = this.editorView.state;
const selection = Selection.atEnd(tr.doc);
this.editorView.dispatch(tr.setSelection(selection));
this.editorView.focus();
}, },
insertMentionNode(mentionItem) { insertMentionNode(mentionItem) {
if (!this.view) { if (!this.editorView) {
return null; return null;
} }
const node = this.view.state.schema.nodes.mention.create({ const node = this.editorView.state.schema.nodes.mention.create({
userId: mentionItem.key, userId: mentionItem.key,
userFullName: mentionItem.label, userFullName: mentionItem.label,
}); });
const tr = this.view.state.tr.replaceWith( const tr = this.editorView.state.tr.replaceWith(
this.range.from, this.range.from,
this.range.to, this.range.to,
node node
); );
this.state = this.view.state.apply(tr); this.state = this.editorView.state.apply(tr);
return this.emitOnChange(); return this.emitOnChange();
}, },
insertCannedResponse(cannedItem) { insertCannedResponse(cannedItem) {
if (!this.view) { if (!this.editorView) {
return null; return null;
} }
const tr = this.view.state.tr.insertText( const tr = this.editorView.state.tr.insertText(
cannedItem, cannedItem,
this.range.from, this.range.from,
this.range.to this.range.to
); );
this.state = this.view.state.apply(tr); this.state = this.editorView.state.apply(tr);
return this.emitOnChange(); this.emitOnChange();
// Hacky fix for #5501
this.state = createState(
this.contentFromEditor,
this.placeholder,
this.plugins
);
this.editorView.updateState(this.state);
return false;
}, },
emitOnChange() { emitOnChange() {
this.view.updateState(this.state); this.editorView.updateState(this.state);
this.lastValue = addMentionsToMarkdownSerializer(
defaultMarkdownSerializer this.$emit('input', this.contentFromEditor);
).serialize(this.state.doc);
this.$emit('input', this.lastValue);
}, },
hideMentions() { hideMentions() {
this.showUserMentions = false; this.showUserMentions = false;
}, },

View file

@ -56,6 +56,7 @@
<woot-message-editor <woot-message-editor
v-else v-else
v-model="message" v-model="message"
:editor-id="editorStateId"
class="input" class="input"
:is-private="isOnPrivateNote" :is-private="isOnPrivateNote"
:placeholder="messagePlaceHolder" :placeholder="messagePlaceHolder"
@ -429,6 +430,13 @@ export default {
profilePath() { profilePath() {
return frontendURL(`accounts/${this.accountId}/profile/settings`); return frontendURL(`accounts/${this.accountId}/profile/settings`);
}, },
conversationId() {
return this.currentChat.id;
},
editorStateId() {
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
return key;
},
}, },
watch: { watch: {
currentChat(conversation) { currentChat(conversation) {

View file

@ -1,6 +1,7 @@
export const LOCAL_STORAGE_KEYS = { export const LOCAL_STORAGE_KEYS = {
DISMISSED_UPDATES: 'dismissedUpdates', DISMISSED_UPDATES: 'dismissedUpdates',
WIDGET_BUILDER: 'widgetBubble_', WIDGET_BUILDER: 'widgetBubble_',
DRAFT_MESSAGES: 'draftMessages',
}; };
export const LocalStorage = { export const LocalStorage = {

View file

@ -46,6 +46,7 @@ export default {
return { return {
articleTitle: '', articleTitle: '',
articleContent: '', articleContent: '',
saveArticle: () => {},
}; };
}, },
mounted() { mounted() {