diff --git a/src/App.js b/src/App.js index ec74f68c..14ec30f6 100644 --- a/src/App.js +++ b/src/App.js @@ -12,7 +12,7 @@ import * as Actions from "./redux/actionCreators"; import { ABOUT_PAGE } from "./constants"; import { useWindowSize, useScrollbarWidth, useActionCreator } from "./hooks"; import { SCREENSHOT_WIDTH, SKIN_RATIO } from "./constants"; -import UploadGrid from "./UploadGrid"; +import UploadGrid from "./upload/UploadGrid"; import Metadata from "./components/Metadata"; import SkinReadme from "./SkinReadme"; import { useDropzone } from "react-dropzone"; diff --git a/src/UploadGrid.js b/src/UploadGrid.js deleted file mode 100644 index 501e11ef..00000000 --- a/src/UploadGrid.js +++ /dev/null @@ -1,215 +0,0 @@ -import React, { useMemo } from "react"; -import { useSelector } from "react-redux"; -import { HEADING_HEIGHT } from "./constants"; -import { useActionCreator } from "./hooks"; -import * as Actions from "./redux/actionCreators"; -import * as Utils from "./utils"; -import DropTarget from "./DropTarget"; - -function Section({ files, title, render, open = false }) { - if (files.length === 0) { - return null; - } - return ( -
- -

{title}

-
- -
- ); -} - -function useBucketed(filesArr) { - return useMemo(() => { - const missing = []; - const notSkins = []; - const foundSkins = []; - filesArr.forEach((file) => { - switch (file.status) { - case "MISSING": - case "UPLOADING": - case "UPLOAD_FAILED": - case "ARCHIVED": - missing.push(file); - break; - case "INVALID_FILE_EXTENSION": - case "INVALID_ARCHIVE": - notSkins.push(file); - break; - case "FOUND": - foundSkins.push(file); - break; - default: - } - }); - return { missing, notSkins, foundSkins }; - }, [filesArr]); -} - -function Plural({ count, single, plural }) { - return count === 1 ? single : plural; -} - -function Inner({ files }) { - const tryToUploadAllFiles = useActionCreator(Actions.tryToUploadAllFiles); - const filesArr = Object.values(files); - const { missing, notSkins, foundSkins } = useBucketed(filesArr); - - if (filesArr.some((file) => file.status === "NEW")) { - return ( -

- Analyzing {filesArr.length.toLocaleString()}{" "} - - ... -

- ); - } - - return ( - <> -

- You have {missing.length.toLocaleString()}{" "} - that we - are missing! -

- {filesArr.some((file) => file.status === "MISSING") && ( -
- -
- )} - - - - - - - - - {missing - .map((file) => { - const fileName = {file.file.name}; - switch (file.status) { - case "MISSING": - return ( - <> - - - ); - case "UPLOADING": - return ( - <> - - - - ); - case "UPLOAD_FAILED": - return ( - <> - - - - ); - case "ARCHIVED": - return ( - <> - - - - ); - default: - console.error(`Unexpected file status: ${file.status}`); - return null; - } - }) - .map((rows, i) => ( - {rows} - ))} - -
StatusFilename
- {fileName}🚀 Uploading...{fileName}❌ Upload Failed{fileName}✅ Added! - {file.skinType === "CLASSIC" ? ( - - {fileName} - - ) : file.skinType === "MODERN" ? ( - <> - {fileName} (Note: Modern skins are not yet visible - in the museum) - - ) : // TODO: Throw? - null} -
-
- Files that are not valid skins ({notSkins.length.toLocaleString()}) - - } - render={(file) => {file.file.name}} - /> - -
Already in the museum ({foundSkins.length.toLocaleString()}) - } - render={(file) => { - if (file.skinType === "MODERN") { - return ( - <> - {file.file.name} (Note: Modern skins are not yet - visible in the museum) - - ); - } - return ( - - {file.file.name} - - ); - }} - /> - - ); -} - -function UploadGrid({ getInputProps, isDragActive, ...props }) { - const files = useSelector((state) => state.fileUploads); - return ( -
- {isDragActive || Object.keys(files).length === 0 ? ( - - ) : ( -
- -
- )} -
- ); -} -export default UploadGrid; diff --git a/src/components/Metadata.js b/src/components/Metadata.js index dfc26651..2a6252a1 100644 --- a/src/components/Metadata.js +++ b/src/components/Metadata.js @@ -2,7 +2,7 @@ import React, { useState } from "react"; import DownloadLink from "./DownloadLink"; import * as Utils from "../utils"; import LinkInput from "./LinkInput"; -import { API_URL } from "../constants"; +// import { API_URL } from "../constants"; // import * as Actions from "../redux/actionCreators"; import * as Selectors from "../redux/selectors"; import * as Actions from "../redux/actionCreators"; diff --git a/src/redux/epics.js b/src/redux/epics.js index e09df907..d43bf57e 100644 --- a/src/redux/epics.js +++ b/src/redux/epics.js @@ -18,7 +18,7 @@ import { import { search } from "../algolia"; import queryParser from "../queryParser"; import { API_URL, CHUNK_SIZE } from "../constants"; -import * as UploadUtils from "../uploadUtils"; +import * as UploadUtils from "../upload/uploadUtils"; const urlChangedEpic = (actions) => actions.pipe( diff --git a/src/upload/UploadGrid.js b/src/upload/UploadGrid.js new file mode 100644 index 00000000..513a7630 --- /dev/null +++ b/src/upload/UploadGrid.js @@ -0,0 +1,128 @@ +import React, { useMemo } from "react"; +import { useSelector } from "react-redux"; +import { HEADING_HEIGHT } from "../constants"; +import { useActionCreator } from "../hooks"; +import * as Actions from "../redux/actionCreators"; +import DropTarget from "../DropTarget"; +import UploadSection from "./UploadSection"; + +function useBucketed(filesArr) { + return useMemo(() => { + const missing = []; + const notSkins = []; + const foundSkins = []; + filesArr.forEach((file) => { + switch (file.status) { + case "MISSING": + case "UPLOADING": + case "UPLOAD_FAILED": + case "ARCHIVED": + missing.push(file); + break; + case "INVALID_FILE_EXTENSION": + case "INVALID_ARCHIVE": + notSkins.push(file); + break; + case "FOUND": + foundSkins.push(file); + break; + default: + } + }); + return { missing, notSkins, foundSkins }; + }, [filesArr]); +} + +function Plural({ count, single, plural }) { + return count === 1 ? single : plural; +} + +function Inner({ files }) { + const tryToUploadAllFiles = useActionCreator(Actions.tryToUploadAllFiles); + const filesArr = Object.values(files); + const { missing, notSkins, foundSkins } = useBucketed(filesArr); + + const analyzing = filesArr.some((file) => file.status === "NEW"); + + if (analyzing) { + return ( +

+ Analyzing {filesArr.length.toLocaleString()}{" "} + + ... +

+ ); + } + + const stillHaveFilesToUpload = filesArr.some( + (file) => file.status === "MISSING" || file.status === "UPLOADING" + ); + + const filesToUpload = missing.length; + + const getTitle = () => { + if (filesToUpload > 0) { + if (stillHaveFilesToUpload) { + return ( + <> + Found {filesToUpload.toLocaleString()}{" "} + to + upload + + ); + } else { + return `Thanks for your contribution!`; + } + } + return `No missing skins found`; + }; + + return ( +
+

{getTitle()}

+ + Upload All + + ) + } + files={missing} + /> + + +
+ ); +} + +function UploadGrid({ getInputProps, isDragActive, ...props }) { + const files = useSelector((state) => state.fileUploads); + return ( +
+ {isDragActive || Object.keys(files).length === 0 ? ( + + ) : ( +
+ +
+ )} +
+ ); +} +export default UploadGrid; diff --git a/src/upload/UploadRow.js b/src/upload/UploadRow.js new file mode 100644 index 00000000..aab6e526 --- /dev/null +++ b/src/upload/UploadRow.js @@ -0,0 +1,117 @@ +import React from "react"; +import * as Utils from "../utils"; + +function Row({ name, loading, right, complete }) { + return ( +
+ {(loading != null || complete) && ( +
+ )} +
+ {name} + {right && ( + + {right} + + )} +
+
+ ); +} + +function SkinLink({ md5, children }) { + return ( + + {children} + + ); +} + +// TODO: This is a component +function getRight(file) { + switch (file.status) { + case "ARCHIVED": + switch (file.skinType) { + case "MODERN": + return "archived"; + case "CLASSIC": + return added; + default: + throw new Error(`Unknown skinType "${file.skinType}"`); + } + case "FOUND": + switch (file.skinType) { + case "MODERN": + return "archived"; + case "CLASSIC": + return view; + default: + throw new Error(`Unknown skinType "${file.skinType}"`); + } + case "MISSING": + return "missing"; + case "UPLOADING": + return "uploading..."; + case "UPLOAD_FAILED": + return "upload failed"; + case "INVALID_ARCHIVE": + return "corrupt"; + case "INVALID_FILE_EXTENSION": + return "not skin"; + default: + return file.status; + } +} + +function UploadRow({ file }) { + return ( + + ); +} + +export default UploadRow; diff --git a/src/upload/UploadSection.js b/src/upload/UploadSection.js new file mode 100644 index 00000000..feffb179 --- /dev/null +++ b/src/upload/UploadSection.js @@ -0,0 +1,34 @@ +import React from "react"; +import UploadRow from "./UploadRow"; + +function UploadSection({ files, title, extra }) { + if (files.length === 0) { + return null; + } + return ( +
+
+

+ {title} ({files.length.toLocaleString()}) +

+
{extra}
+
+ {files.map((file) => ( + + ))} +
+ ); +} + +export default UploadSection; diff --git a/src/uploadUtils.js b/src/upload/uploadUtils.js similarity index 92% rename from src/uploadUtils.js rename to src/upload/uploadUtils.js index 916ab860..c06187ef 100644 --- a/src/uploadUtils.js +++ b/src/upload/uploadUtils.js @@ -1,4 +1,4 @@ -import { API_URL } from "./constants"; +import { API_URL } from "../constants"; export async function upload(file) { const formData = new FormData(); formData.append("skin", file, file.name); @@ -22,7 +22,7 @@ export async function checkMd5sAreMissing(md5s) { } export async function hashFile(file) { - const { hashFile: hasher } = await import("./hashFile"); + const { hashFile: hasher } = await import("../hashFile"); return hasher(file); }