From 27fc24375d6ecde6e7186e4c8f2920808b09bbf2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 21 Sep 2023 11:28:29 +0530 Subject: [PATCH] fix: Handling markdown before replacing or appending signature [CW-2532] (#7944) Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- .../dashboard/helper/editorHelper.js | 27 +++++++------------ .../helper/specs/editorHelper.spec.js | 22 ++++++++++++--- .../shared/components/ResizableTextArea.vue | 10 ++++--- package.json | 2 +- yarn.lock | 7 ++--- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/app/javascript/dashboard/helper/editorHelper.js b/app/javascript/dashboard/helper/editorHelper.js index 52f87c214..795c3af1b 100644 --- a/app/javascript/dashboard/helper/editorHelper.js +++ b/app/javascript/dashboard/helper/editorHelper.js @@ -1,3 +1,9 @@ +import { + messageSchema, + MessageMarkdownTransformer, + MessageMarkdownSerializer, +} from '@chatwoot/prosemirror-schema'; + /** * The delimiter used to separate the signature from the rest of the body. * @type {string} @@ -5,25 +11,12 @@ export const SIGNATURE_DELIMITER = '--'; /** - * Remove trailing spaces from each line in a markdown text - * @param {string} markdownText - * @returns - */ -function removeTrailingSpaces(markdownText) { - return markdownText - .split('\n') - .map(line => line.replace(/\s+$/, '')) - .join('\n'); -} - -/** - * Trim the signature and remove all " \r" from the signature - * 1. Trim any extra lines or spaces at the start or end of the string - * 2. Converts all \r or \r\n to \f + * Parse and Serialize the markdown text to remove any extra spaces or new lines */ export function cleanSignature(signature) { - const cleaned = signature.trim().replace(/\r\n?/g, '\n'); - return removeTrailingSpaces(cleaned); + // convert from markdown to common mark format + const nodes = new MessageMarkdownTransformer(messageSchema).parse(signature); + return MessageMarkdownSerializer.serialize(nodes); } /** diff --git a/app/javascript/dashboard/helper/specs/editorHelper.spec.js b/app/javascript/dashboard/helper/specs/editorHelper.spec.js index cb34f8957..159437e58 100644 --- a/app/javascript/dashboard/helper/specs/editorHelper.spec.js +++ b/app/javascript/dashboard/helper/specs/editorHelper.spec.js @@ -3,6 +3,7 @@ import { appendSignature, removeSignature, replaceSignature, + cleanSignature, extractTextFromMarkdown, } from '../editorHelper'; @@ -17,11 +18,19 @@ const DOES_NOT_HAVE_SIGNATURE = { body: 'This is a test\n\n--\n\nThis is a signature\n\nThis is more text', signature: 'This is a signature', }, - signature_has_images: { + 'signature has images': { body: 'This is a test', signature: 'Testing\n![](http://localhost:3000/rails/active_storage/blobs/redirect/some-hash/image.png)', }, + 'signature has non commonmark syntax': { + body: 'This is a test', + signature: '- Signature', + }, + 'signature has trailing spaces': { + body: 'This is a test', + signature: '**hello** \n**world**', + }, }; const HAS_SIGNATURE = { @@ -37,6 +46,10 @@ const HAS_SIGNATURE = { body: '\n\n--\n\nThis is a signature', signature: 'This is a signature', }, + 'signature has non-commonmark syntax': { + body: '\n\n--\n\n* Signature', + signature: '- Signature', + }, }; describe('findSignatureInBody', () => { @@ -58,9 +71,10 @@ describe('appendSignature', () => { it('appends the signature if it is not present', () => { Object.keys(DOES_NOT_HAVE_SIGNATURE).forEach(key => { const { body, signature } = DOES_NOT_HAVE_SIGNATURE[key]; - expect(appendSignature(body, signature)).toBe( - `${body}\n\n--\n\n${signature}` - ); + const cleanedSignature = cleanSignature(signature); + expect( + appendSignature(body, signature).includes(cleanedSignature) + ).toBeTruthy(); }); }); it('does not append signature if already present', () => { diff --git a/app/javascript/shared/components/ResizableTextArea.vue b/app/javascript/shared/components/ResizableTextArea.vue index bc7c89dc0..c86e4069e 100644 --- a/app/javascript/shared/components/ResizableTextArea.vue +++ b/app/javascript/shared/components/ResizableTextArea.vue @@ -113,7 +113,6 @@ export default { }); }, setCursor() { - const textarea = this.$refs.textarea; const bodyWithoutSignature = removeSignature( this.value, this.cleanedSignature @@ -121,9 +120,12 @@ export default { // only trim at end, so if there are spaces at the start, those are not removed const bodyEndsAt = bodyWithoutSignature.trimEnd().length; + const textarea = this.$refs.textarea; - textarea.focus(); - textarea.setSelectionRange(bodyEndsAt, bodyEndsAt); + if (textarea) { + textarea.focus(); + textarea.setSelectionRange(bodyEndsAt, bodyEndsAt); + } }, onInput(event) { this.$emit('input', event.target.value); @@ -157,7 +159,7 @@ export default { this.$emit('focus'); }, focus() { - this.$refs.textarea.focus(); + if (this.$refs.textarea) this.$refs.textarea.focus(); }, }, }; diff --git a/package.json b/package.json index 2dbe79291..1905fbaa6 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ ], "dependencies": { "@braid/vue-formulate": "^2.5.2", - "@chatwoot/prosemirror-schema": "https://github.com/chatwoot/prosemirror-schema.git#825755b53f1ef4ae14fac2393b53a02e14ce21e0", + "@chatwoot/prosemirror-schema": "^1.0.1", "@chatwoot/utils": "^0.0.16", "@hcaptcha/vue-hcaptcha": "^0.3.2", "@june-so/analytics-next": "^1.36.5", diff --git a/yarn.lock b/yarn.lock index 272c61f2c..3de256ffa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2994,9 +2994,10 @@ is-url "^1.2.4" nanoid "^2.1.11" -"@chatwoot/prosemirror-schema@https://github.com/chatwoot/prosemirror-schema.git#825755b53f1ef4ae14fac2393b53a02e14ce21e0": - version "1.0.0" - resolved "https://github.com/chatwoot/prosemirror-schema.git#825755b53f1ef4ae14fac2393b53a02e14ce21e0" +"@chatwoot/prosemirror-schema@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chatwoot/prosemirror-schema/-/prosemirror-schema-1.0.1.tgz#7beb7a9303fbf5281d3a7e6c470ac78cce21be04" + integrity sha512-fnT2zSzAmiAh1ElEWqBhISavGZF73DLUzQyml0iiXDy6IU7HS3TtQPI/AGoCK3CkCxvoqBtzhRLGZrHsftoQ9w== dependencies: markdown-it-sup "^1.0.0" prosemirror-commands "^1.1.4"