import classNames from 'classnames'; import React, { useState } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { spring } from 'react-motion'; import { useHistory } from 'react-router-dom'; import Blurhash from 'soapbox/components/blurhash'; import Icon from 'soapbox/components/icon'; import IconButton from 'soapbox/components/icon_button'; import Motion from '../../ui/util/optional_motion'; import type { Map as ImmutableMap } from 'immutable'; const bookIcon = require('@tabler/icons/book.svg'); const fileAnalyticsIcon = require('@tabler/icons/file-analytics.svg'); const fileCodeIcon = require('@tabler/icons/file-code.svg'); const fileTextIcon = require('@tabler/icons/file-text.svg'); const fileZipIcon = require('@tabler/icons/file-zip.svg'); const defaultIcon = require('@tabler/icons/paperclip.svg'); const presentationIcon = require('@tabler/icons/presentation.svg'); export const MIMETYPE_ICONS: Record = { 'application/x-freearc': fileZipIcon, 'application/x-bzip': fileZipIcon, 'application/x-bzip2': fileZipIcon, 'application/gzip': fileZipIcon, 'application/vnd.rar': fileZipIcon, 'application/x-tar': fileZipIcon, 'application/zip': fileZipIcon, 'application/x-7z-compressed': fileZipIcon, 'application/x-csh': fileCodeIcon, 'application/html': fileCodeIcon, 'text/javascript': fileCodeIcon, 'application/json': fileCodeIcon, 'application/ld+json': fileCodeIcon, 'application/x-httpd-php': fileCodeIcon, 'application/x-sh': fileCodeIcon, 'application/xhtml+xml': fileCodeIcon, 'application/xml': fileCodeIcon, 'application/epub+zip': bookIcon, 'application/vnd.oasis.opendocument.spreadsheet': fileAnalyticsIcon, 'application/vnd.ms-excel': fileAnalyticsIcon, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': fileAnalyticsIcon, 'application/pdf': fileTextIcon, 'application/vnd.oasis.opendocument.presentation': presentationIcon, 'application/vnd.ms-powerpoint': presentationIcon, 'application/vnd.openxmlformats-officedocument.presentationml.presentation': presentationIcon, 'text/plain': fileTextIcon, 'application/rtf': fileTextIcon, 'application/msword': fileTextIcon, 'application/x-abiword': fileTextIcon, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': fileTextIcon, 'application/vnd.oasis.opendocument.text': fileTextIcon, }; const messages = defineMessages({ description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' }, delete: { id: 'upload_form.undo', defaultMessage: 'Delete' }, }); interface IUpload { media: ImmutableMap, descriptionLimit: number, onUndo: (attachmentId: string) => void, onDescriptionChange: (attachmentId: string, description: string) => void, onOpenFocalPoint: (attachmentId: string) => void, onOpenModal: (attachments: ImmutableMap) => void, onSubmit: (history: ReturnType) => void, } const Upload: React.FC = (props) => { const intl = useIntl(); const history = useHistory(); const [hovered, setHovered] = useState(false); const [focused, setFocused] = useState(false); const [dirtyDescription, setDirtyDescription] = useState(null); const handleKeyDown: React.KeyboardEventHandler = (e) => { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { handleSubmit(); } }; const handleSubmit = () => { handleInputBlur(); props.onSubmit(history); }; const handleUndoClick: React.MouseEventHandler = e => { e.stopPropagation(); props.onUndo(props.media.get('id')); }; const handleInputChange: React.ChangeEventHandler = e => { setDirtyDescription(e.target.value); }; const handleMouseEnter = () => { setHovered(true); }; const handleMouseLeave = () => { setHovered(false); }; const handleInputFocus = () => { setFocused(true); }; const handleClick = () => { setFocused(true); }; const handleInputBlur = () => { setFocused(false); setDirtyDescription(null); if (dirtyDescription !== null) { props.onDescriptionChange(props.media.get('id'), dirtyDescription); } }; const handleOpenModal = () => { props.onOpenModal(props.media); }; const active = hovered || focused; const description = dirtyDescription || (dirtyDescription !== '' && props.media.get('description')) || ''; const focusX = props.media.getIn(['meta', 'focus', 'x']) as number | undefined; const focusY = props.media.getIn(['meta', 'focus', 'y']) as number | undefined; const x = focusX ? ((focusX / 2) + .5) * 100 : undefined; const y = focusY ? ((focusY / -2) + .5) * 100 : undefined; const mediaType = props.media.get('type'); const mimeType = props.media.getIn(['pleroma', 'mime_type']) as string | undefined; const uploadIcon = mediaType === 'unknown' && ( ); return (
{({ scale }) => (
} /> {/* Only display the "Preview" button for a valid attachment with a URL */} {(mediaType !== 'unknown' && Boolean(props.media.get('url'))) && ( } /> )}