import { useConfig } from '@/components/ConfigProvider'; import { useUploadOptionsStore } from '@/lib/store/uploadOptions'; import { ActionIcon, Button, Collapse, Grid, Group, Kbd, Paper, Progress, Text, Title, Tooltip, rem, useMantineTheme, } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { useClipboard, useColorScheme } from '@mantine/hooks'; import { notifications, showNotification } from '@mantine/notifications'; import { IconDeviceSdCard, IconFiles, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import Link from 'next/link'; import { useEffect, useState } from 'react'; import UploadOptionsButton from '../UploadOptionsButton'; import { uploadFiles } from '../uploadFiles'; import ToUploadFile from './ToUploadFile'; import { bytes } from '@/lib/bytes'; import { uploadPartialFiles } from '../uploadPartialFiles'; import { humanizeDuration } from '@/lib/relativeTime'; import { useShallow } from 'zustand/shallow'; export default function UploadFile() { const theme = useMantineTheme(); const colorScheme = useColorScheme(); const clipboard = useClipboard(); const config = useConfig(); const [options, ephemeral, clearEphemeral] = useUploadOptionsStore( useShallow((state) => [state.options, state.ephemeral, state.clearEphemeral]), ); const [files, setFiles] = useState([]); const [progress, setProgress] = useState<{ percent: number; remaining: number; speed: number }>({ percent: 0, remaining: 0, speed: 0, }); const [dropLoading, setLoading] = useState(false); const handlePaste = (e: ClipboardEvent) => { if (!e.clipboardData) return; for (let i = 0; i !== e.clipboardData.items.length; ++i) { if (!e.clipboardData.items[i].type.startsWith('image')) return; const blob = e.clipboardData.items[i].getAsFile(); if (!blob) return; setFiles([...files, blob]); showNotification({ message: `Image ${blob.name} pasted from clipboard`, color: 'blue', }); } }; const aggSize = () => files.reduce((acc, file) => acc + file.size, 0); const upload = () => { const toPartialFiles: File[] = []; for (let i = 0; i !== files.length; ++i) { const file = files[i]; if (config.chunks.enabled && file.size >= config.chunks.max) { toPartialFiles.push(file); } } if (toPartialFiles.length > 0) { uploadPartialFiles(toPartialFiles, { setFiles, setLoading, setProgress, clipboard, clearEphemeral, options, ephemeral, config, }); } else { const size = aggSize(); if (size > config.files.maxFileSize && !toPartialFiles.length) { notifications.show({ title: 'Upload may fail', color: 'yellow', icon: , message: ( <> The upload may fail because the total size of the files (that are not being partially uploaded) you are trying to upload is {bytes(size)}, which is larger than the limit of{' '} {bytes(config.files.maxFileSize)} ), }); } uploadFiles(files, { setFiles, setLoading, setProgress, clipboard, clearEphemeral, options, ephemeral, }); } }; useEffect(() => { document.addEventListener('paste', handlePaste); return () => { document.removeEventListener('paste', handlePaste); }; }, []); return ( <> Upload files setFiles([...f, ...files])} my='sm' loading={dropLoading} disabled={dropLoading} >
Drag images here or click to select files Or Ctrl + V to paste images from clipboard Attach as many files as you like, they will show up below to review before uploading. {bytes(config.files.maxFileSize)} limit per file
0 && progress.percent < 100}> {progress.percent > 0 && progress.percent < 100 && ( {Math.floor(progress.percent)}% )} 0 && progress.remaining > 0}> {bytes(progress.speed)}/s, {humanizeDuration(progress.remaining)} remaining Finalizing upload(s)... {files.map((file, i) => ( setFiles(files.filter((_, j) => i !== j))} /> ))} ); }