commit 2f84475320e9565860dad4948715497e2a557dd1
parent d6fb88bcac5e3b0e1e567be455a9e501ff37284a
Author: MTRNord <mtrnord1@gmail.com>
Date: Sun, 23 Jan 2022 01:09:34 +0100
Implement submit page dropzone
Diffstat:
| M | package-lock.json | | | 81 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
| M | package.json | | | 3 | ++- |
| A | pages/submit.tsx | | | 130 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 206 insertions(+), 8 deletions(-)
diff --git a/package-lock.json b/package-lock.json
@@ -16,7 +16,8 @@
"pouchdb": "7.2.2",
"react": "17.0.2",
"react-blurhash": "0.1.3",
- "react-dom": "17.0.2"
+ "react-dom": "17.0.2",
+ "react-dropzone": "11.5.1"
},
"devDependencies": {
"@babel/plugin-transform-typescript": "7.16.8",
@@ -1821,6 +1822,14 @@
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
"dev": true
},
+ "node_modules/attr-accept": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
+ "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/autoprefixer": {
"version": "10.4.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz",
@@ -3231,6 +3240,22 @@
"node": "^10.12.0 || >=12.0.0"
}
},
+ "node_modules/file-selector": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
+ "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/file-selector/node_modules/tslib": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+ },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -5442,7 +5467,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -5452,8 +5476,7 @@
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/proper-lockfile": {
"version": "4.1.2",
@@ -5566,6 +5589,22 @@
"react": "17.0.2"
}
},
+ "node_modules/react-dropzone": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz",
+ "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==",
+ "dependencies": {
+ "attr-accept": "^2.2.1",
+ "file-selector": "^0.2.2",
+ "prop-types": "^15.7.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8"
+ }
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -8109,6 +8148,11 @@
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
"dev": true
},
+ "attr-accept": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
+ "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
+ },
"autoprefixer": {
"version": "10.4.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz",
@@ -9175,6 +9219,21 @@
"flat-cache": "^3.0.4"
}
},
+ "file-selector": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
+ "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
+ "requires": {
+ "tslib": "^2.0.3"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+ }
+ }
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -10775,7 +10834,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -10785,8 +10843,7 @@
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
@@ -10869,6 +10926,16 @@
"scheduler": "^0.20.2"
}
},
+ "react-dropzone": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz",
+ "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==",
+ "requires": {
+ "attr-accept": "^2.2.1",
+ "file-selector": "^0.2.2",
+ "prop-types": "^15.7.2"
+ }
+ },
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
diff --git a/package.json b/package.json
@@ -27,7 +27,8 @@
"pouchdb": "7.2.2",
"react": "17.0.2",
"react-blurhash": "0.1.3",
- "react-dom": "17.0.2"
+ "react-dom": "17.0.2",
+ "react-dropzone": "11.5.1"
},
"devDependencies": {
"@babel/plugin-transform-typescript": "7.16.8",
diff --git a/pages/submit.tsx b/pages/submit.tsx
@@ -0,0 +1,129 @@
+import Head from "next/head";
+import { PureComponent, ReactNode } from "react";
+import Dropzone, { FileRejection } from "react-dropzone";
+import { ClientContext } from "../components/ClientContext";
+import Header from "../components/Header";
+
+type Props = {};
+
+type State = {
+ files: PreviewWithDataFile[];
+ fileRejections: FileRejection[];
+ error?: string;
+};
+
+type PreviewWithDataFile = File & {
+ preview: HTMLImageElement;
+ data: ArrayBuffer;
+};
+
+class Submit extends PureComponent<Props, State> {
+ declare context: React.ContextType<typeof ClientContext>;
+ onDrop: (files: File[]) => void;
+
+ constructor(props: Props) {
+ super(props);
+ this.onDrop = async (files: File[]) => {
+ const files_mapped = await Promise.all(files.map(async file => Object.assign(file, {
+ preview: await this.readFileToSizes(file),
+ data: await this.readFileToArray(file),
+ }))) as PreviewWithDataFile[];
+ this.setState({
+ files: [...this.state.files, ...files_mapped]
+ });
+ };
+ this.state = {
+ files: [],
+ fileRejections: []
+ };
+ }
+
+ readFileToSizes(file: File): Promise<HTMLImageElement> {
+ return new Promise((resolve, reject) => {
+ var fr = new FileReader();
+ fr.onload = () => {
+ const img = new Image();
+ // Save as we use readAsDataURL
+ img.src = fr.result as string;
+
+ resolve(img);
+ };
+ fr.onerror = reject;
+ fr.readAsDataURL(file);
+ });
+ }
+
+ readFileToArray(file: File): Promise<ArrayBuffer> {
+ return new Promise((resolve, reject) => {
+ var fr = new FileReader();
+ fr.onload = () => {
+ resolve(fr.result as ArrayBuffer);
+ };
+ fr.onerror = reject;
+ fr.readAsArrayBuffer(file);
+ });
+ }
+
+ onDropError(fileRejections: FileRejection[]) {
+ const newRejections = fileRejections.filter(x => !this.state.fileRejections.includes(x));
+ if (newRejections.length > 0) {
+ let errors = "";
+ for (const rejection of newRejections) {
+ if (errors === "") {
+ errors = `${rejection.file.name} - ${rejection.errors.join(" & ")}`;
+ } else {
+ errors = `${errors}\n${rejection.file.name} - ${rejection.errors.join(" & ")}`;
+ }
+ }
+ this.setState({
+ error: errors
+ });
+ }
+ this.setState({ fileRejections: newRejections });
+ }
+
+ renderThumbs() {
+ return this.state.files.map(file => (
+ <div className="relative cursor-pointer border-2 border-solid border-[#eeeeea] p-1" style={{ height: "212px" }} key={file.name}>
+ <img className="h-auto w-full object-cover max-w-full align-bottom" style={{ height: "200px" }} src={file.preview.src} width={file.preview.width} height={file.preview.height} />
+ </div>
+ ));
+ }
+
+ render(): ReactNode {
+ return (
+ <div className='h-full bg-[#fefefe]/[.95] dark:bg-[#14181E]/[.95]'>
+ <Head>
+ <title key="title">Matrix Art | Submit Post</title>
+ <meta property="og:title" content="Matrix Art | Submit Post" key="og-title" />
+ <meta name="twitter:card" content="summary_large_image" key="og-twitter" />
+ <meta name="twitter:title" content="Matrix Art | Submit Post" key="og-twitter-title" />
+ <meta property="og:type" content="website" key="og-type" />
+ </Head>
+ <Header></Header>
+ <main className='w-full lg:pt-20 pt-52 z-0'>
+ <div className="flex justify-center items-center mt-4">
+ <Dropzone accept='image/*' onDropAccepted={this.onDrop} onDropRejected={this.onDropError}>
+ {({ getRootProps, getInputProps }) => (
+ <section className="flex flex-col">
+ <div className="dark:bg-[#fefefe]/[.25] bg-[#14181E]/[.25] min-h-[11.5rem] min-w-[39.5rem] flex-[1] flex flex-col items-center justify-center p-5 border-2 rounded-sm border-[#eeeeee] border-dashed outline-none focus:border-[#2196f3] duration-[24ms] transition" {...getRootProps()}>
+ <input {...getInputProps()} />
+ <p className="text-base text-gray-900 dark:text-gray-200">Drag 'n' drop some images here, or click to select images</p>
+ </div>
+ <ul className="flex flex-row flex-wrap gap-1 mt-4">
+ {this.renderThumbs()}
+ <li className='grow-[10]'></li>
+ </ul>
+ </section>
+ )}
+ </Dropzone>
+ </div>
+ </main>
+ </div>
+ );
+ }
+}
+
+Submit.contextType = ClientContext;
+
+export default Submit;
+\ No newline at end of file