From 332855253f22b1c9017527b255ebfc67fabda33e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Mon, 14 Sep 2020 22:26:56 -0700 Subject: [PATCH 1/4] Revert "Revert "Start table"" This reverts commit 02d7c252fd0c2644872bf53f82106f9886fd6c44. --- src/UploadGrid.js | 340 ++++++++++++++++++++++++++++------------------ 1 file changed, 207 insertions(+), 133 deletions(-) diff --git a/src/UploadGrid.js b/src/UploadGrid.js index 10dca643..654062ba 100644 --- a/src/UploadGrid.js +++ b/src/UploadGrid.js @@ -4,67 +4,223 @@ 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 DropTarget({ getInputProps }) { - return ( -
-
Drop Skins Here
-
- We'll analyze them in your browser to find any that are missing from the - museum -
- -
- ); -} - -function Section({ files, filter, title, render }) { - const matches = useMemo(() => files.filter(filter), [files, filter]); - if (matches.length === 0) { +function Section({ files, title, render, open = false }) { + if (files.length === 0) { return null; } return ( - <> -

- {matches.length.toLocaleString()} {title} -

+
+ +

{title}

+
+
+ ); +} + +function useBucketed(filesArr) { + return useMemo(() => { + const missing = []; + const notSkins = []; + const modernSkins = []; + 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": + notSkins.push(file); + break; + case "NOT_CLASSIC_SKIN": + modernSkins.push(file); + break; + case "FOUND": + foundSkins.push(file); + break; + default: + } + }); + return { missing, notSkins, modernSkins, foundSkins }; + }, [filesArr]); +} + +function Plural({ count, single, plural }) { + return count === 1 ? single : plural; +} + +function Inner({ getInputProps, isDragActive, files }) { + const tryToUploadAllFiles = useActionCreator(Actions.tryToUploadAllFiles); + const filesArr = Object.values(files); + const { missing, notSkins, modernSkins, 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) => { + 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; + } + })} + +
StatusFilename
+ + {file.file.name} +
🚀 Uploading... + {file.file.name} +
❌ Upload Failed + {file.file.name} + ✅ Added! + + {file.file.name} + +
+
New skins ({missing.length.toLocaleString()})} + render={(file) => { + switch (file.status) { + case "MISSING": + return {file.file.name}; + case "UPLOADING": + return ( + <> + {file.file.name} (🚀 Uploading...) + + ); + case "UPLOAD_FAILED": + return ( + <> + {file.file.name} (❌ Upload Failed) + + ); + case "ARCHIVED": + return ( + <> + + {file.file.name} + {" "} + (✅ Added!) + + ); + default: + console.error(`Unexpected file status: ${file.status}`); + return null; + } + }} + /> +
Files that are not skins ({notSkins.length.toLocaleString()}) + } + render={(file) => {file.file.name}} + /> +
+ Modern Skins (we're not accepting these yet) ( + {modernSkins.length.toLocaleString()}) + + } + render={(file) => {file.file.name}} + /> +
Already in the museum ({foundSkins.length.toLocaleString()}) + } + render={(file) => ( + + {file.file.name} + + )} + /> ); } -function UploadGrid({ getInputProps, isDragActive }) { +function UploadGrid({ getInputProps, isDragActive, ...props }) { const files = useSelector((state) => state.fileUploads); - const tryToUploadAllFiles = useActionCreator(Actions.tryToUploadAllFiles); - const filesArr = Object.values(files); - return (
{isDragActive || Object.keys(files).length === 0 ? ( ) : ( -
-
- This feature is still in beta. If you have any issues, please reach - out to{" "} - - jordan@jordaneldredge.com - - . -
-

- {`You've dragged in ${filesArr.length.toLocaleString()} files`} - {filesArr.some((file) => file.status === "NEW") && - " (Analyzing...)"} -

-
- are new skins! - {filesArr.some((file) => file.status === "MISSING") && ( -
- -
- )} - - } - filter={(file) => - file.status === "MISSING" || - file.status === "UPLOADING" || - file.status === "UPLOAD_FAILED" || - file.status === "ARCHIVED" - } - render={(file) => { - switch (file.status) { - case "MISSING": - return file.file.name; - case "UPLOADING": - return <>{file.file.name} (🚀 Uploading...); - case "UPLOAD_FAILED": - return <>{file.file.name} (❌ Upload Failed); - case "ARCHIVED": - return ( - <> - - {file.file.name} - {" "} - (✅ Added!) - - ); - default: - console.error(`Unexpected file status: ${file.status}`); - return null; - } - }} - /> -
file.status === "INVALID_FILE_EXTENSION"} - render={(file) => file.file.name} - /> -
file.status === "NOT_CLASSIC_SKIN"} - render={(file) => file.file.name} - /> -
file.status === "FOUND"} - render={(file) => ( - - {file.file.name} - - )} - /> +
+
)}
From b7f59a0e10be946482bac32b074f1d3be00384d2 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Mon, 14 Sep 2020 22:27:07 -0700 Subject: [PATCH 2/4] Add droptarget --- src/DropTarget.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/DropTarget.js diff --git a/src/DropTarget.js b/src/DropTarget.js new file mode 100644 index 00000000..b305a8b2 --- /dev/null +++ b/src/DropTarget.js @@ -0,0 +1,39 @@ +import React from "react"; + +function DropTarget({ getInputProps }) { + return ( +
+
Drop Skins Here
+
+ We'll analyze them in your browser to find any that are missing from the + museum +
+ +
+ ); +} + +export default DropTarget; From 4595d3213770408b002879dd42c2948c2c85b467 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 15 Sep 2020 15:15:58 -0700 Subject: [PATCH 3/4] Allow uploading modern skins --- src/UploadGrid.js | 194 +++++++++++++++--------------------- src/redux/actionCreators.js | 8 +- src/redux/epics.js | 22 ++-- src/redux/reducer.js | 17 +++- src/uploadUtils.js | 11 +- 5 files changed, 120 insertions(+), 132 deletions(-) diff --git a/src/UploadGrid.js b/src/UploadGrid.js index 654062ba..501e11ef 100644 --- a/src/UploadGrid.js +++ b/src/UploadGrid.js @@ -28,7 +28,6 @@ function useBucketed(filesArr) { return useMemo(() => { const missing = []; const notSkins = []; - const modernSkins = []; const foundSkins = []; filesArr.forEach((file) => { switch (file.status) { @@ -39,18 +38,16 @@ function useBucketed(filesArr) { missing.push(file); break; case "INVALID_FILE_EXTENSION": + case "INVALID_ARCHIVE": notSkins.push(file); break; - case "NOT_CLASSIC_SKIN": - modernSkins.push(file); - break; case "FOUND": foundSkins.push(file); break; default: } }); - return { missing, notSkins, modernSkins, foundSkins }; + return { missing, notSkins, foundSkins }; }, [filesArr]); } @@ -58,10 +55,10 @@ function Plural({ count, single, plural }) { return count === 1 ? single : plural; } -function Inner({ getInputProps, isDragActive, files }) { +function Inner({ files }) { const tryToUploadAllFiles = useActionCreator(Actions.tryToUploadAllFiles); const filesArr = Object.values(files); - const { missing, notSkins, modernSkins, foundSkins } = useBucketed(filesArr); + const { missing, notSkins, foundSkins } = useBucketed(filesArr); if (filesArr.some((file) => file.status === "NEW")) { return ( @@ -93,127 +90,98 @@ function Inner({ getInputProps, isDragActive, files }) { - {missing.map((file) => { - switch (file.status) { - case "MISSING": - return ( - - - - {file.file.name} - - - ); - case "UPLOADING": - return ( - - 🚀 Uploading... - - {file.file.name} - - - ); - case "UPLOAD_FAILED": - return ( - <> - ❌ Upload Failed - - {file.file.name} - - - ); - case "ARCHIVED": - return ( - <> - ✅ Added! - - - {file.file.name} - - - - ); - default: - console.error(`Unexpected file status: ${file.status}`); - return null; - } - })} + {missing + .map((file) => { + const fileName = {file.file.name}; + switch (file.status) { + case "MISSING": + return ( + <> + + {fileName} + + ); + case "UPLOADING": + return ( + <> + 🚀 Uploading... + {fileName} + + ); + case "UPLOAD_FAILED": + return ( + <> + ❌ Upload Failed + {fileName} + + ); + case "ARCHIVED": + return ( + <> + ✅ Added! + + {file.skinType === "CLASSIC" ? ( + + {fileName} + + ) : file.skinType === "MODERN" ? ( + <> + {fileName} (Note: Modern skins are not yet visible + in the museum) + + ) : // TODO: Throw? + null} + + + ); + default: + console.error(`Unexpected file status: ${file.status}`); + return null; + } + }) + .map((rows, i) => ( + {rows} + ))} -
New skins ({missing.length.toLocaleString()})} - render={(file) => { - switch (file.status) { - case "MISSING": - return {file.file.name}; - case "UPLOADING": - return ( - <> - {file.file.name} (🚀 Uploading...) - - ); - case "UPLOAD_FAILED": - return ( - <> - {file.file.name} (❌ Upload Failed) - - ); - case "ARCHIVED": - return ( - <> - - {file.file.name} - {" "} - (✅ Added!) - - ); - default: - console.error(`Unexpected file status: ${file.status}`); - return null; - } - }} - />
Files that are not skins ({notSkins.length.toLocaleString()}) - } - render={(file) => {file.file.name}} - /> -
- Modern Skins (we're not accepting these yet) ( - {modernSkins.length.toLocaleString()}) + Files that are not valid skins ({notSkins.length.toLocaleString()}) } render={(file) => {file.file.name}} /> +
Already in the museum ({foundSkins.length.toLocaleString()}) } - render={(file) => ( - - {file.file.name} - - )} + render={(file) => { + if (file.skinType === "MODERN") { + return ( + <> + {file.file.name} (Note: Modern skins are not yet + visible in the museum) + + ); + } + return ( + + {file.file.name} + + ); + }} /> ); diff --git a/src/redux/actionCreators.js b/src/redux/actionCreators.js index 1bd1d3d1..44e94fd3 100644 --- a/src/redux/actionCreators.js +++ b/src/redux/actionCreators.js @@ -67,8 +67,12 @@ export function invalidFileExtension(id) { return { type: "INVALID_FILE_EXTENSION", id }; } -export function notClassicSkin(id) { - return { type: "NOT_CLASSIC_SKIN", id }; +export function invalidArchive(id) { + return { type: "INVALID_ARCHIVE", id }; +} + +export function gotSkinType(id, skinType) { + return { type: "GOT_SKIN_TYPE", id, skinType }; } export function gotFileMd5(id, md5) { diff --git a/src/redux/epics.js b/src/redux/epics.js index c3f4494d..e09df907 100644 --- a/src/redux/epics.js +++ b/src/redux/epics.js @@ -213,16 +213,17 @@ const uploadSingleFileEpic = (actions) => if (!UploadUtils.isValidSkinFilename(file.name)) { return of(Actions.invalidFileExtension(id)); } - return from(UploadUtils.isClassicSkin(file)) + return from(UploadUtils.getSkinType(file)) .pipe( - mergeMap((isClassic) => { - if (!isClassic) { - return of(Actions.notClassicSkin(id)); + mergeMap((skinType) => { + if (skinType === null) { + return of(Actions.invalidArchive(id)); } - return from(UploadUtils.hashFile(file)).pipe( - map((md5) => { - return Actions.gotFileMd5(id, md5); - }) + return concat( + of(Actions.gotSkinType(id, skinType)), + from(UploadUtils.hashFile(file)).pipe( + map((md5) => Actions.gotFileMd5(id, md5)) + ) ); }) ) @@ -235,8 +236,8 @@ const checkIfUploadsAreMissingEpic = (actions, state) => filter((action) => { return ( (action.type === "GOT_FILE_MD5" || - action.type === "NOT_CLASSIC_SKIN" || - action.type === "INVALID_FILE_EXTENSION") && + action.type === "INVALID_FILE_EXTENSION" || + action.type === "INVALID_ARCHIVE") && Selectors.getAreReadyToCheckMissingUploads(state.value) ); }), @@ -323,7 +324,6 @@ const loggingEpic = (actions, state) => case "TRY_TO_UPLOAD_ALL_FILES": case "INVALID_FILE_EXTENSION": case "GOT_FILE": - case "NOT_CLASSIC_SKIN": case "GOT_MISSING_AND_FOUND_MD5S": window.ga("send", "event", "redux", action.type); break; diff --git a/src/redux/reducer.js b/src/redux/reducer.js index a74a29e7..ef569fe5 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -66,8 +66,18 @@ export default function reducer(state = defaultState, action) { true ); } - case "NOT_CLASSIC_SKIN": { - return setUploadFileStatus(state, action.id, "NOT_CLASSIC_SKIN", true); + case "GOT_SKIN_TYPE": { + const previousFile = state.fileUploads[action.id]; + return { + ...state, + fileUploads: { + ...state.fileUploads, + [action.id]: { + ...previousFile, + skinType: action.skinType, + }, + }, + }; } case "STARTING_FILE_UPLOAD": { return setUploadFileStatus(state, action.id, "UPLOADING"); @@ -78,6 +88,8 @@ export default function reducer(state = defaultState, action) { case "ARCHIVED_SKIN": { return setUploadFileStatus(state, action.id, "ARCHIVED"); } + case "INVALID_ARCHIVE": + return setUploadFileStatus(state, action.id, "INVALID_ARCHIVE"); case "GOT_FILE_MD5": { return { ...state, @@ -232,7 +244,6 @@ export default function reducer(state = defaultState, action) { }, }; case "REQUESTED_PAGE": - console.log(action); return { ...state, activeContentPage: action.page, diff --git a/src/uploadUtils.js b/src/uploadUtils.js index 2b5bbb0a..916ab860 100644 --- a/src/uploadUtils.js +++ b/src/uploadUtils.js @@ -33,14 +33,19 @@ export function isValidSkinFilename(filename) { return validSkinFilename.test(filename); } -export async function isClassicSkin(file) { +export async function getSkinType(file) { const JSZip = await import("jszip"); try { const zip = await JSZip.loadAsync(file); - return zip.file(/main\.bmp$/i).length > 0; + if (zip.file(/main\.bmp$/i).length > 0) { + return "CLASSIC"; + } else if (zip.file(/skin\.xml$/i).length > 0) { + return "MODERN"; + } + return null; } catch (e) { // TODO: We could give a better message here. console.error(e); - return false; + return null; } } From 8cd744d3e3dcf35e7d315e8e5f608399719aa0e7 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 15 Sep 2020 22:36:52 -0700 Subject: [PATCH 4/4] Table based upload flow --- src/App.js | 2 +- src/UploadGrid.js | 215 -------------------------------- src/components/Metadata.js | 2 +- src/redux/epics.js | 2 +- src/upload/UploadGrid.js | 128 +++++++++++++++++++ src/upload/UploadRow.js | 117 +++++++++++++++++ src/upload/UploadSection.js | 34 +++++ src/{ => upload}/uploadUtils.js | 4 +- 8 files changed, 284 insertions(+), 220 deletions(-) delete mode 100644 src/UploadGrid.js create mode 100644 src/upload/UploadGrid.js create mode 100644 src/upload/UploadRow.js create mode 100644 src/upload/UploadSection.js rename src/{ => upload}/uploadUtils.js (92%) 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}

-
-
    - {files.map((match, i) => { - return
  • {render(match)}
  • ; - })} -
-
- ); -} - -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); }