main

mattermost/focalboard

Last updated at: 28/12/2023 01:34

imagePaste.tsx

TLDR

This file imagePaste.tsx is a React component that handles image pasting and dropping functionality. It allows users to paste or drop images onto a board and automatically uploads and inserts them as image blocks.

Methods

useImagePaste

This method is the main function of the file. It takes in boardId, cardId, and contentOrder as parameters and sets up event listeners for paste and drop events. It also defines the behavior for handling pasted or dropped images. The method utilizes various helper functions from other files to handle image uploading, creating image blocks, sending flash messages, and manipulating the board's content order.

Classes

None

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {useEffect, useCallback} from 'react'
import {useIntl} from 'react-intl'

import {ImageBlock, createImageBlock} from '../../blocks/imageBlock'
import {sendFlashMessage} from '../flashMessages'
import {Block} from '../../blocks/block'
import octoClient from '../../octoClient'
import mutator from '../../mutator'

export default function useImagePaste(boardId: string, cardId: string, contentOrder: Array<string | string[]>): void {
    const intl = useIntl()
    const uploadItems = useCallback(async (items: FileList) => {
        let newImage: File|null = null
        const uploads: Array<Promise<string|undefined>> = []

        if (!items.length) {
            return
        }

        for (const item of items) {
            newImage = item
            if (newImage?.type.indexOf('image/') === 0) {
                uploads.push(octoClient.uploadFile(boardId, newImage))
            }
        }

        const uploaded = await Promise.all(uploads)
        const blocksToInsert: ImageBlock[] = []
        let someFilesNotUploaded = false
        for (const fileId of uploaded) {
            if (!fileId) {
                someFilesNotUploaded = true
                continue
            }
            const block = createImageBlock()
            block.parentId = cardId
            block.boardId = boardId
            block.fields.fileId = fileId || ''
            blocksToInsert.push(block)
        }

        if (someFilesNotUploaded) {
            sendFlashMessage({content: intl.formatMessage({id: 'imagePaste.upload-failed', defaultMessage: 'Some files not uploaded. File size limit reached'}), severity: 'normal'})
        }

        const afterRedo = async (newBlocks: Block[]) => {
            const newContentOrder = JSON.parse(JSON.stringify(contentOrder))
            newContentOrder.push(...newBlocks.map((b: Block) => b.id))
            await octoClient.patchBlock(boardId, cardId, {updatedFields: {contentOrder: newContentOrder}})
        }

        const beforeUndo = async () => {
            const newContentOrder = JSON.parse(JSON.stringify(contentOrder))
            await octoClient.patchBlock(boardId, cardId, {updatedFields: {contentOrder: newContentOrder}})
        }

        await mutator.insertBlocks(boardId, blocksToInsert, 'pasted images', afterRedo, beforeUndo)
    }, [cardId, contentOrder, boardId])

    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)
        }
    }, [uploadItems, onPaste, onDrop])
}