Support Insert from iPhone or iPad in Safari (#10851)

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
许煜恒 2023-05-25 19:00:15 +08:00 committed by GitHub
parent f52fab39fc
commit 8abe392294
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 11 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ClipboardEvent, createRef, KeyboardEvent } from "react";
import React, { createRef, KeyboardEvent, SyntheticEvent } from "react";
import EMOJI_REGEX from "emojibase-regex";
import { IContent, MatrixEvent, IEventRelation, IMentions } from "matrix-js-sdk/src/models/event";
import { DebouncedFunc, throttle } from "lodash";
@ -61,6 +61,7 @@ import { addReplyToMessageContent } from "../../../utils/Reply";
import { doMaybeLocalRoomAction } from "../../../utils/local-room";
import { Caret } from "../../../editor/caret";
import { IDiff } from "../../../editor/diff";
import { getBlobSafeMimeType } from "../../../utils/blobs";
/**
* Build the mentions information based on the editor model (and any related events):
@ -667,15 +668,14 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
}
};
private onPaste = (event: ClipboardEvent<HTMLDivElement>): boolean => {
const { clipboardData } = event;
private onPaste = (event: Event | SyntheticEvent, data: DataTransfer): boolean => {
// Prioritize text on the clipboard over files if RTF is present as Office on macOS puts a bitmap
// in the clipboard as well as the content being copied. Modern versions of Office seem to not do this anymore.
// We check text/rtf instead of text/plain as when copy+pasting a file from Finder or Gnome Image Viewer
// it puts the filename in as text/plain which we want to ignore.
if (clipboardData.files.length && !clipboardData.types.includes("text/rtf")) {
if (data.files.length && !data.types.includes("text/rtf")) {
ContentMessages.sharedInstance().sendContentListToRoom(
Array.from(clipboardData.files),
Array.from(data.files),
this.props.room.roomId,
this.props.relation,
this.props.mxClient,
@ -684,6 +684,57 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
return true; // to skip internal onPaste handler
}
// Safari `Insert from iPhone or iPad`
// data.getData("text/html") returns a string like: <img src="blob:https://...">
if (data.types.includes("text/html")) {
const imgElementStr = data.getData("text/html");
const parser = new DOMParser();
const imgDoc = parser.parseFromString(imgElementStr, "text/html");
if (
imgDoc.getElementsByTagName("img").length !== 1 ||
!imgDoc.querySelector("img")?.src.startsWith("blob:") ||
imgDoc.childNodes.length !== 1
) {
console.log("Failed to handle pasted content as Safari inserted content");
// Fallback to internal onPaste handler
return false;
}
const imgSrc = imgDoc!.querySelector("img")!.src;
fetch(imgSrc).then(
(response) => {
response.blob().then(
(imgBlob) => {
const type = imgBlob.type;
const safetype = getBlobSafeMimeType(type);
const ext = type.split("/")[1];
const parts = response.url.split("/");
const filename = parts[parts.length - 1];
const file = new File([imgBlob], filename + "." + ext, { type: safetype });
ContentMessages.sharedInstance().sendContentToRoom(
file,
this.props.room.roomId,
this.props.relation,
this.props.mxClient,
this.context.replyToEvent,
);
},
(error) => {
console.log(error);
},
);
},
(error) => {
console.log(error);
},
);
// Skip internal onPaste handler
return true;
}
return false;
};