From e2627ea0ffc25eb27ae2dc9d322d2e4edb949191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 28 Mar 2024 14:18:09 +0100 Subject: [PATCH 1/5] Fix pasting text into main composer --- src/hooks/useHtmlPaste/index.ts | 35 ++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index bdd13c2ceff8..426db02e2af7 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -6,13 +6,46 @@ import type UseHtmlPaste from './types'; const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { const navigation = useNavigation(); + const insertByCommand = (text: string) => { + document.execCommand('insertText', false, text); + }; + + function insertAtCaret(text: string) { + const selection = window.getSelection(); + if (selection?.rangeCount) { + const range = selection.getRangeAt(0); + range.deleteContents(); + const node = document.createTextNode(text); + range.insertNode(node); + + // Move caret to the end of the newly inserted text node. + range.setStart(node, node.length); + range.setEnd(node, node.length); + selection.removeAllRanges(); + selection.addRange(range); + + // dispatch input event to trigger Markdown Input to parser text + (textInputRef.current as HTMLElement)?.dispatchEvent( + new Event('input', { + bubbles: true, + }), + ); + } else { + insertByCommand(text); + } + } + /** * Set pasted text to clipboard * @param {String} text */ const paste = useCallback((text: string) => { try { - document.execCommand('insertText', false, text); + if ((textInputRef.current as HTMLElement)?.hasAttribute('contenteditable')) { + insertAtCaret(text); + } else { + insertByCommand(text); + } // Pointer will go out of sight when a large paragraph is pasted on the web. Refocusing the input keeps the cursor in view. textInputRef.current?.blur(); From 83d34a5291c643db5225b5d530677f76ff9f508b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 28 Mar 2024 14:39:33 +0100 Subject: [PATCH 2/5] Correct comment --- src/hooks/useHtmlPaste/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 426db02e2af7..80b58a7adc02 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -24,7 +24,7 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi selection.removeAllRanges(); selection.addRange(range); - // dispatch input event to trigger Markdown Input to parser text + // Dispatch input event to trigger Markdown Input to parse the new text (textInputRef.current as HTMLElement)?.dispatchEvent( new Event('input', { bubbles: true, From 810af19ade08518a4362da87a2313489466c6108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 28 Mar 2024 14:46:30 +0100 Subject: [PATCH 3/5] Move functions ouside the hook --- src/hooks/useHtmlPaste/index.ts | 63 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 80b58a7adc02..287c5b44e0ef 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -3,37 +3,37 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useCallback, useEffect} from 'react'; import type UseHtmlPaste from './types'; -const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { - const navigation = useNavigation(); +const insertByCommand = (text: string) => { + document.execCommand('insertText', false, text); +}; - const insertByCommand = (text: string) => { - document.execCommand('insertText', false, text); - }; - - function insertAtCaret(text: string) { - const selection = window.getSelection(); - if (selection?.rangeCount) { - const range = selection.getRangeAt(0); - range.deleteContents(); - const node = document.createTextNode(text); - range.insertNode(node); - - // Move caret to the end of the newly inserted text node. - range.setStart(node, node.length); - range.setEnd(node, node.length); - selection.removeAllRanges(); - selection.addRange(range); - - // Dispatch input event to trigger Markdown Input to parse the new text - (textInputRef.current as HTMLElement)?.dispatchEvent( - new Event('input', { - bubbles: true, - }), - ); - } else { - insertByCommand(text); - } +function insertAtCaret(target: HTMLElement, text: string) { + const selection = window.getSelection(); + if (selection?.rangeCount) { + const range = selection.getRangeAt(0); + range.deleteContents(); + const node = document.createTextNode(text); + range.insertNode(node); + + // Move caret to the end of the newly inserted text node. + range.setStart(node, node.length); + range.setEnd(node, node.length); + selection.removeAllRanges(); + selection.addRange(range); + + // Dispatch input event to trigger Markdown Input to parse the new text + target.dispatchEvent( + new Event('input', { + bubbles: true, + }), + ); + } else { + insertByCommand(text); } +} + +const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { + const navigation = useNavigation(); /** * Set pasted text to clipboard @@ -41,8 +41,9 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi */ const paste = useCallback((text: string) => { try { - if ((textInputRef.current as HTMLElement)?.hasAttribute('contenteditable')) { - insertAtCaret(text); + const textInputHTMLElement = textInputRef.current as HTMLElement; + if (textInputHTMLElement?.hasAttribute('contenteditable')) { + insertAtCaret(textInputHTMLElement, text); } else { insertByCommand(text); } From 966c865fa9cd1a9af4731b6352d47001aadb8f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 28 Mar 2024 14:49:17 +0100 Subject: [PATCH 4/5] Change insertAtCaret to arrow function --- src/hooks/useHtmlPaste/index.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 287c5b44e0ef..40e5522ec466 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -7,7 +7,7 @@ const insertByCommand = (text: string) => { document.execCommand('insertText', false, text); }; -function insertAtCaret(target: HTMLElement, text: string) { +const insertAtCaret = (target: HTMLElement, text: string) => { const selection = window.getSelection(); if (selection?.rangeCount) { const range = selection.getRangeAt(0); @@ -22,15 +22,11 @@ function insertAtCaret(target: HTMLElement, text: string) { selection.addRange(range); // Dispatch input event to trigger Markdown Input to parse the new text - target.dispatchEvent( - new Event('input', { - bubbles: true, - }), - ); + target.dispatchEvent(new Event('input', {bubbles: true})); } else { insertByCommand(text); } -} +}; const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { const navigation = useNavigation(); From e6c9f06a0952434f3f8ff9ace267c0e3ca026e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 4 Apr 2024 13:31:59 +0200 Subject: [PATCH 5/5] Add paste event dispatching --- src/hooks/useHtmlPaste/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 40e5522ec466..925a3db518ae 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -21,6 +21,8 @@ const insertAtCaret = (target: HTMLElement, text: string) => { selection.removeAllRanges(); selection.addRange(range); + // Dispatch paste event to simulate real browser behavior + target.dispatchEvent(new Event('paste', {bubbles: true})); // Dispatch input event to trigger Markdown Input to parse the new text target.dispatchEvent(new Event('input', {bubbles: true})); } else {