From 727641f3db2e4ec85e11c886c7218c7a38b60e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 18 Aug 2021 12:14:50 +0200 Subject: [PATCH] Add paste/drop images on cards (#987) * Add paste/drop images on cards * Fix eslint --- .../src/components/cardDetail/cardDetail.tsx | 3 + .../src/components/cardDetail/imagePaste.tsx | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 webapp/src/components/cardDetail/imagePaste.tsx diff --git a/webapp/src/components/cardDetail/cardDetail.tsx b/webapp/src/components/cardDetail/cardDetail.tsx index c3b05f6d2..9d90af9ad 100644 --- a/webapp/src/components/cardDetail/cardDetail.tsx +++ b/webapp/src/components/cardDetail/cardDetail.tsx @@ -21,6 +21,7 @@ import CommentsList from './commentsList' import CardDetailContents from './cardDetailContents' import CardDetailContentsMenu from './cardDetailContentsMenu' import CardDetailProperties from './cardDetailProperties' +import useImagePaste from './imagePaste' import './cardDetail.scss' @@ -49,6 +50,8 @@ const CardDetail = (props: Props): JSX.Element|null => { const saveTitleRef = useRef<() => void>(saveTitle) saveTitleRef.current = saveTitle + useImagePaste(card.id, card.fields.contentOrder, card.rootId) + useEffect(() => { if (!title) { titleRef.current?.focus() diff --git a/webapp/src/components/cardDetail/imagePaste.tsx b/webapp/src/components/cardDetail/imagePaste.tsx new file mode 100644 index 000000000..6c12ec167 --- /dev/null +++ b/webapp/src/components/cardDetail/imagePaste.tsx @@ -0,0 +1,62 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {useEffect, useCallback} from 'react' + +import {ImageBlock, createImageBlock} from '../../blocks/imageBlock' +import octoClient from '../../octoClient' +import mutator from '../../mutator' + +export default function useImagePaste(cardId: string, contentOrder: Array, rootId: string): void { + const uploadItems = useCallback(async (items: FileList) => { + let newImage: File|null = null + const uploads: Promise[] = [] + for (const item of items) { + newImage = item + if (newImage?.type.indexOf('image/') === 0) { + uploads.push(octoClient.uploadFile(rootId, newImage)) + } + } + + const uploaded = await Promise.all(uploads) + const blocksToInsert: ImageBlock[] = [] + for (const fileId of uploaded) { + if (!fileId) { + continue + } + const block = createImageBlock() + block.parentId = cardId + block.rootId = rootId + block.fields.fileId = fileId || '' + blocksToInsert.push(block) + } + mutator.performAsUndoGroup(async () => { + await mutator.insertBlocks(blocksToInsert, 'pasted images') + const newContentOrder = [...contentOrder, ...blocksToInsert.map((b: ImageBlock) => b.id)] + return mutator.changeCardContentOrder(cardId, contentOrder, newContentOrder, 'paste image') + }) + }, [cardId, contentOrder, rootId]) + + const onDrop = useCallback((event: DragEvent): void => { + if (event.dataTransfer) { + const items = event.dataTransfer.files + uploadItems(items) + } + }, [uploadItems]) + + const onPaste = useCallback((event: ClipboardEvent): void => { + if (event.clipboardData) { + const items = event.clipboardData.files + uploadItems(items) + } + }, [uploadItems]) + + useEffect(() => { + document.addEventListener('paste', onPaste) + document.addEventListener('drop', onDrop) + return () => { + document.removeEventListener('paste', onPaste) + document.removeEventListener('drop', onDrop) + } + }, []) +}