Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spike: form challenges api (front-end) #7495

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
4 changes: 3 additions & 1 deletion src/DonationForms/resources/app/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {DonationSummaryProvider} from '@givewp/forms/app/store/donation-summary'

const {donateUrl, inlineRedirectRoutes} = getWindowData();
const formTemplates = window.givewp.form.templates;
const challenges = window.givewp.form.challenges.getAll();

const FormTemplate = withTemplateWrapper(formTemplates.layouts.form);

Expand Down Expand Up @@ -48,7 +49,8 @@ export default function Form({defaultValues, sections, validationSchema}: PropTy
setError,
getGateway(values.gatewayId),
donateUrl,
inlineRedirectRoutes
inlineRedirectRoutes,
challenges
)
),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import getWindowData from '@givewp/forms/app/utilities/getWindowData';
import useGetGatewayById from '@givewp/forms/app/form/MultiStepForm/hooks/useGetGatewayById';

const {validateUrl} = getWindowData();
const challenges = window.givewp.form.challenges.getAll();

/**
* @since 3.0.0
Expand Down Expand Up @@ -44,7 +45,8 @@ export default function NextButton({buttonText = __('Continue')}: {buttonText?:
validateUrl,
values,
setError,
getGateway(values?.gatewayId)
getGateway(values?.gatewayId),
challenges.filter(({id}) => fieldNames.includes(id))
);

setIsValidating(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {useDonationFormSettings} from '@givewp/forms/app/store/form-settings';

const {donateUrl, inlineRedirectRoutes} = getWindowData();
const formTemplates = window.givewp.form.templates;
const challenges = window.givewp.form.challenges.getAll();

const MultiStepFormTemplate = withTemplateWrapper(formTemplates.layouts.multiStepForm);
/**
Expand Down Expand Up @@ -58,7 +59,8 @@ export default function StepForm({
setError,
getGateway(values.gatewayId),
donateUrl,
inlineRedirectRoutes
inlineRedirectRoutes,
challenges
)
),
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
Challenge,
Gateway,
isFormResponseGatewayError,
isFormResponseRedirect,
isFormResponseValidationError,
isResponseRedirected,
isResponseRedirected
} from '@givewp/forms/types';
import generateRequestErrors from '../utilities/generateRequestErrors';
import FormRequestError from '../errors/FormRequestError';
Expand All @@ -13,31 +14,43 @@ import handleRedirect from '@givewp/forms/app/utilities/handleFormRedirect';
import getCurrentFormUrlData from '@givewp/forms/app/utilities/getCurrentFormUrlData';
import postFormData from '@givewp/forms/app/utilities/postFormData';
import convertValuesToFormData from '@givewp/forms/app/utilities/convertValuesToFormData';
import validateChallenges from '@givewp/forms/app/utilities/validateFormChallenges';

export default async function handleSubmitRequest(
values,
setError,
gateway: Gateway,
donateUrl: string,
inlineRedirectRoutes: string[]
inlineRedirectRoutes: string[],
challenges: Challenge[] = []
) {
if (challenges.length > 0) {
const isValid = await validateChallenges(challenges, values, setError);

if (!isValid) {
return setError('FORM_ERROR', {
message: __('You must be a human to submit this form.', 'give')
});
}
}

if (values?.donationType === 'subscription' && !gateway.supportsSubscriptions) {
return setError('FORM_ERROR', {
message: __(
'This payment gateway does not support recurring payments, please try selecting another payment gateway.',
'give'
),
)
});
}

try {
const {originUrl, isEmbed, embedId} = getCurrentFormUrlData();
const { originUrl, isEmbed, embedId } = getCurrentFormUrlData();

const formValues = {
...values,
originUrl,
isEmbed,
embedId,
embedId
};

const formData = convertValuesToFormData(formValues);
Expand All @@ -52,7 +65,7 @@ export default async function handleSubmitRequest(
}
}

const {response} = await postFormData(donateUrl, formData);
const { response } = await postFormData(donateUrl, formData);

if (isResponseRedirected(response)) {
await handleRedirect(response.url, inlineRedirectRoutes);
Expand All @@ -77,7 +90,7 @@ export default async function handleSubmitRequest(
}

return setError('FORM_ERROR', {
message: error?.message ?? __('Something went wrong, please try again or contact support.', 'give'),
message: error?.message ?? __('Something went wrong, please try again or contact support.', 'give')
});
}
};
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {Gateway, isFormResponseGatewayError, isFormResponseValidationError} from '@givewp/forms/types';
import {Challenge, Gateway, isFormResponseGatewayError, isFormResponseValidationError} from '@givewp/forms/types';
import generateRequestErrors from '../utilities/generateRequestErrors';
import FormRequestError from '../errors/FormRequestError';

import {__} from '@wordpress/i18n';
import {FieldValues, UseFormSetError} from 'react-hook-form';
import postFormData from '@givewp/forms/app/utilities/postFormData';
import convertValuesToFormData from '@givewp/forms/app/utilities/convertValuesToFormData';
import validateChallenges from '@givewp/forms/app/utilities/validateFormChallenges';

/**
* @since 3.0.0
Expand All @@ -14,8 +15,19 @@ export default async function handleValidationRequest(
validateUrl: string,
values: FieldValues,
setError: UseFormSetError<FieldValues>,
gateway?: Gateway
gateway?: Gateway,
challenges: Challenge[] = []
) {
if (challenges.length > 0) {
const isValid = await validateChallenges(challenges, values, setError);

if (!isValid) {
return setError('FORM_ERROR', {
message: __('You must be a human to submit this form.', 'give')
});
}
}

if (gateway !== undefined && values?.donationType === 'subscription' && !gateway.supportsSubscriptions) {
return setError('FORM_ERROR', {
message: __(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Challenge} from '@givewp/forms/types';

const isValid = async (challenge: Challenge) => challenge.execute((value: any) => {
challenge.value = value;
});

export default async function validateChallenges(challenges: Challenge[], values: any, setError: any) {
for (const challenge of challenges) {
if (!await isValid(challenge)) {
return false;
}

if (challenge.id in values && challenge?.value && challenge?.value !== values[challenge.id]) {
values[challenge.id] = challenge?.value;
}
}

return true;
}
66 changes: 66 additions & 0 deletions src/DonationForms/resources/registrars/challenges/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {Challenge} from '@givewp/forms/types';

/**
* @unreleased
*/
interface ChallengeRegistrar {
register(challenge: Challenge): void;

unregister(challenge: Challenge): void;

getAll(): Challenge[];

get(id: string): Challenge | undefined;
}

/**
* @unreleased
*/
export default class Registrar implements ChallengeRegistrar {
/**
* @unreleased
*/
private challenges: Challenge[] = [];

/**
* @unreleased
*/
public get(id: string): Challenge | undefined {
return this.challenges.find((challenge) => challenge.id === id);
}

/**
* @unreleased
*/
public getAll(): Challenge[] {
return this.challenges;
}

/**
* @unreleased
*/
public unregister(challenge: Challenge): void {
if (this.get(challenge.id)) {
this.challenges = this.challenges.filter(({ id }) => id !== challenge.id);
}
}

/**
* @unreleased
*/
public register(challenge: Challenge): void {
if (challenge.hasOwnProperty('initialize')) {
try {
challenge.initialize();
} catch (e) {
console.error(`Error initializing ${challenge.id} gateway:`, e);
}
}

if (this.get(challenge.id)) {
throw new Error(`Challenge with id ${challenge.id} is already registered.`);
}

this.challenges.push(challenge);
}
}
3 changes: 3 additions & 0 deletions src/DonationForms/resources/registrars/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import GatewayRegistrar from './gateways';
import ChallengeRegistrar from './challenges';
import type {DonationConfirmationReceiptServerExports, FormServerExports} from '@givewp/forms/types';
import type {useFormContext, useFormState, useWatch} from 'react-hook-form';
import defaultFormTemplates from './templates';
Expand All @@ -13,6 +14,7 @@ declare global {
givewp: {
gateways: GatewayRegistrar;
form: {
challenges: ChallengeRegistrar;
templates: typeof defaultFormTemplates;
hooks: {
useFormContext: typeof useFormContext;
Expand All @@ -35,5 +37,6 @@ if (!window.givewp) {
};
}

window.givewp.form.challenges = new ChallengeRegistrar();
window.givewp.gateways = new GatewayRegistrar();
window.givewp.form.templates = Object.freeze(defaultFormTemplates);
7 changes: 7 additions & 0 deletions src/DonationForms/resources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,10 @@ export function isResponseRedirected(response: Response): response is Response {
export function isDonationTypeSubscription(donationType: string): boolean {
return donationType === 'subscription';
}

export interface Challenge {
value?: any;
id: string;
initialize?(): void;
execute(setValue: Function): Promise<boolean>;
}
Loading