aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/pages/FormPage.tsx50
1 files changed, 48 insertions, 2 deletions
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index 1e331b9..9bd90d3 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -2,7 +2,8 @@
import { jsx, css } from "@emotion/react";
import { Link } from "react-router-dom";
-import React, { SyntheticEvent, useEffect, useState, createRef } from "react";
+import HCaptcha from "@hcaptcha/react-hcaptcha";
+import React, {SyntheticEvent, useEffect, useState, createRef, useRef} from "react";
import { useParams } from "react-router";
import { PropagateLoader } from "react-spinners";
@@ -154,6 +155,14 @@ const closedHeaderStyles = css`
}
`;
+const captchaStyles = css`
+ text-align: center;
+
+ @media (max-width: 850px) {
+ padding: 1.2rem;
+ }
+`;
+
function FormPage(): JSX.Element {
const { id } = useParams<PathParams>();
@@ -161,6 +170,9 @@ function FormPage(): JSX.Element {
const [sending, setSending] = useState<boolean>();
const [sent, setSent] = useState<boolean>();
+ let captchaToken: string | null = null;
+ const captchaRef = useRef<HCaptcha>(null);
+
useEffect(() => {
getForm(id).then(form => {
setForm(form);
@@ -208,6 +220,9 @@ function FormPage(): JSX.Element {
async function handleSubmit(event: SyntheticEvent) {
event.preventDefault();
+ // Make copy to avoid losing value on state change.
+ const submitCaptchaToken = captchaToken;
+
// Client-side required validation
const invalidFieldIDs: number[] = [];
questions.forEach((prop, i) => {
@@ -242,6 +257,11 @@ function FormPage(): JSX.Element {
return;
}
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ if (!(FormFeatures.DisableAntispam in form!.features) && !submitCaptchaToken && captchaRef && captchaRef.current) {
+ captchaRef.current.execute();
+ }
+
setSending(true);
const answers: { [key: string]: unknown } = {};
@@ -276,7 +296,13 @@ 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"] = submitCaptchaToken;
+ }
+
+ await ApiClient.post(`forms/submit/${id}`, data);
setSending(false);
setSent(true);
}
@@ -288,6 +314,25 @@ 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 => {
+ captchaToken = token;
+ }}
+ onExpire={() => {
+ captchaToken = null;
+ }}
+ ref={captchaRef}
+ />
+ </div>
+ );
+ }
+
return (
<div>
<HeaderBar title={form.name} description={form.description}/>
@@ -296,6 +341,7 @@ function FormPage(): JSX.Element {
<form id="form" onSubmit={handleSubmit} css={[formStyles, unselectable]}>
{ closed_header }
{ questions }
+ { captcha }
</form>
<Navigation form_state={open}/>
</div>