Skip to content

Commit

Permalink
Merge pull request #428 from Chia-Network/fix-tokenization-modal-vali…
Browse files Browse the repository at this point in the history
…dations

fix: tokenization modal wallet address validations
  • Loading branch information
wwills2 authored Jan 17, 2025
2 parents 3a6b760 + 2343de1 commit 8e44f5c
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 89 deletions.
112 changes: 65 additions & 47 deletions src/renderer/components/blocks/forms/CreateTokenForm.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,84 @@
import React, { forwardRef, useImperativeHandle } from 'react';
import { Field, HelperText, Spacer } from '@/components';
import { Field, HelperText, Spacer, validateWalletAddress } from '@/components';
import { FormattedMessage, IntlShape, useIntl } from 'react-intl';
import { Form, Formik, FormikProps } from 'formik';
import * as yup from 'yup';
import { Address } from '@/schemas/Address.schema';

interface FormProps extends React.RefAttributes<CreateTokenFormRef> {
data?: Address[];
addressBookRecords: Address[];
onValidityChange: (isValid: boolean) => void;
}

export interface CreateTokenFormRef {
submitForm: () => Promise<string | undefined>;
submitForm: () => Promise<any>;
}

const CreateTokenForm: React.FC<FormProps> = forwardRef<CreateTokenFormRef, FormProps>(({ data }, ref) => {
const intl: IntlShape = useIntl();
const formikRef = React.useRef<FormikProps<any>>(null);
const CreateTokenForm: React.FC<FormProps> = forwardRef<CreateTokenFormRef, FormProps>(
({ addressBookRecords, onValidityChange }, ref) => {
const intl: IntlShape = useIntl();
const formikRef = React.useRef<FormikProps<Address>>(null);

const validationSchema = yup.object({
walletAddress: yup.string().required(intl.formatMessage({ id: 'wallet-address-is-required' })),
});
const validationSchema = yup.object({
walletAddress: yup
.string()
.required(intl.formatMessage({ id: 'wallet-address-is-required' }))
.test('validate-wallet-address', function (walletAddress) {
return validateWalletAddress(walletAddress, this, intl);
}),
name: yup.string().optional(),
});

useImperativeHandle(ref, () => ({
submitForm: async () => {
if (formikRef.current) {
const formik = formikRef.current;
if (formik) {
const errors = await formik.validateForm(formik.values);
formik.setTouched(Object.keys(errors).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
if (formik.values?.walletAddress) {
return formik.values.walletAddress;
useImperativeHandle(ref, () => ({
submitForm: async () => {
if (formikRef.current) {
const formik = formikRef.current;
if (formik) {
const errors = await formik.validateForm(formik.values);
formik.setTouched(Object.keys(errors).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
if (formik.values?.walletAddress) {
return formik.values.walletAddress;
}
}
}
}
},
}));
},
}));

return (
<Formik innerRef={formikRef} initialValues={data} validationSchema={validationSchema} onSubmit={() => {}}>
{() => (
<Form>
<div className="mb-4">
<HelperText className="text-gray-400 sentence-case">
<FormattedMessage id="carbon-token-recipient" />
</HelperText>
<Spacer size={5} />
<Field
name="walletAddress"
type="picklist"
options={
data?.map((addressData) => ({
label: addressData.name || '',
value: addressData.walletAddress || '',
})) || []
}
freeform={true}
/>
</div>
</Form>
)}
</Formik>
);
});
return (
<Formik<Address>
innerRef={formikRef}
initialValues={{ walletAddress: '', name: '' }}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ isValid }) => {
onValidityChange(isValid);

return (
<Form>
<div className="mb-4">
<HelperText className="text-gray-400 sentence-case">
<FormattedMessage id="carbon-token-recipient" />
</HelperText>
<Spacer size={5} />
<Field
required
name="walletAddress"
type="picklist"
initialValue={''}
options={addressBookRecords.map((addressRecord) => ({
label: addressRecord.name || '',
value: addressRecord.walletAddress || '',
}))}
freeform={true}
/>
</div>
</Form>
);
}}
</Formik>
);
},
);

export { CreateTokenForm };
37 changes: 2 additions & 35 deletions src/renderer/components/blocks/forms/UpsertWalletAddressForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useCallback } from 'react';
import { ErrorMessage, Field, Form, Formik, FormikHelpers } from 'formik';
import { Button, FloatingLabel, FormButton } from '@/components';
import { FormattedMessage, IntlShape, useIntl } from 'react-intl';
import { Button, FloatingLabel, FormButton, validateWalletAddress } from '@/components';
import { FormattedMessage, useIntl } from 'react-intl';
import * as yup from 'yup';
import { TestContext, ValidationError } from 'yup';
import { noop } from 'lodash';
import { Address } from '@/schemas/Address.schema';

Expand All @@ -14,38 +13,6 @@ interface FormProps {
onClose: any;
}

const validateWalletAddress = (value: string, context: TestContext, intl: IntlShape): ValidationError | true => {
if (!value) return true; // If empty, required will handle it

if (value.startsWith('xch')) {
if (/^xch[a-zA-Z0-9]{59}$/.test(value)) {
return true;
} else {
return context.createError({
message: intl.formatMessage({
id: 'wallet-addresses-start-with-xch-and-are-62-characters-long',
}),
});
}
} else if (value.startsWith('txch')) {
if (/^txch[a-zA-Z0-9]{59}$/.test(value)) {
return true;
} else {
return context.createError({
message: intl.formatMessage({
id: 'testnet-wallet-addresses-start-with-txch-and-are-63-characters-long',
}),
});
}
} else {
return context.createError({
message: intl.formatMessage({
id: 'wallet-address-must-start-with-xch-or-txch',
}),
});
}
};

const UpsertWalletAddressForm: React.FC<FormProps> = ({ onSubmit, onClearError = noop, data: address, onClose }) => {
const intl = useIntl();

Expand Down
13 changes: 11 additions & 2 deletions src/renderer/components/blocks/modals/CreateTokenModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const CreateTokenModal: React.FC<UpsertModalProps> = ({ onClose, onTokenizationS
const warehouseUnitId = urlHashValues?.length >= 1 ? urlHashValues[0] : '';
const warehouseProjectId = urlHashValues?.length >= 2 ? urlHashValues[1] : '';
const [tokenizationProcessing, setTokenizationProcessing] = useState<boolean>(false);
const [creatTokenFormValid, setCreateTokenFormValid] = useState<boolean>(false);

const [showTokenizationFailure, setShowTokenizationFailure] = useState<boolean>(false);
const { data: unit, isLoading: unitLoading } = useGetUnitQuery(
Expand Down Expand Up @@ -182,10 +183,18 @@ const CreateTokenModal: React.FC<UpsertModalProps> = ({ onClose, onTokenizationS
</Table>
</div>
<div>
<CreateTokenForm ref={createTokenFormRef} data={addressBookData?.data} />
<CreateTokenForm
ref={createTokenFormRef}
onValidityChange={setCreateTokenFormValid}
addressBookRecords={addressBookData?.data || []}
/>
</div>
<div className="flex">
<Button onClick={onSubmitTokenization} isProcessing={tokenizationProcessing}>
<Button
onClick={onSubmitTokenization}
isProcessing={tokenizationProcessing}
disabled={!creatTokenFormValid}
>
<p className="capitalize">
<FormattedMessage id="create-token" />
</p>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/components/form/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './Field';
export * from './TagInput';
export * from './Repeater';
export * from './validations';
36 changes: 36 additions & 0 deletions src/renderer/components/form/validations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TestContext, ValidationError } from 'yup';
import { IntlShape } from 'react-intl';

export const validateWalletAddress = (value: string, context: TestContext, intl: IntlShape): ValidationError | true => {
console.log('!!!!!!!!', value);

if (!value) return true; // If empty, required will handle it

if (value.startsWith('xch')) {
if (/^xch[a-zA-Z0-9]{59}$/.test(value)) {
return true;
} else {
return context.createError({
message: intl.formatMessage({
id: 'wallet-addresses-start-with-xch-and-are-62-characters-long',
}),
});
}
} else if (value.startsWith('txch')) {
if (/^txch[a-zA-Z0-9]{59}$/.test(value)) {
return true;
} else {
return context.createError({
message: intl.formatMessage({
id: 'testnet-wallet-addresses-start-with-txch-and-are-63-characters-long',
}),
});
}
} else {
return context.createError({
message: intl.formatMessage({
id: 'wallet-address-must-start-with-xch-or-txch',
}),
});
}
};
6 changes: 1 addition & 5 deletions src/renderer/components/proxy/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, ChangeEventHandler } from 'react';
import React, { ChangeEventHandler, useEffect, useState } from 'react';
import { Select as FlowbiteSelect } from 'flowbite-react';
import { PiCaretDown } from 'react-icons/pi';

Expand Down Expand Up @@ -39,10 +39,6 @@ const Select: React.FC<SelectProps> = ({
setFilteredOptions(options);
}, [options]);

useEffect(() => {
setInputValue(initialValue);
}, [initialValue, options]);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement> | ChangeEventHandler<HTMLSelectElement>) => {
// @ts-ignore
const value = event.target.value;
Expand Down

0 comments on commit 8e44f5c

Please sign in to comment.