Skip to content

Ignore images from embedded html when pasting, paste plaintext instead. #21476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
61 changes: 14 additions & 47 deletions src/components/Composer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import _ from 'underscore';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import RNTextInput from '../RNTextInput';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import Growl from '../../libs/Growl';
import themeColors from '../../styles/themes/default';
import updateIsFullComposerAvailable from '../../libs/ComposerUtils/updateIsFullComposerAvailable';
import * as ComposerUtils from '../../libs/ComposerUtils';
Expand Down Expand Up @@ -112,16 +111,6 @@ const defaultProps = {
checkComposerVisibility: () => false,
};

const IMAGE_EXTENSIONS = {
'image/bmp': 'bmp',
'image/gif': 'gif',
'image/jpeg': 'jpg',
'image/png': 'png',
'image/svg+xml': 'svg',
'image/tiff': 'tiff',
'image/webp': 'webp',
};

/**
* Enable Markdown parsing.
* On web we like to have the Text Input field always focused so the user can easily type a new chat
Expand Down Expand Up @@ -300,6 +289,16 @@ class Composer extends React.Component {
this.paste(parser.htmlToMarkdown(html));
}

/**
* Paste the plaintext content into Composer.
*
* @param {ClipboardEvent} event
*/
handlePastePlainText(event) {
const plainText = event.clipboardData.getData('text/plain');
this.paste(plainText);
}

/**
* 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.
Expand Down Expand Up @@ -337,52 +336,20 @@ class Composer extends React.Component {
const domparser = new DOMParser();
const embeddedImages = domparser.parseFromString(pastedHTML, TEXT_HTML).images;

// If HTML has img tag, then fetch images from it.
// Exclude parsing img tags in the HTML, as fetching the image via fetch triggers a Content-Security-Policy error.
if (embeddedImages.length > 0 && embeddedImages[0].src) {
// If HTML has emoji, then treat this as plain text.
if (embeddedImages[0].dataset && embeddedImages[0].dataset.stringifyType === 'emoji') {
const plainText = event.clipboardData.getData('text/plain');
this.paste(plainText);
return;
this.handlePastePlainText(event);
return;
}
fetch(embeddedImages[0].src)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.blob();
})
.then((x) => {
const extension = IMAGE_EXTENSIONS[x.type];
if (!extension) {
throw new Error(this.props.translate('composer.noExtensionFoundForMimeType'));
}

return new File([x], `pasted_image.${extension}`, {});
})
.then(this.props.onPasteFile)
.catch(() => {
const errorDesc = this.props.translate('composer.problemGettingImageYouPasted');
Growl.error(errorDesc);

/*
* Since we intercepted the user-triggered paste event to check for attachments,
* we need to manually set the value and call the `onChangeText` handler.
* 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.
*/
this.handlePastedHTML(pastedHTML);
});
return;
}

this.handlePastedHTML(pastedHTML);
return;
}

const plainText = event.clipboardData.getData('text/plain');

this.paste(plainText);
this.handlePastePlainText(event);
}

/**
Expand Down