import type { FC } from '../../../lib/teact/teact'; import React, { memo, useEffect, useRef } from '../../../lib/teact/teact'; import { getActions } from '../../../global'; import type { ApiMessage } from '../../../api/types'; import { canReplaceMessageMedia } from '../../../global/helpers'; import buildClassName from '../../../util/buildClassName'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import buildAttachment from './helpers/buildAttachment'; import getFilesFromDataTransferItems from './helpers/getFilesFromDataTransferItems'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated'; import useShowTransitionDeprecated from '../../../hooks/useShowTransitionDeprecated'; import Portal from '../../ui/Portal'; import DropTarget from './DropTarget'; import './DropArea.scss'; export type OwnProps = { isOpen: boolean; withQuick?: boolean; onHide: NoneToVoidFunction; onFileSelect: (files: File[], suggestCompression?: boolean) => void; editingMessage?: ApiMessage | undefined; }; export enum DropAreaState { None = 'none', Document = 'document', QuickFile = 'quick_file', } const DROP_LEAVE_TIMEOUT_MS = 150; const DropArea: FC = ({ isOpen, withQuick, onHide, onFileSelect, editingMessage, }) => { const lang = useOldLang(); const { showNotification } = getActions(); // eslint-disable-next-line no-null/no-null const hideTimeoutRef = useRef(null); const prevWithQuick = usePreviousDeprecated(withQuick); const { shouldRender, transitionClassNames } = useShowTransitionDeprecated(isOpen); const isInAlbum = editingMessage && editingMessage?.groupedId; useEffect(() => (isOpen ? captureEscKeyListener(onHide) : undefined), [isOpen, onHide]); const handleFilesDrop = useLastCallback(async (e: React.DragEvent) => { const { dataTransfer: dt } = e; let files: File[] = []; if (dt.files && dt.files.length > 0 && (!dt.items || !dt.items.length)) { files = files.concat(Array.from(dt.files)); } else if (dt.items && dt.items.length > 0) { const folderFiles = await getFilesFromDataTransferItems(dt.items); const newAttachment = folderFiles && await buildAttachment(folderFiles[0].name, folderFiles[0]); const canReplace = editingMessage && newAttachment && canReplaceMessageMedia(editingMessage, newAttachment); if (canReplace) { showNotification({ message: lang(isInAlbum ? 'lng_edit_media_album_error' : 'lng_edit_media_invalid_file') }); return; } if (folderFiles?.length) { files = files.concat(folderFiles); } } onHide(); onFileSelect(files, withQuick ? false : undefined); }); const handleQuickFilesDrop = useLastCallback((e: React.DragEvent) => { const { dataTransfer: dt } = e; if (dt.files && dt.files.length > 0) { onHide(); onFileSelect(Array.from(dt.files), true); } }); const handleDragLeave = useLastCallback((e: React.DragEvent) => { e.stopPropagation(); const { target: fromTarget, relatedTarget: toTarget } = e; // Esc button pressed during drag event if ( (fromTarget as HTMLDivElement).matches('.DropTarget, .DropArea') && ( !toTarget || !(toTarget as HTMLDivElement)!.matches('.DropTarget, .DropArea') ) ) { hideTimeoutRef.current = window.setTimeout(() => { onHide(); }, DROP_LEAVE_TIMEOUT_MS); } }); const handleDragOver = () => { if (hideTimeoutRef.current) { window.clearTimeout(hideTimeoutRef.current); } }; if (!shouldRender) { return undefined; } const shouldRenderQuick = withQuick || prevWithQuick; const className = buildClassName( 'DropArea', transitionClassNames, ); return (
{shouldRenderQuick && }
); }; export default memo(DropArea);