FEATURE: Allow users to paste animated gifs into composer (#16204)

If a user copies a gif from a website into their clipboard and then
tries to paste it into the Discourse composer, we would only paste a
static single frame of the original gif. This happens because the
browser doesn't store the original image in the clipboard, but two
entries:

1. image/png with the frame of the copy moment
2. text/html with the markup of the gif img element

This commit adds an heuristic that detects this and makes us pick the
clipboard content of text/html instead of the image/png when this
happens.

From there our existing HTML paste logic handles and converts the HTML
img tag into markdown, preserving even the alt text.

See https://meta.discourse.org/t/-/218720 for context.
This commit is contained in:
Rafael dos Santos Silva 2022-03-17 10:47:54 -03:00 committed by GitHub
parent 3b8ff1184f
commit d678ba1103
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 16 additions and 3 deletions

View File

@ -349,6 +349,15 @@ const toArray = (items) => {
return items;
};
const gifInDisguise = (clipboard) => {
return (
clipboard.files.length === 1 &&
clipboard.files[0].type === "image/png" &&
clipboard.types.every((e) => ["text/html", "Files"].includes(e)) &&
/<img.*src=.*\.gif/.test(clipboard.getData("text/html"))
);
};
export function clipboardHelpers(e, opts) {
const clipboard =
e.clipboardData ||
@ -365,7 +374,9 @@ export function clipboardHelpers(e, opts) {
let canUpload = files && opts.canUpload && types.includes("Files");
const canUploadImage =
canUpload && files.filter((f) => f.type.match("^image/"))[0];
canUpload &&
files.filter((f) => f.type.match("^image/"))[0] &&
!gifInDisguise(clipboard);
const canPasteHtml =
opts.siteSettings.enable_rich_text_paste &&
types.includes("text/html") &&

View File

@ -426,11 +426,13 @@ export default Mixin.create({
@bind
paste(e) {
if (!this._$textarea.is(":focus") && !isTesting()) {
const isComposer =
document.querySelector(this.composerFocusSelector) === e.target;
if (!isComposer && !isTesting()) {
return;
}
const isComposer = $(this.composerFocusSelector).is(":focus");
let { clipboard, canPasteHtml, canUpload } = clipboardHelpers(e, {
siteSettings: this.siteSettings,
canUpload: isComposer,