diff --git a/package-lock.json b/package-lock.json index 600f49592149..6c1b7f05ea79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23054,8 +23054,8 @@ } }, "expensify-common": { - "version": "git://github.com/Expensify/expensify-common.git#7cba6ef48c703b304021f917e4ed9943f186fc21", - "from": "git://github.com/Expensify/expensify-common.git#7cba6ef48c703b304021f917e4ed9943f186fc21", + "version": "git://github.com/Expensify/expensify-common.git#77b43a207e36a3aae646e38a16ef468ac488bbab", + "from": "git://github.com/Expensify/expensify-common.git#77b43a207e36a3aae646e38a16ef468ac488bbab", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", diff --git a/package.json b/package.json index af3426fd1759..c71aa0bf9a33 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "electron-log": "^4.3.5", "electron-serve": "^1.0.0", "electron-updater": "^4.3.4", - "expensify-common": "git://github.com/Expensify/expensify-common.git#7cba6ef48c703b304021f917e4ed9943f186fc21", + "expensify-common": "git://github.com/Expensify/expensify-common.git#77b43a207e36a3aae646e38a16ef468ac488bbab", "expo-haptics": "^10.0.0", "file-loader": "^6.0.0", "html-entities": "^1.3.1", diff --git a/src/components/TextInputFocusable/index.js b/src/components/TextInputFocusable/index.js index 2004ed232e28..1ee3da703a2b 100755 --- a/src/components/TextInputFocusable/index.js +++ b/src/components/TextInputFocusable/index.js @@ -2,6 +2,7 @@ import React from 'react'; import {TextInput, StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Growl from '../../libs/Growl'; import themeColors from '../../styles/themes/default'; @@ -120,6 +121,8 @@ class TextInputFocusable extends React.Component { }; this.saveSelection = this.saveSelection.bind(this); this.dragNDropListener = this.dragNDropListener.bind(this); + this.handlePaste = this.handlePaste.bind(this); + this.handlePastedHTML = this.handlePastedHTML.bind(this); } componentDidMount() { @@ -142,7 +145,7 @@ class TextInputFocusable extends React.Component { document.addEventListener('dragenter', this.dragNDropListener); document.addEventListener('dragleave', this.dragNDropListener); document.addEventListener('drop', this.dragNDropListener); - this.textInput.addEventListener('paste', this.checkForAttachment.bind(this)); + this.textInput.addEventListener('paste', this.handlePaste); } } @@ -169,7 +172,7 @@ class TextInputFocusable extends React.Component { document.removeEventListener('dragenter', this.dragNDropListener); document.removeEventListener('dragleave', this.dragNDropListener); document.removeEventListener('drop', this.dragNDropListener); - this.textInput.removeEventListener('paste', this.checkForAttachment.bind(this)); + this.textInput.removeEventListener('paste', this.handlePaste); } } @@ -241,26 +244,52 @@ class TextInputFocusable extends React.Component { } /** - * Check the paste event for an attachment, parse the data and - * call onPasteFile from props with the selected file + * Manually place the pasted HTML into Composer + * + * @param {String} html - pasted HTML + * @memberof TextInputFocusable + */ + handlePastedHTML(html) { + const parser = new ExpensiMark(); + const markdownText = parser.htmlToMarkdown(html); + const beforeCursorText = this.textInput.value.substring(0, this.selection.start); + const afterCursorText = this.textInput.value.substring(this.selection.end); + this.textInput.value = beforeCursorText + markdownText + afterCursorText; + const newCursorPosition = beforeCursorText.length + markdownText.length; + this.setState({selection: {start: newCursorPosition, end: newCursorPosition}}); + this.updateNumberOfLines(); + this.props.onChangeText(this.textInput.value); + } + + /** + * Check the paste event for an attachment, parse the data and call onPasteFile from props with the selected file, + * Otherwise, convert pasted HTML to Markdown and set it on the composer. * * @param {ClipboardEvent} event */ - checkForAttachment(event) { + handlePaste(event) { const {files, types} = event.clipboardData; const TEXT_HTML = 'text/html'; - const TEXT_PLAIN = 'text/plain'; + + // If paste contains files, then trigger file management if (files.length > 0) { // Prevent the default so we do not post the file name into the text box event.preventDefault(); this.props.onPasteFile(event.clipboardData.files[0]); - } else if (types.includes(TEXT_HTML)) { + return; + } + + // If paste contains HTML + if (types.includes(TEXT_HTML)) { + const pastedHTML = event.clipboardData.getData(TEXT_HTML); + + event.preventDefault(); const domparser = new DOMParser(); - const embededImages = domparser.parseFromString(event.clipboardData.getData(TEXT_HTML), TEXT_HTML).images; - const pastedText = event.clipboardData.getData(TEXT_PLAIN); - if (embededImages.length > 0) { - event.preventDefault(); - fetch(embededImages[0].src) + const embeddedImages = domparser.parseFromString(pastedHTML, TEXT_HTML).images; + + // If HTML has img tag, then fetch images from it. + if (embeddedImages.length > 0) { + fetch(embeddedImages[0].src) .then((response) => { if (!response.ok) { throw Error(response.statusText); } return response.blob(); @@ -284,15 +313,12 @@ class TextInputFocusable extends React.Component { * Synthetically-triggered paste events do not affect the document's contents. * See https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event for more details. */ - const beforeCursorText = this.textInput.value.substring(0, this.selection.start); - const afterCursorText = this.textInput.value.substring(this.selection.end); - this.textInput.value = beforeCursorText + pastedText + afterCursorText; - this.updateNumberOfLines(); - this.props.onChangeText(this.textInput.value); - const newCursorPosition = beforeCursorText.length + pastedText.length; - this.setState({selection: {start: newCursorPosition, end: newCursorPosition}}); + this.handlePastedHTML(pastedHTML); }); + return; } + + this.handlePastedHTML(pastedHTML); } } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a17ae01410bc..9606b457d1f9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1233,9 +1233,12 @@ NetworkConnection.onReconnect(fetchAllReports); * * @param {Number} reportID * @param {Object} originalReportAction - * @param {String} htmlForNewComment + * @param {String} textForNewComment */ -function editReportComment(reportID, originalReportAction, htmlForNewComment) { +function editReportComment(reportID, originalReportAction, textForNewComment) { + const parser = new ExpensiMark(); + const htmlForNewComment = parser.replace(textForNewComment); + // Skip the Edit if message is not changed if (originalReportAction.message[0].html === htmlForNewComment.trim()) { return;