uploader
This commit is contained in:
parent
7c228c3f4b
commit
4980a99ef2
|
@ -9,6 +9,7 @@
|
|||
"@fontsource/source-sans-pro": "^4.2.2",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"bytes": "^3.1.0",
|
||||
"classnames": "^2.2.6",
|
||||
"framer-motion": "^4.0.3",
|
||||
"gatsby": "^3.0.1",
|
||||
|
@ -26,16 +27,21 @@
|
|||
"gatsby-transformer-json": "^3.1.0",
|
||||
"gatsby-transformer-sharp": "^3.0.0",
|
||||
"gbimage-bridge": "^0.1.1",
|
||||
"http-status-codes": "^2.1.4",
|
||||
"normalize.css": "^8.0.1",
|
||||
"path-browserify": "^0.0.1",
|
||||
"polished": "^4.1.1",
|
||||
"popmotion": "^9.3.4",
|
||||
"postcss": "^8.2.8",
|
||||
"preact-svg-loader": "^0.2.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-dropzone": "^11.3.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-svg-loader": "^3.0.3",
|
||||
"react-use": "^17.2.1",
|
||||
"skynet-js": "^3.0.0",
|
||||
"tailwindcss": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./HeroStartPage";
|
|
@ -0,0 +1,14 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="66" height="66" viewBox="0 0 66 66">
|
||||
<defs>
|
||||
<filter id="add-a">
|
||||
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 1.000000 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd" transform="translate(-.097 .903)">
|
||||
<circle cx="33.097" cy="33.097" r="32" fill="#00C65E"/>
|
||||
<path stroke="#0D0D0D" stroke-width="2" d="M59.7279532,50.4620828 C62.9634424,45.4615529 64.8418319,39.5010119 64.8418319,33.1017494 C64.8418319,15.4286374 50.5149439,1.1017494 32.8418319,1.1017494 C15.1687199,1.1017494 0.84183186,15.4286374 0.84183186,33.1017494" transform="rotate(-2 32.842 25.782)"/>
|
||||
<g filter="url(#add-a)" transform="translate(13.097 13.097)">
|
||||
<path fill="#FFF" d="M21,12 L21,19 L28,19 L28,21 L21,21 L21,28 L19,28 L19,21 L12,21 L12,19 L19,19 L19,12 L21,12 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 912 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
|
||||
<path fill="#0D0D0D" d="M20,4 C28.836,4 36,11.164 36,20 C36,28.836 28.836,36 20,36 C11.164,36 4,28.836 4,20 C4,11.164 11.164,4 20,4 Z M20,6 C12.2685695,6 6,12.2685695 6,20 C6,27.7314305 12.2685695,34 20,34 C27.7314305,34 34,27.7314305 34,20 C34,12.2685695 27.7314305,6 20,6 Z M19.4363719,13.4966307 C19.8299265,13.1934645 20.3968364,13.2236414 20.7557217,13.5855059 L20.7557217,13.5855059 L25.7100234,18.5809324 L24.2899766,19.9892886 L21,16.673 L21,26 L19,26 L19,16.733 L15.7038828,20.0000001 L14.2961172,18.5793679 L19.3418155,13.5793679 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 638 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
|
||||
<path fill="#0D0D0D" d="M20,4 C28.836,4 36,11.164 36,20 C36,28.836 28.836,36 20,36 C11.164,36 4,28.836 4,20 C4,11.164 11.164,4 20,4 Z M20,6 C12.2685695,6 6,12.2685695 6,20 C6,27.7314305 12.2685695,34 20,34 C27.7314305,34 34,27.7314305 34,20 C34,12.2685695 27.7314305,6 20,6 Z M26.0574126,15.2928932 L27.4716262,16.7071068 L18.0464454,26.1322875 L13.2928932,21.3787353 L14.7071068,19.9645218 L18.046,23.303 L26.0574126,15.2928932 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 527 B |
|
@ -0,0 +1,20 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="62" height="54" viewBox="0 0 62 54">
|
||||
<defs>
|
||||
<filter id="cloud-a">
|
||||
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 1.000000 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#00C65E" points="25.097 46.291 25.097 32 19.354 32 30 21 40.343 32 35 32 35 50"/>
|
||||
<g filter="url(#cloud-a)">
|
||||
<path stroke="#222829" stroke-width="2" d="M41 40L50 40C56.627 40 62 34.628 62 28 62 22.053 58 17 52 17 52 12 49 8 43 8L42 8C39.272 3.076 34.028 0 28 0 19.164 0 12 7.164 12 16 5.373 16 0 21.373 0 28 0 34.628 5.373 40 12 40L21 40M18 17C18 10.925 23 6 28 6"/>
|
||||
<polyline stroke="#222829" stroke-linejoin="round" stroke-width="2" points="25 54 25 32 19 32 30 21 41 32 35 32 35 54"/>
|
||||
<line x1="30" x2="30" y1="31" y2="33" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
<line x1="30" x2="30" y1="35" y2="37" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
<line x1="30" x2="30" y1="39" y2="41" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
<line x1="30" x2="30" y1="43" y2="45" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
<line x1="30" x2="30" y1="47" y2="49" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
<line x1="30" x2="30" y1="51" y2="53" stroke="#222829" stroke-linejoin="round" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -1,5 +1,9 @@
|
|||
export { default as Add } from "./Add.svg";
|
||||
export { default as ArrowRight } from "./ArrowRight.svg";
|
||||
export { default as ArrowUpCircle } from "./ArrowUpCircle.svg";
|
||||
export { default as CheckActive } from "./CheckActive.svg";
|
||||
export { default as CheckCircle } from "./CheckCircle.svg";
|
||||
export { default as Cloud } from "./Cloud.svg";
|
||||
export { default as DiscordSmall } from "./DiscordSmall.svg";
|
||||
export { default as DiscordSmallWhite } from "./DiscordSmallWhite.svg";
|
||||
export { default as LogoBlackText } from "./LogoBlackText.svg";
|
||||
|
|
|
@ -15,16 +15,26 @@ import Navigation from "../Navigation/Navigation";
|
|||
import NewsHeader from "../NewsHeader/NewsHeader";
|
||||
import Footer from "../Footer/Footer";
|
||||
import FooterNavigation from "../FooterNavigation/FooterNavigation";
|
||||
// import { useWindowScroll } from "react-use";
|
||||
import { useWindowScroll } from "react-use";
|
||||
import classnames from "classnames";
|
||||
import { readableColor } from "polished";
|
||||
|
||||
const modeMap = { "#fff": "dark", "#000": "light" };
|
||||
|
||||
const StickyHeader = () => {
|
||||
// const { y } = useWindowScroll();
|
||||
useWindowScroll();
|
||||
|
||||
const ref = React.useRef(null);
|
||||
const element = document.elementFromPoint(0, ref.current?.offsetHeight ?? 0);
|
||||
|
||||
const backgroundColor = window.getComputedStyle(element, null).getPropertyValue("background-color");
|
||||
const color = React.useMemo(() => readableColor(backgroundColor), [backgroundColor]);
|
||||
const mode = modeMap[color];
|
||||
|
||||
return (
|
||||
<div className={classnames("sticky top-0", { "bg-white border-b border-palette-200": false })}>
|
||||
<div ref={ref} className={classnames("sticky top-0 z-50", { "bg-white border-b border-palette-200": false })}>
|
||||
<NewsHeader />
|
||||
<Navigation mode={false ? "light" : "dark"} />
|
||||
<Navigation mode={mode} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Layout";
|
|
@ -7,6 +7,8 @@ import LogoBlackText from "../Icons/LogoBlackText.svg";
|
|||
import MenuMobile from "../Icons/MenuMobile.svg";
|
||||
import MenuMobileClose from "../Icons/MenuMobileClose.svg";
|
||||
import DiscordSmall from "../Icons/DiscordSmall.svg";
|
||||
import { motion } from "framer-motion";
|
||||
import { useWindowSize, useWindowScroll } from "react-use";
|
||||
|
||||
const routes = [
|
||||
{ title: "Home", route: "/" },
|
||||
|
@ -48,17 +50,45 @@ const SignUpButton = ({ className, ...props }) => (
|
|||
);
|
||||
|
||||
const Navigation = ({ mode }) => {
|
||||
const navRef = React.useRef(null);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const windowSize = useWindowSize();
|
||||
const { y: offsetY } = useWindowScroll();
|
||||
|
||||
React.useEffect(() => {
|
||||
setOpen(false);
|
||||
}, [windowSize, setOpen]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open && document.body.style.overflow !== "hidden") {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else if (document.body.style.overflow === "hidden") {
|
||||
document.body.style.overflow = "unset";
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const mobileMenuOffset = navRef.current ? navRef.current.offsetTop : 0;
|
||||
|
||||
return (
|
||||
<nav className={classnames("relative px-8 py-12", { "bg-palette-600": open }, "desktop:bg-transparent")}>
|
||||
<div className="max-w-layout mx-auto">
|
||||
<motion.nav
|
||||
className={classnames("relative px-8 transition-colors duration-500", {
|
||||
"bg-white border-b border-palette-200": mode === "light",
|
||||
"bg-palette-600 bg-opacity-50": mode === "dark",
|
||||
})}
|
||||
initial={false}
|
||||
animate={{
|
||||
paddingTop: offsetY ? "24px" : "48px",
|
||||
paddingBottom: offsetY ? "24px" : "48px",
|
||||
}}
|
||||
ref={navRef}
|
||||
>
|
||||
<div className={classnames("max-w-layout mx-auto")}>
|
||||
<div className="flex justify-between">
|
||||
<Link to="/" className={classnames("flex flex-shrink-0 items-center", { hidden: open }, "desktop:flex")}>
|
||||
{mode === "dark" && <LogoWhiteText className="h-8 w-auto" />}
|
||||
{mode === "light" && <LogoBlackText className="h-8 w-auto" />}
|
||||
</Link>
|
||||
<div className="ml-auto flex items-center desktop:hidden">
|
||||
<div className="ml-auto flex items-center desktop:hidden z-10">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
|
||||
|
@ -71,12 +101,13 @@ const Navigation = ({ mode }) => {
|
|||
<MenuMobileClose className={classnames({ hidden: !open })} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="hidden desktop:ml-6 desktop:flex desktop:items-center desktop:space-x-12">
|
||||
{routes.map(({ title, route }) => (
|
||||
<Link
|
||||
key={route}
|
||||
to={route}
|
||||
className={classnames("text-sm font-light", {
|
||||
className={classnames("text-sm font-light transition-colors duration-500", {
|
||||
"text-white": mode === "dark",
|
||||
"text-palette-600": mode === "light",
|
||||
})}
|
||||
|
@ -88,15 +119,15 @@ const Navigation = ({ mode }) => {
|
|||
<SignUpButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classnames("absolute bg-palette-600 inset-x-0 px-8 pb-12 desktop:hidden", {
|
||||
className={classnames("fixed bg-palette-600 inset-0 px-8 py-12 desktop:hidden", {
|
||||
block: open,
|
||||
hidden: !open,
|
||||
})}
|
||||
id="mobile-menu"
|
||||
style={{ marginTop: mobileMenuOffset }}
|
||||
>
|
||||
<ul className="pt-4 pb-10 space-y-2">
|
||||
<ul className="py-10 space-y-2">
|
||||
{routes.map(({ title, route }) => (
|
||||
<li key={title}>
|
||||
<Link key={route} to={route} className="text-xl leading-7 font-semibold text-white">
|
||||
|
@ -117,13 +148,14 @@ const Navigation = ({ mode }) => {
|
|||
</a>
|
||||
</div>
|
||||
<div className="pt-12 pb-8 border-t border-palette-500">
|
||||
<div className="flex items-center px-4 space-x-6">
|
||||
<LogInButton className="flex-grow" />
|
||||
<SignUpButton className="flex-grow" />
|
||||
<div className="flex items-center justify-center px-4 space-x-6">
|
||||
<LogInButton className="px-10" />
|
||||
<SignUpButton className="px-10" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</motion.nav>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
import * as React from "react";
|
||||
import classnames from "classnames";
|
||||
import { Add, Cloud, ArrowUpCircle, CheckCircle } from "../Icons";
|
||||
import bytes from "bytes";
|
||||
import classNames from "classnames";
|
||||
import { getReasonPhrase, StatusCodes } from "http-status-codes";
|
||||
import path from "path-browserify";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { SkynetClient } from "skynet-js";
|
||||
|
||||
const getFilePath = (file) => file.webkitRelativePath || file.path || file.name;
|
||||
|
||||
const getRelativeFilePath = (file) => {
|
||||
const filePath = getFilePath(file);
|
||||
const { root, dir, base } = path.parse(filePath);
|
||||
const relative = path.normalize(dir).slice(root.length).split(path.sep).slice(1);
|
||||
|
||||
return path.join(...relative, base);
|
||||
};
|
||||
|
||||
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 createUploadErrorMessage = (error) => {
|
||||
// The request was made and the server responded with a status code that falls out of the range of 2xx
|
||||
if (error.response) {
|
||||
if (error.response.data.message) {
|
||||
return `Upload failed with error: ${error.response.data.message}`;
|
||||
}
|
||||
|
||||
const statusCode = error.response.status;
|
||||
const statusText = getReasonPhrase(error.response.status);
|
||||
|
||||
return `Upload failed, our server received your request but failed with status code: ${statusCode} ${statusText}`;
|
||||
}
|
||||
|
||||
// The request was made but no response was received. The best we can do is detect whether browser is online.
|
||||
// This will be triggered mostly if the server is offline or misconfigured and doesn't respond to valid request.
|
||||
if (error.request) {
|
||||
if (!navigator.onLine) {
|
||||
return "You are offline, please connect to the internet and try again";
|
||||
}
|
||||
|
||||
// TODO: We should add a note "our team has been notified" and have some kind of notification with this error.
|
||||
return "Server failed to respond to your request, please try again later.";
|
||||
}
|
||||
|
||||
// TODO: We should add a note "our team has been notified" and have some kind of notification with this error.
|
||||
return `Critical error, please refresh the application and try again. ${error.message}`;
|
||||
};
|
||||
|
||||
const client = new SkynetClient("https://siasky.net");
|
||||
|
||||
const UploadElement = ({ file, progress, status, url }) => {
|
||||
const handleCopy = (url) => {
|
||||
console.log(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
{status === "uploading" && <ArrowUpCircle />}
|
||||
{status === "processing" && <ArrowUpCircle />}
|
||||
{status === "complete" && <CheckCircle />}
|
||||
<div className="flex flex-col flex-grow ml-3">
|
||||
<div className="text-palette-600 text-sm font-light">{file.name}</div>
|
||||
<div className="flex justify-between text-palette-400 text-xs">
|
||||
<div className="font-content">
|
||||
{status === "uploading" && (
|
||||
<span>
|
||||
Uploading {bytes(file.size * progress)} of {bytes(file.size)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{status === "processing" && <span>Processing...</span>}
|
||||
|
||||
{status === "complete" && (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||
{url}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{status === "uploading" && <span className="uppercase">{Math.floor(progress * 100)}% completed</span>}
|
||||
{status === "complete" && (
|
||||
<button className="uppercase" onClick={() => handleCopy(url)}>
|
||||
Copy link
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex bg-palette-200 mt-1" style={{ height: "5px" }}>
|
||||
<div style={{ width: `${Math.floor(progress * 100)}%` }} className="bg-primary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Uploader = () => {
|
||||
const [mode, setMode] = React.useState("file");
|
||||
const [files, setFiles] = React.useState([]);
|
||||
|
||||
const handleDrop = async (acceptedFiles) => {
|
||||
if (mode === "directory" && acceptedFiles.length) {
|
||||
const rootDir = getRootDirectory(acceptedFiles[0]); // get the file path from the first file
|
||||
|
||||
acceptedFiles = [{ name: rootDir, directory: true, files: acceptedFiles }];
|
||||
}
|
||||
|
||||
setFiles((previousFiles) => [...acceptedFiles.map((file) => ({ file, status: "uploading" })), ...previousFiles]);
|
||||
|
||||
const onFileStateChange = (file, state) => {
|
||||
setFiles((previousFiles) => {
|
||||
const index = previousFiles.findIndex((f) => f.file === file);
|
||||
|
||||
return [
|
||||
...previousFiles.slice(0, index),
|
||||
{
|
||||
...previousFiles[index],
|
||||
...state,
|
||||
},
|
||||
...previousFiles.slice(index + 1),
|
||||
];
|
||||
});
|
||||
};
|
||||
|
||||
acceptedFiles.forEach((file) => {
|
||||
const onUploadProgress = (progress) => {
|
||||
const status = progress === 1 ? "processing" : "uploading";
|
||||
|
||||
onFileStateChange(file, { status, progress });
|
||||
};
|
||||
|
||||
// Reject files larger than our hard limit of 1 GB with proper message
|
||||
if (file.size > bytes("1 GB")) {
|
||||
onFileStateChange(file, { status: "error", error: "This file size exceeds the maximum allowed size of 1 GB." });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const upload = async () => {
|
||||
try {
|
||||
let response;
|
||||
|
||||
if (file.directory) {
|
||||
const directory = file.files.reduce((acc, file) => ({ ...acc, [getRelativeFilePath(file)]: file }), {});
|
||||
|
||||
response = await client.uploadDirectory(directory, encodeURIComponent(file.name), { onUploadProgress });
|
||||
} else {
|
||||
response = await client.uploadFile(file, { onUploadProgress });
|
||||
}
|
||||
|
||||
onFileStateChange(file, { status: "complete", url: client.getSkylinkUrl(response.skylink) });
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === StatusCodes.TOO_MANY_REQUESTS) {
|
||||
onFileStateChange(file, { progress: -1 });
|
||||
|
||||
return new Promise((resolve) => setTimeout(() => resolve(upload()), 3000));
|
||||
}
|
||||
|
||||
onFileStateChange(file, { status: "error", error: createUploadErrorMessage(error) });
|
||||
}
|
||||
};
|
||||
|
||||
upload();
|
||||
});
|
||||
};
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive, inputRef } = useDropzone({ onDrop: handleDrop });
|
||||
const inputElement = inputRef.current;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (mode === "directory") {
|
||||
inputElement.setAttribute("webkitdirectory", "true");
|
||||
} else {
|
||||
inputElement.removeAttribute("webkitdirectory");
|
||||
}
|
||||
}, [mode, inputElement]);
|
||||
|
||||
return (
|
||||
<div className="px-8 py-12">
|
||||
<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-3 rounded-tl-lg 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-3 rounded-tr-lg leading-8", {
|
||||
"bg-primary": mode === "directory",
|
||||
"bg-palette-200": mode === "file",
|
||||
})}
|
||||
onClick={() => setMode("directory")}
|
||||
>
|
||||
<span className="hidden desktop:inline">Do you want to upload an entire 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()}
|
||||
>
|
||||
<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,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Cloud />
|
||||
<h4 className="font-light text-palette-600 text-lg mt-2 text-center">
|
||||
Add or drop your files here to pin to Skynet
|
||||
</h4>
|
||||
</div>
|
||||
<div className="absolute left-1/2 -bottom-4 desktop:-bottom-8">
|
||||
<div className="relative -left-1/2" role="button">
|
||||
<Add />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{files.length > 0 && (
|
||||
<div className="flex flex-col space-y-5 p-14">
|
||||
{files.map((file, index) => (
|
||||
<UploadElement key={index} {...file} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Uploader.propTypes = {};
|
||||
|
||||
Uploader.defaultProps = {};
|
||||
|
||||
export default Uploader;
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Uploader";
|
|
@ -1,9 +1,10 @@
|
|||
import * as React from "react";
|
||||
// import { StaticImage } from "gatsby-plugin-image";
|
||||
import Layout from "../components/Layout/Layout";
|
||||
import Layout from "../components/Layout";
|
||||
import SEO from "../components/seo";
|
||||
import HeroStartPage from "../components/HeroStartPage/HeroStartPage";
|
||||
import HeroStartPage from "../components/HeroStartPage";
|
||||
import CommunitySection from "../components/CommunitySection";
|
||||
import Uploader from "../components/Uploader";
|
||||
import {
|
||||
ArrowRight,
|
||||
SkynetToolBig,
|
||||
|
@ -17,8 +18,8 @@ import {
|
|||
} from "../components/Icons";
|
||||
import classnames from "classnames";
|
||||
|
||||
const Section = ({ children, className }) => (
|
||||
<div className={classnames("px-8 p-3", className)}>
|
||||
const Section = ({ children, className, ...props }) => (
|
||||
<div className={classnames("p-8", className)} {...props}>
|
||||
<div className="max-w-content mx-auto">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -57,10 +58,15 @@ const IndexPage = () => (
|
|||
<Layout>
|
||||
<SEO title="Home" />
|
||||
|
||||
<Section className="py-24">
|
||||
<Section>
|
||||
<HeroStartPage />
|
||||
</Section>
|
||||
|
||||
<Section className="relative">
|
||||
{/* <div className="absolute inset-x-0 bg-white bottom-0" style={{ top: 256 }}></div> */}
|
||||
<Uploader />
|
||||
</Section>
|
||||
|
||||
<Section className="bg-white py-32">
|
||||
<SectionTitle className="text-center mb-11">The new decentralized internet is here</SectionTitle>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module.exports = {
|
|||
tablet: "640px",
|
||||
md: "768px",
|
||||
lg: "1024px",
|
||||
desktop: "1088px",
|
||||
desktop: "1024px",
|
||||
xl: "1280px",
|
||||
hires: "1408px",
|
||||
"2xl": "1536px",
|
||||
|
@ -32,6 +32,9 @@ module.exports = {
|
|||
sans: ["Sora", ...defaultTheme.fontFamily.sans],
|
||||
content: ["Source\\ Sans\\ Pro", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
fontSize: {
|
||||
xxs: ["0.625rem", "0.75rem"],
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: "#00c65e",
|
||||
|
@ -51,6 +54,7 @@ module.exports = {
|
|||
},
|
||||
variants: {
|
||||
extend: {
|
||||
animation: ["hover"],
|
||||
backgroundColor: ["disabled"],
|
||||
textColor: ["disabled"],
|
||||
margin: ["first"],
|
||||
|
|
Reference in New Issue