main

mattermost/focalboard

Last updated at: 29/12/2023 09:47

index.tsx

TLDR

This file contains a React component called Attachment that represents an attachment block. The Attachment component has a Display component for rendering the attachment and an Input component for selecting and saving attachments. The component also includes a runSlashCommand function for running a slash command related to attachments.

Classes

Attachment

The Attachment component represents an attachment block. It includes the following features:

  • Name: 'attachment'
  • DisplayName: 'Attachment'
  • SlashCommand: '/attachment'
  • Prefix: ''
  • Editable: false

Display

The Display component is responsible for rendering the attachment. It receives BlockInputProps<FileInfo> as its props and displays the following:

  • A link to the attachment file
  • The attachment filename

Input

The Input component is responsible for selecting and saving attachments. It receives BlockInputProps<FileInfo> as its props and displays the following:

  • An input field of type 'file' for selecting attachments

END

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef, useEffect, useState} from 'react'

import {BlockInputProps, ContentType} from '../types'
import octoClient from '../../../../octoClient'

import './attachment.scss'

type FileInfo = {
    file: string|File
    filename: string
}

const Attachment: ContentType<FileInfo> = {
    name: 'attachment',
    displayName: 'Attachment',
    slashCommand: '/attachment',
    prefix: '',
    runSlashCommand: (): void => {},
    editable: false,
    Display: (props: BlockInputProps<FileInfo>) => {
        const [fileDataUrl, setFileDataUrl] = useState<string|null>(null)

        useEffect(() => {
            if (!fileDataUrl) {
                const loadFile = async () => {
                    if (props.value && props.value.file && typeof props.value.file === 'string') {
                        const fileURL = await octoClient.getFileAsDataUrl(props.currentBoardId || '', props.value.file)
                        setFileDataUrl(fileURL.url || '')
                    }
                }
                loadFile()
            }
        }, [props.value, props.value.file, props.currentBoardId])

        return (
            <div
                className='AttachmentView'
                data-testid='attachment'
            >
                <a
                    href={fileDataUrl || '#'}
                    onClick={(e) => e.stopPropagation()}
                    download={props.value.filename}
                >
                    {'📎'} {props.value.filename}
                </a>
            </div>
        )
    },
    Input: (props: BlockInputProps<FileInfo>) => {
        const ref = useRef<HTMLInputElement|null>(null)
        useEffect(() => {
            ref.current?.click()
        }, [])

        return (
            <input
                ref={ref}
                className='Attachment'
                data-testid='attachment-input'
                type='file'
                onChange={(e) => {
                    const files = e.currentTarget?.files
                    if (files) {
                        for (let i = 0; i < files.length; i++) {
                            const file = files.item(i)
                            if (file) {
                                props.onSave({file, filename: file.name})
                            }
                        }
                    }
                }}
            />
        )
    },
}

Attachment.runSlashCommand = (changeType: (contentType: ContentType<FileInfo>) => void, changeValue: (value: FileInfo) => void): void => {
    changeType(Attachment)
    changeValue({} as any)
}

export default Attachment