Skip to content
Snippets Groups Projects
Select Git revision
  • 9f9e00fcafd2b6931e655e3c4b399654a7423949
  • main default protected
  • cf2025
  • cf2024
  • cf2023-euro
  • cf2023-offline
6 results

AddPostForm.jsx

Blame
  • user avatar
    xaralis authored
    9004a89b
    History
    AddPostForm.jsx 7.41 KiB
    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;