2023-10-27 12:52:06 +02:00

112 lines
3.7 KiB
TypeScript

import type { StateHookSetter } from '../../../../lib/teact/teact';
import { useEffect } from '../../../../lib/teact/teact';
import type { ApiAttachment, ApiFormattedText, ApiMessage } from '../../../../api/types';
import {
EDITABLE_INPUT_ID, EDITABLE_INPUT_MODAL_ID, EDITABLE_STORY_INPUT_ID,
} from '../../../../config';
import { containsCustomEmoji, stripCustomEmoji } from '../../../../global/helpers/symbols';
import parseMessageInput from '../../../../util/parseMessageInput';
import buildAttachment from '../helpers/buildAttachment';
import { preparePastedHtml } from '../helpers/cleanHtml';
import getFilesFromDataTransferItems from '../helpers/getFilesFromDataTransferItems';
const MAX_MESSAGE_LENGTH = 4096;
const TYPE_HTML = 'text/html';
const DOCUMENT_TYPE_WORD = 'urn:schemas-microsoft-com:office:word';
const NAMESPACE_PREFIX_WORD = 'xmlns:w';
const useClipboardPaste = (
isActive: boolean,
insertTextAndUpdateCursor: (text: ApiFormattedText, inputId?: string) => void,
setAttachments: StateHookSetter<ApiAttachment[]>,
setNextText: StateHookSetter<ApiFormattedText | undefined>,
editedMessage: ApiMessage | undefined,
shouldStripCustomEmoji?: boolean,
onCustomEmojiStripped?: VoidFunction,
) => {
useEffect(() => {
if (!isActive) {
return undefined;
}
async function handlePaste(e: ClipboardEvent) {
if (!e.clipboardData) {
return;
}
const input = document.activeElement;
if (input && ![EDITABLE_INPUT_ID, EDITABLE_INPUT_MODAL_ID, EDITABLE_STORY_INPUT_ID].includes(input.id)) {
return;
}
const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH);
const html = e.clipboardData.getData('text/html');
let pastedFormattedText = html ? parseMessageInput(
preparePastedHtml(html), undefined, true,
) : undefined;
if (pastedFormattedText && containsCustomEmoji(pastedFormattedText) && shouldStripCustomEmoji) {
pastedFormattedText = stripCustomEmoji(pastedFormattedText);
onCustomEmojiStripped?.();
}
const { items } = e.clipboardData;
let files: File[] | undefined = [];
e.preventDefault();
if (items.length > 0) {
files = await getFilesFromDataTransferItems(items);
}
if (!files?.length && !pastedText) {
return;
}
const textToPaste = pastedFormattedText?.entities?.length ? pastedFormattedText : { text: pastedText };
let isWordDocument = false;
try {
const parser = new DOMParser();
const parsedDocument = parser.parseFromString(html, TYPE_HTML);
isWordDocument = parsedDocument.documentElement
.getAttribute(NAMESPACE_PREFIX_WORD) === DOCUMENT_TYPE_WORD;
} catch (err: any) {
// Ignore
}
const hasText = textToPaste && textToPaste.text;
const shouldSetAttachments = files?.length && !editedMessage && !isWordDocument;
if (shouldSetAttachments) {
const newAttachments = await Promise.all(files!.map((file) => {
return buildAttachment(file.name, file);
}));
setAttachments((attachments) => attachments.concat(newAttachments));
}
if (hasText) {
if (shouldSetAttachments) {
setNextText(textToPaste);
} else {
insertTextAndUpdateCursor(textToPaste, input?.id);
}
}
}
document.addEventListener('paste', handlePaste, false);
return () => {
document.removeEventListener('paste', handlePaste, false);
};
}, [
insertTextAndUpdateCursor, editedMessage, setAttachments, isActive, shouldStripCustomEmoji, onCustomEmojiStripped,
setNextText,
]);
};
export default useClipboardPaste;