This repository has been archived on 2022-10-07. You can view files and clone it, but cannot push or open issues or pull requests.
skynet-webportal/packages/website/src/components/Uploader/Uploader.js

211 lines
7.5 KiB
JavaScript

import * as React from "react";
import classnames from "classnames";
import { Add, Cloud, Unlock, Info } from "../Icons";
import classNames from "classnames";
import path from "path-browserify";
import { useDropzone } from "react-dropzone";
import { nanoid } from "nanoid";
import useAccounts from "../../services/useAccounts";
import Link from "../Link";
import UploaderElement from "./UploaderElement";
const MAX_PARALLEL_UPLOADS = 1;
const getFilePath = (file) => file.webkitRelativePath || file.path || file.name;
const getRootDirectory = (file) => {
const filePath = getFilePath(file);
const { root, dir } = path.parse(filePath);
return path.normalize(dir).slice(root.length).split(path.sep)[0];
};
const RegistrationLink = () => (
<Link
href="https://account.siasky.net/auth/registration"
className="uppercase underline-primary hover:text-primary transition-colors duration-200"
>
Sign up
</Link>
);
const LogInLink = () => (
<Link
href="https://account.siasky.net/auth/login"
className="uppercase underline-primary hover:text-primary transition-colors duration-200"
>
Log in
</Link>
);
const Uploader = () => {
const [mode, setMode] = React.useState("file");
const [uploads, setUploads] = React.useState([]);
const { data: accounts } = useAccounts();
const showAccountFeatures = accounts?.enabled && !accounts?.auth_required && !accounts?.authenticated;
const disabledComponent = accounts?.enabled && accounts?.auth_required && !accounts?.authenticated;
const onUploadStateChange = React.useCallback((id, state) => {
setUploads((uploads) => {
const index = uploads.findIndex((upload) => upload.id === id);
return [...uploads.slice(0, index), { ...uploads[index], ...state }, ...uploads.slice(index + 1)];
});
}, []);
const handleDrop = async (files) => {
if (mode === "directory" && files.length) {
const name = getRootDirectory(files[0]); // get the file path from the first file
const size = files.reduce((acc, file) => acc + file.size, 0);
files = [{ name, size, files }];
}
setUploads((uploads) => [...files.map((file) => ({ id: nanoid(), file, mode, status: "enqueued" })), ...uploads]);
};
React.useEffect(() => {
const enqueued = uploads.filter(({ status }) => status === "enqueued");
const uploading = uploads.filter(({ status }) => ["uploading", "processing", "retrying"].includes(status));
const queue = enqueued.slice(0, MAX_PARALLEL_UPLOADS - uploading.length).map(({ id }) => id);
if (queue.length && uploading.length < MAX_PARALLEL_UPLOADS) {
setUploads((uploads) => {
return uploads.map((upload) => {
if (queue.includes(upload.id)) return { ...upload, status: "uploading" };
return upload;
});
});
}
}, [uploads]);
const { getRootProps, getInputProps, isDragActive, inputRef } = useDropzone({ onDrop: handleDrop });
const inputElement = inputRef.current;
React.useEffect(() => {
if (!inputElement) return;
if (mode === "directory") inputElement.setAttribute("webkitdirectory", "true");
if (mode === "file") inputElement.removeAttribute("webkitdirectory");
}, [inputElement, mode]);
return (
<div className={classnames("relative", { "p-8": disabledComponent })}>
<div className="max-w-content mx-auto rounded-lg shadow bg-white z-0 relative">
<div className="flex">
<button
className={classnames(
"uppercase text-xxs desktop:text-xs w-1/2 p-2 desktop:p-3 rounded-tl-lg leading-6 desktop:leading-8",
{
"bg-primary": mode === "file",
"bg-palette-200": mode === "directory",
}
)}
onClick={() => setMode("file")}
>
<span className="hidden desktop:inline">Try it now and upload your files</span>
<span className="inline desktop:hidden">Upload files</span>
</button>
<button
className={classnames(
"uppercase text-xxs desktop:text-xs w-1/2 p-2 desktop:p-3 rounded-tr-lg leading-6 desktop:leading-8",
{
"bg-primary": mode === "directory",
"bg-palette-200": mode === "file",
}
)}
onClick={() => setMode("directory")}
>
<span className="hidden desktop:inline">Do you want to upload a web app or directory?</span>
<span className="inline desktop:hidden">Upload directory</span>
</button>
</div>
<div
className={classNames("p-4 relative home-upload-dropzone", {
"drop-active": isDragActive,
})}
{...getRootProps()}
disabled={true}
>
<input {...getInputProps()} />
<div
className={classnames(
"p-8 border-2 border-dashed border-palette-200 rounded-lg flex flex-col items-center",
{
"bg-palette-100": isDragActive,
}
)}
>
{uploads.length === 0 && <Cloud />}
<h4 className="font-light text-palette-600 text-lg mt-2 text-center">
{mode === "file" && <span>Add or drop your files here to pin to Skynet</span>}
{mode === "directory" && <span>Drop any folder with an index.html file to deploy to Skynet</span>}
</h4>
</div>
{!disabledComponent && (
<div className="absolute left-1/2 -bottom-4 desktop:-bottom-8">
<div className="relative -left-1/2 transform transition-transform hover:rotate-180" role="button">
<Add />
</div>
</div>
)}
</div>
{uploads.length > 0 && (
<div className="flex flex-col space-y-5 px-4 py-10 desktop:p-14">
{uploads.map((upload) => (
<UploaderElement key={upload.id} onUploadStateChange={onUploadStateChange} upload={upload} />
))}
{showAccountFeatures && (
<div className="z-0 relative flex flex-col items-center space-y-1 pt-8">
<Info />
{/* mobile - 2 lines */}
<p className="text-sm font-light text-palette-600 desktop:hidden">
Your files are available for 90 days
</p>
<p className="text-sm font-light text-palette-600 desktop:hidden">
<RegistrationLink /> to keep them forever
</p>
{/* desktop - 1 line */}
<p className="text-sm font-light text-palette-600 hidden desktop:block">
Your files are available for 90 days, <RegistrationLink /> to keep them forever
</p>
</div>
)}
</div>
)}
</div>
{uploads.length === 0 && showAccountFeatures && (
<div className="z-0 relative flex flex-col items-center space-y-1 mt-10">
<Unlock />
<p className="text-sm font-light text-palette-600">
<RegistrationLink /> for free and unlock features
</p>
</div>
)}
{disabledComponent && (
<div className="absolute inset-0 bg-palette-500 bg-opacity-90 rounded-lg">
<div class="flex h-full">
<div class="m-auto">
<h4 className="font-light text-palette-100 text-lg mt-2 text-center">
<LogInLink /> or <RegistrationLink /> for free
</h4>
</div>
</div>
</div>
)}
</div>
);
};
Uploader.propTypes = {};
Uploader.defaultProps = {};
export default Uploader;