CoastalCommitsPastes/client/app/(posts)/new/components/drag-and-drop/index.tsx

106 lines
2.8 KiB
TypeScript
Raw Normal View History

2022-04-09 20:48:19 -04:00
import { useDropzone } from "react-dropzone"
import styles from "./drag-and-drop.module.css"
import generateUUID from "@lib/generate-uuid"
import {
allowedFileTypes,
allowedFileNames,
allowedFileExtensions
} from "@lib/constants"
import byteToMB from "@lib/byte-to-mb"
import type { Document } from "../new"
import { useToasts } from "@components/toasts"
2022-04-09 20:48:19 -04:00
function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
const { setToast } = useToasts()
const onDrop = async (acceptedFiles: File[]) => {
const newDocs = await Promise.all(
acceptedFiles.map((file) => {
return new Promise<Document>((resolve) => {
const reader = new FileReader()
2022-04-09 20:48:19 -04:00
reader.onabort = () =>
setToast({ message: "File reading was aborted", type: "error" })
2022-04-09 20:48:19 -04:00
reader.onerror = () =>
setToast({ message: "File reading failed", type: "error" })
2022-04-09 20:48:19 -04:00
reader.onload = () => {
const content = reader.result as string
resolve({
title: file.name,
content,
id: generateUUID()
})
}
reader.readAsText(file)
})
})
)
2022-04-09 20:48:19 -04:00
setDocs(newDocs)
}
2022-04-09 20:48:19 -04:00
const validator = (file: File) => {
// TODO: make this configurable
const maxFileSize = 50000000
if (file.size > maxFileSize) {
return {
code: "file-too-big",
message:
"File is too big. Maximum file size is " +
byteToMB(maxFileSize) +
" MB."
}
}
2022-04-09 20:48:19 -04:00
// We initially try to use the browser provided mime type, and then fall back to file names and finally extensions
if (
allowedFileTypes.includes(file.type) ||
allowedFileNames.includes(file.name) ||
allowedFileExtensions.includes(file.name?.split(".").pop() || "")
) {
return null
} else {
return {
code: "not-plain-text",
message: `Only plain text files are allowed.`
}
}
}
2022-03-28 15:04:29 -04:00
2022-04-09 20:48:19 -04:00
const { getRootProps, getInputProps, isDragActive, fileRejections } =
useDropzone({ onDrop, validator })
2022-04-09 20:48:19 -04:00
const fileRejectionItems = fileRejections.map(({ file, errors }) => (
<li key={file.name}>
{file.name}:
<ul>
{errors.map((e) => (
2022-11-18 01:36:53 -05:00
<li key={e.code}>{e.message}</li>
2022-04-09 20:48:19 -04:00
))}
</ul>
</li>
))
2022-04-09 20:48:19 -04:00
return (
<div className={styles.container}>
<div {...getRootProps()} className={styles.dropzone}>
2022-04-09 20:48:19 -04:00
<input {...getInputProps()} />
{!isDragActive && (
2022-11-18 01:36:53 -05:00
<p style={{ color: "var(--gray)" }}>
Drag some files here, or <span className={styles.verb} /> to select files
2022-11-18 01:36:53 -05:00
</p>
2022-04-09 20:48:19 -04:00
)}
{isDragActive && <p>Release to drop the files here</p>}
2022-04-09 20:48:19 -04:00
</div>
{fileRejections.length > 0 && (
<ul className={styles.error}>
{/* <Button style={{ float: 'right' }} type="abort" onClick={() => fileRejections.splice(0, fileRejections.length)} auto iconRight={<XCircle />}></Button> */}
<p>There was a problem with one or more of your files.</p>
2022-04-09 20:48:19 -04:00
{fileRejectionItems}
</ul>
)}
</div>
)
}
2022-11-12 04:28:06 -05:00
export default FileDropzone