import React, {useEffect, useState, useContext} from "react"; import {useLocation} from "react-router-dom"; import {encode} from "base64-arraybuffer"; import {getSubreactFromLocation} from "helpers/Location.jsx"; import {UserContext} from "contexts/UserContext.jsx"; import {StateContext} from "contexts/StateContext.jsx"; import Card from "components/card/Card.jsx"; import Button from "components/button/Button.jsx"; import Input from "components/input/Input.jsx"; import Loading from "components/loading/Loading.jsx"; import styles from "./Submission.css"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faMinus, faPlus} from "@fortawesome/free-solid-svg-icons"; import {getFieldError, correctFieldAnimate, isSupportedExtension} from "helpers/Input.jsx"; const maxFileSize = 4 * 1000 * 1000; // 4MB max size const validateTitle = (title) => getFieldError(title?.target?.value, [3, 128]); const validateContents = (contents) => getFieldError(contents?.target?.value, [5, 3000]); function validateFile(file) { const files = file?.target?.files; if (files == null || files.length == 0) { return "Required"; } const f = files.item(0); if (f.size > maxFileSize) { // 4MB return "File too large, exceeded 4MB"; } const ext = f.name.split(".")?.pop()?.toLowerCase(); if (!isSupportedExtension(ext)) { return "Unsupported image extension"; } return null; } function ErrorMessage({message}) { return (

{message}

); } function RequestButton({promise, setPromise, title, setTitle, contents, setContents, file, setFile, setError, className, children, setSubmissionActive, subreact}) { const onClick = (event) => { event.preventDefault(); if (promise != null) { return; } setError(null); const titleErrors = validateTitle(title); const contentsErrors = validateContents(contents); const fileErrors = validateFile(file); if (titleErrors != null || contentsErrors != null || fileErrors != null) { correctFieldAnimate(title, setTitle, titleErrors); correctFieldAnimate(contents, setContents, contentsErrors); correctFieldAnimate(file, setFile, fileErrors); return; } setPromise(file.target.files.item(0).arrayBuffer() .then((buffer) => { const init = { method: "POST", headers: { 'Content-Type': "application/json" }, body: JSON.stringify({ "title": title.target.value, "contents": contents.target.value, "subreact": subreact, "file": encode(buffer), }), referrer: "same-origin", }; return fetch("/api/post", init); }) .then((response) => { if (response.ok) { setSubmissionActive(false); setContents(null); setTitle(null); setFile(null); return; } return response.json() .then((json) => { if ("error" in json) { throw new Error(json.error); } throw new Error("unexpected data received"); }); }) .catch((error) => { console.log(error); setError(error.message); }) .finally(() => { setPromise(null); })); } return ( ); } function TabIcon({icon, submissionActive, isDefault}) { return ( ); } function Tab({submissionActive, setSubmissionActive, user, setLoginActive}) { const [animate, setAnimate] = useState(false); const onClick = () => { if (user === null) { setLoginActive(true); return; } setAnimate(true); setSubmissionActive(!submissionActive); }; return (
); } function SubmissionForm({setSubmissionActive, setError, subreact}) { const [promise, setPromise] = useState(null); const [title, setTitle] = useState(null); const [contents, setContents] = useState(null); const [file, setFile] = useState(null); return (
Submit
); } export default function Submission({active, setActive}) { const [error, setError] = useState(null); const location = useLocation(); const subreact = getSubreactFromLocation(location); const [user] = useContext(UserContext); const [state, setState] = useContext(StateContext); const setLoginActive = (active) => {setState({...state, loginActive: active})}; // If our subreact is the front page, disable our ability to post. Posting // should be done to the main subreact. useEffect(() => { if (subreact === "") { setActive(false); } }, [location]); return (

Make a new submission

); }