aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--src/pages/FormPage.tsx48
-rw-r--r--webpack.config.js3
-rw-r--r--yarn.lock12
4 files changed, 62 insertions, 3 deletions
diff --git a/package.json b/package.json
index 2e5bf34..eeb8983 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"@fortawesome/free-brands-svg-icons": "5.15.2",
"@fortawesome/free-solid-svg-icons": "5.15.2",
"@fortawesome/react-fontawesome": "0.1.14",
+ "@hcaptcha/react-hcaptcha": "0.3.2",
"@sentry/react": "6.2.1",
"@svgr/webpack": "5.5.0",
"@swc/core": "1.2.50",
@@ -59,6 +60,7 @@
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
"@testing-library/user-event": "12.8.1",
+ "@types/hcaptcha__react-hcaptcha": "0.1.4",
"@types/jest": "26.0.20",
"@types/node": "14.14.31",
"@types/react": "17.0.2",
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index 8852ac5..2022d43 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";
@@ -143,6 +144,12 @@ const closedHeaderStyles = css`
}
`;
+const captchaStyles = css`
+ text-align: center;
+
+ margin-bottom: 1.5rem;
+`;
+
function FormPage(): JSX.Element {
const { id } = useParams<PathParams>();
@@ -150,6 +157,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);
@@ -197,6 +207,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) => {
@@ -231,6 +244,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 } = {};
@@ -265,7 +283,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);
}
@@ -284,6 +308,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) && open) {
+ 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}/>
@@ -293,6 +336,7 @@ function FormPage(): JSX.Element {
{ closed_header }
{ questions }
</form>
+ { captcha }
<Navigation form_state={open} scopes={scopes}/>
</div>
diff --git a/webpack.config.js b/webpack.config.js
index 35c90a7..70c2ae9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -93,7 +93,8 @@ module.exports = {
REACT_APP_SENTRY_DSN: "https://[email protected]/1234",
REACT_APP_OAUTH2_CLIENT_ID: "0",
BACKEND_URL: "https://forms-api.pythondiscord.com/",
- CONTEXT: "development"
+ CONTEXT: "development",
+ HCAPTCHA_SITEKEY: "10000000-ffff-ffff-ffff-000000000001"
}), new HtmlWebpackPlugin({
inject: true,
template: 'public/index.html'
diff --git a/yarn.lock b/yarn.lock
index 4472095..7288683 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1295,6 +1295,11 @@
dependencies:
"@hapi/hoek" "^8.3.0"
+"@hcaptcha/[email protected]":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@hcaptcha/react-hcaptcha/-/react-hcaptcha-0.3.2.tgz#8910ea4c111799336fb64de6aa7b74329a7d7579"
+ integrity sha512-+90hSDwtnKAk3PXJsyABi+wRHS1B+wgWjDO4nz68KpkLnE73rMz/XMdl+ckrwYkFFilzIDKI3o1IqOpMapEwgg==
+
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -2015,6 +2020,13 @@
dependencies:
"@types/node" "*"
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/@types/hcaptcha__react-hcaptcha/-/hcaptcha__react-hcaptcha-0.1.4.tgz#145cab4f0ac29fe8925dc98ab16cf2dffacad7d5"
+ integrity sha512-eqEIBR7yn4Y1fRtxPnFlEP8SAHwX762Z27s/ifd5wfJicviz6HynF8gHCgUPPtfYlaQFHa/A0NkwBA5YNmNCmQ==
+ dependencies:
+ "@types/react" "*"
+
"@types/history@*":
version "4.7.8"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"