import React, { useState, useContext, useEffect } from "react"; import classNames from "classnames"; import path from "path-browserify"; import { useDropzone } from "react-dropzone"; import Reveal from "react-reveal/Reveal"; import { Button, UploadFile } from "../"; import { Deco3, Deco4, Deco5, Folder, DownArrow } from "../../svg"; import "./HomeUpload.scss"; import AppContext from "../../AppContext"; import axios from "axios"; export default function HomeUpload() { const [files, setFiles] = useState([]); const { apiUrl } = useContext(AppContext); const [directoryMode, setDirectoryMode] = useState(false); useEffect(() => { if (directoryMode) { inputRef.current.setAttribute("webkitdirectory", "true"); } else { inputRef.current.removeAttribute("webkitdirectory"); } }, [directoryMode]); 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 handleDrop = async (acceptedFiles) => { if (directoryMode && 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) ]; }); }; const upload = async (formData, directory, file) => { const uploadUrl = `${apiUrl}/skynet/skyfile/${directory ? `?filename=${encodeURIComponent(directory)}` : ""}`; const { data } = await axios.post(uploadUrl, formData, { onUploadProgress: ({ loaded, total }) => { const progress = loaded / total; const status = progress === 1 ? "processing" : "uploading"; onFileStateChange(file, { status, progress }); } }); return data; }; acceptedFiles.forEach(async (file) => { try { const formData = new FormData(); if (file.directory) { file.files.forEach((directoryFile) => { const relativeFilePath = getRelativeFilePath(directoryFile); formData.append("files[]", directoryFile, relativeFilePath); }); } else { formData.append("file", file); } const { skylink } = await upload(formData, directoryMode && file.name, file); onFileStateChange(file, { status: "complete", url: `${apiUrl}/${skylink}` }); } catch (error) { onFileStateChange(file, { status: "error" }); } }); }; const { getRootProps, getInputProps, isDragActive, inputRef } = useDropzone({ onDrop: handleDrop }); const handleSkylink = (event) => { event.preventDefault(); const skylink = event.target.skylink.value.replace("sia://", ""); if (skylink.match(/^[a-zA-Z0-9_-]{46}$/)) { window.open(skylink, "_blank"); } }; return ( <Reveal effect="active"> <div className="home-upload"> <div className="home-upload-white fadeInUp delay4"> <div className="home-upload-split"> <div className="home-upload-box "> <div className={classNames("home-upload-dropzone", { "drop-active": isDragActive })} {...getRootProps()} > <span className="home-upload-text"> <h3>Upload your {directoryMode ? "Directory" : "Files"}</h3> Drop your {directoryMode ? "directory" : "files"} here to pin to Skynet </span> <Button iconLeft> <Folder /> Browse </Button> </div> <input {...getInputProps()} className="offscreen" /> <button type="button" className="home-upload-mode-switch link" onClick={() => setDirectoryMode(!directoryMode)} > {directoryMode ? "⇐ Switch back to uploading files" : "Do you want to upload entire directory?"} </button> {directoryMode && ( <p className="home-upload-directory-mode-notice"> Please note that directory upload is not a standard browser feature and the browser support is limited. To check whether your browser is compatible, visit{" "} <a href="https://caniuse.com/#feat=mdn-api_htmlinputelement_webkitdirectory" target="_blank" rel="noopener noreferrer" className="link" > caniuse.com </a> . </p> )} </div> <div className="home-upload-retrieve"> <div className="home-upload-text"> <h3 id="skylink-retrieve-title">Have a Skylink?</h3> <p>Paste the link to retrieve your file</p> <form className="home-upload-retrieve-form" onSubmit={handleSkylink}> <input name="skylink" type="text" placeholder="sia://" aria-labelledby="skylink-retrieve-title" /> <button type="submit" aria-label="Retrieve file"> <DownArrow /> </button> </form> </div> </div> </div> {files.length > 0 && ( <div className="home-uploaded-files"> {files.map((file, i) => { return <UploadFile key={i} {...file} />; })} </div> )} </div> <p className="bottom-text fadeInUp delay5"> Upon uploading a file, Skynet generates a 46 byte link called a <strong>Skylink</strong>. This link can then be shared with anyone to retrieve the file on any Skynet Webportal. </p> <Deco3 className="deco-3 fadeInUp delay6" /> <Deco4 className="deco-4 fadeInUp delay6" /> <Deco5 className="deco-5 fadeInUp delay6" /> </div> </Reveal> ); }