From 3dd37f973732a68291e317f5797c43b4ec214bd5 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:41:40 +0200 Subject: [PATCH] Drop Area: Show error for multiple files (#5973) --- src/assets/localization/fallback.strings | 2 + src/components/middle/composer/DropArea.tsx | 48 ++++++++++++++----- .../composer/hooks/useAttachmentModal.ts | 8 ++-- .../composer/hooks/useClipboardPaste.ts | 17 +++++-- src/types/language.d.ts | 1 + 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 29b05a961..87a987612 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -1318,6 +1318,8 @@ "FileDropZoneTitle" = "Drop files here to send them"; "FileDropZoneQuick" = "in a quick way"; "FileDropZoneNoCompression" = "without compression"; +"MediaReplaceInvalidError_one" = "This media cannot be replaced with the selected file"; +"MediaReplaceInvalidError_other" = "This media cannot be replaced with the selected files"; "GifPickerBlocked" = "Sending GIFs is not allowed in this chat"; "GifPickerEmpty" = "No saved GIFs"; "AriaCancelPollCreation" = "Cancel poll creation"; diff --git a/src/components/middle/composer/DropArea.tsx b/src/components/middle/composer/DropArea.tsx index d32e5dca7..8aa9164f1 100644 --- a/src/components/middle/composer/DropArea.tsx +++ b/src/components/middle/composer/DropArea.tsx @@ -10,8 +10,8 @@ import captureEscKeyListener from '../../../util/captureEscKeyListener'; import buildAttachment from './helpers/buildAttachment'; import getFilesFromDataTransferItems from './helpers/getFilesFromDataTransferItems'; +import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; -import useOldLang from '../../../hooks/useOldLang'; import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated'; import useShowTransitionDeprecated from '../../../hooks/useShowTransitionDeprecated'; @@ -39,12 +39,11 @@ const DROP_LEAVE_TIMEOUT_MS = 150; const DropArea: FC = ({ isOpen, withQuick, onHide, onFileSelect, editingMessage, }) => { - const lang = useOldLang(); + const lang = useLang(); const { showNotification } = getActions(); const hideTimeoutRef = useRef(); const prevWithQuick = usePreviousDeprecated(withQuick); const { shouldRender, transitionClassNames } = useShowTransitionDeprecated(isOpen); - const isInAlbum = editingMessage && editingMessage?.groupedId; useEffect(() => (isOpen ? captureEscKeyListener(onHide) : undefined), [isOpen, onHide]); @@ -56,28 +55,53 @@ const DropArea: FC = ({ 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); } } + if (editingMessage) { + if (files.length > 1) { + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); + return; + } + + if (files.length === 1) { + const newAttachment = await buildAttachment(files[0].name, files[0]); + const canReplace = editingMessage && newAttachment && canReplaceMessageMedia(editingMessage, newAttachment); + if (!canReplace) { + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); + return; + } + } + } + onHide(); onFileSelect(files, withQuick ? false : undefined); }); - const handleQuickFilesDrop = useLastCallback((e: React.DragEvent) => { + const handleQuickFilesDrop = useLastCallback(async (e: React.DragEvent) => { const { dataTransfer: dt } = e; if (dt.files && dt.files.length > 0) { + const files = Array.from(dt.files); + if (editingMessage) { + if (files.length > 1) { + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); + return; + } + if (files.length === 1) { + const newAttachment = await buildAttachment(files[0].name, files[0]); + const canReplace = editingMessage && newAttachment && canReplaceMessageMedia(editingMessage, newAttachment); + if (!canReplace) { + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); + return; + } + } + } + onHide(); - onFileSelect(Array.from(dt.files), true); + onFileSelect(files, true); } }); diff --git a/src/components/middle/composer/hooks/useAttachmentModal.ts b/src/components/middle/composer/hooks/useAttachmentModal.ts index 83737d8d9..6f129201e 100644 --- a/src/components/middle/composer/hooks/useAttachmentModal.ts +++ b/src/components/middle/composer/hooks/useAttachmentModal.ts @@ -7,8 +7,8 @@ import { canReplaceMessageMedia, getAttachmentMediaType } from '../../../../glob import { MEMO_EMPTY_ARRAY } from '../../../../util/memo'; import buildAttachment from '../helpers/buildAttachment'; +import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; -import useOldLang from '../../../../hooks/useOldLang'; export default function useAttachmentModal({ attachments, @@ -35,7 +35,7 @@ export default function useAttachmentModal({ insertNextText: VoidFunction; editedMessage: ApiMessage | undefined; }) { - const lang = useOldLang(); + const lang = useLang(); const { openLimitReachedModal, showAllowedMessageTypesNotification, showNotification } = getActions(); const [shouldForceAsFile, setShouldForceAsFile] = useState(false); const [shouldForceCompression, setShouldForceCompression] = useState(false); @@ -92,7 +92,7 @@ export default function useAttachmentModal({ if (canReplace) { handleSetAttachments([newAttachment]); } else { - showNotification({ message: lang('lng_edit_media_album_error') }); + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); } } else { handleSetAttachments([newAttachment]); @@ -114,7 +114,7 @@ export default function useAttachmentModal({ if (canReplace) { handleSetAttachments([newAttachment]); } else { - showNotification({ message: lang('lng_edit_media_album_error') }); + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: files.length }) }); } } else { handleSetAttachments([newAttachment]); diff --git a/src/components/middle/composer/hooks/useClipboardPaste.ts b/src/components/middle/composer/hooks/useClipboardPaste.ts index 72422e366..b5dff20f4 100644 --- a/src/components/middle/composer/hooks/useClipboardPaste.ts +++ b/src/components/middle/composer/hooks/useClipboardPaste.ts @@ -14,7 +14,7 @@ import buildAttachment from '../helpers/buildAttachment'; import { preparePastedHtml } from '../helpers/cleanHtml'; import getFilesFromDataTransferItems from '../helpers/getFilesFromDataTransferItems'; -import useOldLang from '../../../../hooks/useOldLang'; +import useLang from '../../../../hooks/useLang'; const TYPE_HTML = 'text/html'; const DOCUMENT_TYPE_WORD = 'urn:schemas-microsoft-com:office:word'; @@ -33,7 +33,7 @@ const useClipboardPaste = ( onCustomEmojiStripped?: VoidFunction, ) => { const { showNotification } = getActions(); - const lang = useOldLang(); + const lang = useLang(); useEffect(() => { if (!isActive) { @@ -104,15 +104,24 @@ const useClipboardPaste = ( const isUploadingDocumentSticker = isUploadingFileSticker(newAttachments[0]); const isInAlbum = editedMessage && editedMessage?.groupedId; + if (editedMessage && newAttachments?.length > 1) { + showNotification({ + message: lang('MediaReplaceInvalidError', undefined, { pluralValue: newAttachments.length }), + }); + return; + } + if (editedMessage && isUploadingDocumentSticker) { - showNotification({ message: lang(isInAlbum ? 'lng_edit_media_album_error' : 'lng_edit_media_invalid_file') }); + showNotification({ message: lang('MediaReplaceInvalidError', undefined, { pluralValue: 1 }) }); return; } if (isInAlbum) { shouldSetAttachments = canReplace; if (!shouldSetAttachments) { - showNotification({ message: lang('lng_edit_media_album_error') }); + showNotification({ + message: lang('MediaReplaceInvalidError', undefined, { pluralValue: newAttachments.length }), + }); return; } } diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 0e5e9bd89..968823715 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -2497,6 +2497,7 @@ export interface LangPairPlural { 'DeleteForMeChatHint': undefined; 'DeleteForEveryoneHint': undefined; 'PreviewDraggingAddItems': undefined; + 'MediaReplaceInvalidError': undefined; } export interface LangPairPluralWithVariables {