diff options
| author | 2021-02-21 08:33:06 +0200 | |
|---|---|---|
| committer | 2021-02-21 08:33:06 +0200 | |
| commit | 75273e5798eb3cc69fb23ab8547b9a438d35845e (patch) | |
| tree | 47446ed79136e5c6b91af1284d34119bbb5c5df4 /src/pages | |
| parent | Migrate from public state map to Redux (diff) | |
Implement basic hCaptcha
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/FormPage.tsx | 52 |
1 files changed, 49 insertions, 3 deletions
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx index e9306e3..c06b33c 100644 --- a/src/pages/FormPage.tsx +++ b/src/pages/FormPage.tsx @@ -2,10 +2,11 @@ import { jsx, css } from "@emotion/react"; import { Link } from "react-router-dom"; -import React, { SyntheticEvent, useEffect, useState, createRef } from "react"; +import React, {SyntheticEvent, useEffect, useState, createRef, useRef} from "react"; import { useParams } from "react-router"; import { PropagateLoader } from "react-spinners"; import { useDispatch, useSelector } from "react-redux"; +import HCaptcha from "@hcaptcha/react-hcaptcha"; import HeaderBar from "../components/HeaderBar"; import ConnectedRenderedQuestion, { RenderedQuestion } from "../components/Question"; @@ -18,7 +19,7 @@ import { unselectable } from "../commonStyles"; import { Question, QuestionType } from "../api/question"; import ApiClient from "../api/client"; import { FormState } from "../store/form/types"; -import { clean } from "../store/form/actions"; +import { clean, setCaptchaToken } from "../store/form/actions"; interface PathParams { id: string @@ -157,6 +158,14 @@ const closedHeaderStyles = css` } `; +const captchaStyles = css` + text-align: center; + + @media (max-width: 850px) { + padding-bottom: 1.2rem; + } +`; + function FormPage(): JSX.Element { const { id } = useParams<PathParams>(); @@ -166,6 +175,9 @@ function FormPage(): JSX.Element { const values = useSelector<FormState, FormState["values"]>( state => state.values ); + const captchaToken = useSelector<FormState, FormState["captchaToken"]>( + state => state.captchaToken + ); const dispatch = useDispatch(); @@ -173,6 +185,9 @@ function FormPage(): JSX.Element { const [sending, setSending] = useState<boolean>(); const [sent, setSent] = useState<boolean>(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const captchaRef = useRef<any>(); + useEffect(() => { dispatch(clean()); getForm(id).then(form => { @@ -255,6 +270,14 @@ function FormPage(): JSX.Element { return; } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!(FormFeatures.DisableAntispam in form!.features) && captchaToken === null) { + if (captchaRef && captchaRef.current) { + captchaRef.current.execute(); + } + return; + } + setSending(true); const answers: { [key: string]: unknown } = {}; @@ -294,7 +317,14 @@ function FormPage(): JSX.Element { } }); - await ApiClient.post(`forms/submit/${id}`, {response: answers}); + const data: { [key: string]: unknown } = {response: answers}; + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!(FormFeatures.DisableAntispam in form!.features)) { + data["captcha"] = captchaToken; + } + + await ApiClient.post(`forms/submit/${id}`, data); setSending(false); setSent(true); } @@ -306,6 +336,21 @@ function FormPage(): JSX.Element { closed_header = <div css={closedHeaderStyles}>This form is now closed. You will not be able to submit your response.</div>; } + let captcha = null; + if (!(FormFeatures.DisableAntispam in form.features)) { + captcha = ( + <div css={captchaStyles}> + <HCaptcha + sitekey={process.env.HCAPTCHA_SITEKEY ? process.env.HCAPTCHA_SITEKEY : ""} + theme={"dark"} + onVerify={(token) => dispatch(setCaptchaToken(token))} + onExpire={() => dispatch(setCaptchaToken(null))} + ref={captchaRef} + /> + </div> + ); + } + return ( <div> <HeaderBar title={form.name} description={form.description}/> @@ -314,6 +359,7 @@ function FormPage(): JSX.Element { <form id="form" onSubmit={handleSubmit} css={[formStyles, unselectable]}> { closed_header } { questions } + { captcha } </form> <Navigation form_state={open}/> </div> |