import React, { useCallback, useRef, useState } from "react"; import useOutsideClick from "@rooks/use-outside-click"; import useTimeout from "@rooks/use-timeout"; import classNames from "classnames"; import { addPost, addProposal } from "actions/posts"; import Button from "components/Button"; import { Card, CardBody } from "components/cards"; import ErrorMessage from "components/ErrorMessage"; import MarkdownEditor from "components/mde/MarkdownEditor"; import { useActionState } from "hooks"; const AddPostForm = ({ className, canAddProposal }) => { const cardRef = useRef(); const editorRef = useRef(); const [expanded, setExpanded] = useState(false); const [text, setText] = useState(""); const [showAddConfirm, setShowAddConfirm] = useState(false); const [type, setType] = useState("post"); const [error, setError] = useState(null); const [addingPost, addingPostError] = useActionState(addPost, { content: text, }); const [addingProposal, addingProposalError] = useActionState(addProposal, { content: text, }); const apiError = addingPostError || addingProposalError; const is429ApiError = apiError && apiError.toString().indexOf("Unexpected status code 429") !== -1; const onOutsideClick = useCallback(() => { setExpanded(false); }, [setExpanded]); const onWrite = useCallback( (evt) => { setShowAddConfirm(false); if (!expanded) { setExpanded(true); setTimeout(() => { if ( editorRef.current && editorRef.current.finalRefs.textarea.current ) { editorRef.current.finalRefs.textarea.current.focus(); } }, 0); } }, [setExpanded, expanded, setShowAddConfirm] ); const hideAddConfirm = useCallback(() => { setShowAddConfirm(false); }, [setShowAddConfirm]); useOutsideClick(cardRef, onOutsideClick); const { start: enqueueHideAddConfirm } = useTimeout(hideAddConfirm, 2000); const onTextInput = (newText) => { setText(newText); if (newText !== "") { if (newText.length >= 1024) { setError("Maximální délka příspěvku je 1024 znaků."); } else { setError(null); } } }; const onAdd = async (evt) => { evt.preventDefault(); if (!!text) { if (!error) { const result = await (type === "post" ? addPost : addProposal).run({ content: text, }); if (!result.error) { setText(""); setExpanded(false); setShowAddConfirm(true); enqueueHideAddConfirm(); } } } else { setError("Před přidáním příspěvku nezapomeňte vyplnit jeho obsah."); } }; const wrapperClass = classNames( className, "hover:elevation-16 transition duration-500", { "elevation-4 cursor-text": !expanded && !showAddConfirm, "lg:elevation-16 container-padding--zero lg:container-padding--auto": expanded, } ); return ( <Card className={wrapperClass} ref={cardRef}> <span className={classNames("alert items-center transition duration-500", { "alert--success": showAddConfirm, "alert--light": !showAddConfirm, hidden: expanded, })} onClick={onWrite} > <i className={classNames("alert__icon text-lg mr-4", { "ico--checkmark": showAddConfirm, "ico--pencil": !showAddConfirm, })} /> {showAddConfirm && <span>Příspěvek byl přidán.</span>} {!showAddConfirm && <span>Napiš nový příspěvek ...</span>} </span> <CardBody className={ "p-4 lg:p-8 " + (showAddConfirm || !expanded ? "hidden" : "") } > <form className="space-y-4" onSubmit={onAdd}> {apiError && is429ApiError && ( <div className="alert alert--warning"> <i className="alert__icon ico--clock text-lg" /> <span> <strong>Zpomal!</strong> Další příspěvek můžeš přidat nejdříve po 1 minutě od přidání posledního. </span> </div> )} {apiError && !is429ApiError && ( <ErrorMessage> Při přidávání příspěvku došlo k problému: {apiError}. </ErrorMessage> )} <MarkdownEditor ref={editorRef} value={text} onChange={onTextInput} error={error} placeholder="Vyplňte text vašeho příspěvku" toolbarCommands={[ ["header", "bold", "italic", "strikethrough"], ["link", "quote"], ["unordered-list", "ordered-list"], ]} /> {canAddProposal && ( <div className="form-field" onChange={(evt) => setType(evt.target.value)} > <div className="form-field__wrapper form-field__wrapper--freeform flex-col sm:flex-row"> <div className="radio form-field__control"> <label> <input type="radio" name="postType" value="post" defaultChecked /> <span className="text-sm sm:text-base"> Přidávám <strong>běžný příspěvek</strong> </span> </label> </div> <div className="radio form-field__control ml-0 mt-4 sm:mt-0 sm:ml-4"> <label> <input type="radio" name="postType" value="procedure-proposal" /> <span className="text-sm sm:text-base"> Přidávám <strong>návrh postupu</strong> </span> </label> </div> </div> </div> )} {type === "procedure-proposal" && ( <p className="alert alert--light text-sm"> <i className="alert__icon ico--info mr-2 text-lg hidden md:block" /> <span> Návrh postupu se v rozpravě zobrazí až poté, co předsedající{" "} <strong>posoudí jeho přijatelnost</strong>. Po odeslání proto nepanikař, že jej hned nevidíš. </span> </p> )} <div className="space-x-4"> <Button type="submit" disabled={error || addingPost || addingProposal} loading={addingPost || addingProposal} fullwidth hoverActive className="text-sm xl:text-base" > {type === "post" && "Přidat příspěvek"} {type === "procedure-proposal" && "Navrhnout postup"} </Button> <span className="text-sm text-grey-200 hidden lg:inline"> Pro pokročilejší formátování můžete používat{" "} <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet" className="underline" target="_blank" rel="noreferrer noopener" > Markdown </a> . </span> </div> </form> </CardBody> </Card> ); }; export default AddPostForm;