aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar ks129 <[email protected]>2021-02-21 08:33:06 +0200
committerGravatar ks129 <[email protected]>2021-02-21 08:33:06 +0200
commit75273e5798eb3cc69fb23ab8547b9a438d35845e (patch)
tree47446ed79136e5c6b91af1284d34119bbb5c5df4 /src
parentMigrate from public state map to Redux (diff)
Implement basic hCaptcha
Diffstat (limited to 'src')
-rw-r--r--src/pages/FormPage.tsx52
-rw-r--r--src/store/form/actions.ts14
-rw-r--r--src/store/form/reducers.ts7
-rw-r--r--src/store/form/types.ts3
4 files changed, 69 insertions, 7 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>
diff --git a/src/store/form/actions.ts b/src/store/form/actions.ts
index 8b48a37..dc43729 100644
--- a/src/store/form/actions.ts
+++ b/src/store/form/actions.ts
@@ -5,7 +5,8 @@ export enum FormAction {
SET_VALUE = "SET_VALUE",
SET_ERROR = "SET_ERROR",
SET_VALID = "SET_VALID",
- CLEAN = "CLEAN"
+ CLEAN = "CLEAN",
+ SET_CAPTCHA_TOKEN = "SET_CAPTCHA_TOKEN"
}
// This is base for all actions
@@ -42,7 +43,12 @@ export interface CleanAction extends DefaultFormAction {
type: FormAction.CLEAN
}
-export type Action = SetValueAction | SetErrorAction | SetValidAction | CleanAction;
+export interface SetCaptchaTokenAction extends DefaultFormAction {
+ type: FormAction.SET_CAPTCHA_TOKEN,
+ payload: string | null
+}
+
+export type Action = SetValueAction | SetErrorAction | SetValidAction | CleanAction | SetCaptchaTokenAction;
export function setValue(question: Question, value: string | Map<string, boolean> | null): SetValueAction {
return {
@@ -77,3 +83,7 @@ export function setValid(question: Question, valid: boolean): SetValidAction {
export function clean(): CleanAction {
return { type: FormAction.CLEAN };
}
+
+export function setCaptchaToken(token: string | null): SetCaptchaTokenAction {
+ return { type: FormAction.SET_CAPTCHA_TOKEN, payload: token };
+}
diff --git a/src/store/form/reducers.ts b/src/store/form/reducers.ts
index fcbb33a..21bd047 100644
--- a/src/store/form/reducers.ts
+++ b/src/store/form/reducers.ts
@@ -4,7 +4,8 @@ import {FormState} from "./types";
export const initialState: FormState = {
values: new Map(),
errors: new Map(),
- valid: new Map()
+ valid: new Map(),
+ captchaToken: null
};
export function formReducer(state = initialState, action: Action): FormState {
@@ -24,6 +25,10 @@ export function formReducer(state = initialState, action: Action): FormState {
case FormAction.CLEAN:
return initialState;
+
+ case FormAction.SET_CAPTCHA_TOKEN:
+ new_state.captchaToken = action.payload;
+ break;
}
return new_state;
}
diff --git a/src/store/form/types.ts b/src/store/form/types.ts
index 1d3e594..3f8557e 100644
--- a/src/store/form/types.ts
+++ b/src/store/form/types.ts
@@ -1,5 +1,6 @@
export interface FormState {
values: Map<string, string | Map<string, boolean> | null>,
errors: Map<string, string>,
- valid: Map<string, boolean>
+ valid: Map<string, boolean>,
+ captchaToken: string | null
}