From f9d52e300ed6ada6015294f1d26ba23ce35e60b9 Mon Sep 17 00:00:00 2001 From: Jack Nam <30609178+thienlnam@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:25:40 -0700 Subject: [PATCH] Merge pull request #39177 from software-mansion-labs/@Skalakid/fix-pasting-to-composer Fix pasting text into main composer (cherry picked from commit 93e9719a7adba038f62aa1217aab6633e02e0724) --- src/hooks/useHtmlPaste/index.ts | 34 ++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index bdd13c2ceff8..925a3db518ae 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -3,6 +3,33 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useCallback, useEffect} from 'react'; import type UseHtmlPaste from './types'; +const insertByCommand = (text: string) => { + document.execCommand('insertText', false, text); +}; + +const 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 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 { + insertByCommand(text); + } +}; + const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { const navigation = useNavigation(); @@ -12,7 +39,12 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi */ const paste = useCallback((text: string) => { try { - document.execCommand('insertText', false, text); + const textInputHTMLElement = textInputRef.current as HTMLElement; + if (textInputHTMLElement?.hasAttribute('contenteditable')) { + insertAtCaret(textInputHTMLElement, 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();