Commit 2b8f48d8 authored by tom's avatar tom

form fields and steps

parent 3b2d575a
import { Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm, FormProvider } from 'react-hook-form';
import type { AddressVerificationFormFields } from './types';
import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress';
import AddressVerificationStepSignature from './steps/AddressVerificationStepSignature';
import AddressVerificationStepSuccess from './steps/AddressVerificationStepSuccess';
interface Props {
isOpen: boolean;
onClose: () => void;
}
const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
const [ stepIndex, setStepIndex ] = React.useState(0);
const formApi = useForm<AddressVerificationFormFields>({
mode: 'onBlur',
});
const { handleSubmit } = formApi;
const handleGoToNextStep = React.useCallback(() => {
setStepIndex((prev) => prev + 1);
}, []);
const handleClose = React.useCallback(() => {
onClose();
setStepIndex(0);
formApi.reset();
}, [ formApi, onClose ]);
const steps = [
{ title: 'Verify new address ownership', content: <AddressVerificationStepAddress onContinue={ handleGoToNextStep }/> },
{ title: 'Copy message to sign', content: <AddressVerificationStepSignature onContinue={ handleGoToNextStep }/> },
{ title: 'Congrats! Address is verified.', content: <AddressVerificationStepSuccess onShowListClick={ handleClose } onAddTokenClick={ handleClose }/> },
];
const onFormSubmit: SubmitHandler<AddressVerificationFormFields> = React.useCallback(async(data) => {
// eslint-disable-next-line no-console
console.log('__>__', data);
}, [ ]);
const step = steps[stepIndex];
return (
<Modal isOpen={ isOpen } onClose={ handleClose } size={{ base: 'full', lg: 'md' }}>
<ModalOverlay/>
<ModalContent>
<ModalHeader fontWeight="500" textStyle="h3" mb={ 6 }>{ step.title }</ModalHeader>
<ModalCloseButton/>
<ModalBody mb={ 0 }>
<FormProvider { ...formApi }>
<form noValidate onSubmit={ handleSubmit(onFormSubmit) }>
{ step.content }
</form>
</FormProvider>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default React.memo(AddressVerificationModal);
import { FormControl, Input } from '@chakra-ui/react';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import type { AddressVerificationFormFields } from '../types';
import { ADDRESS_REGEXP, ADDRESS_LENGTH } from 'lib/validations/address';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
interface Props {
isDisabled?: boolean;
}
const AddressVerificationFieldAddress = ({ isDisabled }: Props) => {
const { formState, control } = useFormContext<AddressVerificationFormFields>();
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<AddressVerificationFormFields, 'address'>}) => {
const error = 'address' in formState.errors ? formState.errors.address : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md">
<Input
{ ...field }
required
isInvalid={ Boolean(error) }
maxLength={ ADDRESS_LENGTH }
isDisabled={ isDisabled || formState.isSubmitting }
autoComplete="off"
/>
<InputPlaceholder text="Smart contract address (0x...)" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting, isDisabled ]);
return (
<Controller
name="address"
control={ control }
render={ renderControl }
rules={{ required: true, pattern: ADDRESS_REGEXP }}
/>
);
};
export default React.memo(AddressVerificationFieldAddress);
import { FormControl, Textarea } from '@chakra-ui/react';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import type { AddressVerificationFormFields } from '../types';
import appConfig from 'configs/app/config';
import dayjs from 'lib/date/dayjs';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
interface Props {
isDisabled?: boolean;
}
const AddressVerificationFieldMessage = ({ isDisabled }: Props) => {
const { formState, control, getValues } = useFormContext<AddressVerificationFormFields>();
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<AddressVerificationFormFields, 'message'>}) => {
const error = 'message' in formState.errors ? formState.errors.message : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md">
<Textarea
{ ...field }
required
isInvalid={ Boolean(error) }
isDisabled={ isDisabled || formState.isSubmitting }
autoComplete="off"
maxH="105px"
/>
<InputPlaceholder text="Message to sign" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting, isDisabled ]);
const address = getValues('address');
// eslint-disable-next-line max-len
const defaultValue = `[${ appConfig.host } ${ dayjs().format('YYYY-MM-DD HH:mm:ss') }] I hereby verify that I am the owner/creator of the address ${ address }.`;
return (
<Controller
defaultValue={ defaultValue }
name="message"
control={ control }
render={ renderControl }
rules={{ required: true }}
/>
);
};
export default React.memo(AddressVerificationFieldMessage);
import { FormControl, Input } from '@chakra-ui/react';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import type { AddressVerificationFormFields } from '../types';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
const AddressVerificationFieldSignature = () => {
const { formState, control } = useFormContext<AddressVerificationFormFields>();
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<AddressVerificationFormFields, 'signature'>}) => {
const error = 'signature' in formState.errors ? formState.errors.signature : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md">
<Input
{ ...field }
required
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
autoComplete="off"
/>
<InputPlaceholder text="Signature hash" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting ]);
return (
<Controller
name="signature"
control={ control }
render={ renderControl }
rules={{ required: true }}
/>
);
};
export default React.memo(AddressVerificationFieldSignature);
import { Box, Button, Flex, Link } from '@chakra-ui/react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import type { AddressVerificationFormFields } from '../types';
import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress';
interface Props {
onContinue: () => void;
}
const AddressVerificationStepAddress = ({ onContinue }: Props) => {
const { formState, trigger } = useFormContext<AddressVerificationFormFields>();
const handleButtonClick = React.useCallback(() => {
if (formState.errors.address) {
trigger('address');
return;
}
onContinue();
}, [ formState, onContinue, trigger ]);
return (
<Box>
<Box mb={ 8 }>Let’s check your address...</Box>
<AddressVerificationFieldAddress/>
<Flex alignItems="center" mt={ 8 } columnGap={ 5 }>
<Button size="lg" onClick={ handleButtonClick }>
Continue
</Button>
<Box>
<span>Contact </span>
<Link>support@blockscout.com</Link>
</Box>
</Flex>
</Box>
);
};
export default React.memo(AddressVerificationStepAddress);
import { Box, Button, Flex, Link } from '@chakra-ui/react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import type { AddressVerificationFormFields } from '../types';
import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress';
import AddressVerificationFieldMessage from '../fields/AddressVerificationFieldMessage';
import AddressVerificationFieldSignature from '../fields/AddressVerificationFieldSignature';
interface Props {
onContinue: () => void;
}
const AddressVerificationStepSignature = ({ onContinue }: Props) => {
const [ isManualSigning, setIsManualSigning ] = React.useState(false);
const { formState, trigger } = useFormContext<AddressVerificationFormFields>();
const handleVerifyButtonClick = React.useCallback(() => {
if (!formState.isValid) {
trigger('signature');
trigger('message');
return;
}
onContinue();
}, [ formState, onContinue, trigger ]);
const handleWeb3SignClick = React.useCallback(() => {
}, []);
const handleManualSignClick = React.useCallback(() => {
setIsManualSigning(true);
}, []);
const buttons = isManualSigning ? (
<>
<Button size="lg" onClick={ handleVerifyButtonClick }>
Verify ownership
</Button>
<Box>
<span>Contact </span>
<Link>support@blockscout.com</Link>
</Box>
</>
) : (
<>
<Button size="lg" onClick={ handleManualSignClick }>
Sign manually
</Button>
<Button size="lg" onClick={ handleWeb3SignClick }>
Sign via web3 wallet
</Button>
</>
);
return (
<Box>
<Box mb={ 8 }>
Copy the message below and sign it using the Blockscout sign message provider of your choice.
</Box>
<Flex rowGap={ 5 } flexDir="column">
<AddressVerificationFieldAddress isDisabled/>
<AddressVerificationFieldMessage isDisabled={ !isManualSigning }/>
{ isManualSigning && <AddressVerificationFieldSignature/> }
</Flex>
<Box mt={ 8 }>
<span>{ `Check our article on "` }</span>
<Link>How to sign message?</Link>
<span>{ `" if you have not done before.` }</span>
</Box>
<Flex alignItems="center" mt={ 8 } columnGap={ 5 }>
{ buttons }
</Flex>
</Box>
);
};
export default React.memo(AddressVerificationStepSignature);
import { Alert, Box, Button, chakra, Flex } from '@chakra-ui/react';
import React from 'react';
interface Props {
onShowListClick: () => void;
onAddTokenClick: () => void;
}
const AddressVerificationStepSuccess = ({ onAddTokenClick, onShowListClick }: Props) => {
return (
<Box>
<Alert status="success" flexWrap="wrap" whiteSpace="pre-wrap" wordBreak="break-word" mb={ 3 } display="inline-block">
<span>The address ownership for </span>
<chakra.span fontWeight={ 700 }>0xaba7161a7fb69c88e16ed9f455ce62b791ee4d03</chakra.span>
<span> is verified.</span>
</Alert>
<p>You may now submit the “Add token information” request</p>
<Flex alignItems="center" mt={ 8 } columnGap={ 5 } flexWrap="wrap" rowGap={ 5 }>
<Button size="lg" variant="outline" onClick={ onShowListClick }>
View my verified addresses
</Button>
<Button size="lg" onClick={ onAddTokenClick }>
Add token information
</Button>
</Flex>
</Box>
);
};
export default React.memo(AddressVerificationStepSuccess);
export interface AddressVerificationFormFields {
address: string;
signature: string;
message: string;
}
import { UnorderedList, ListItem, chakra, Button } from '@chakra-ui/react';
import { UnorderedList, ListItem, chakra, Button, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
const VerifiedAddresses = () => {
useRedirectForInvalidAuthToken();
const modalProps = useDisclosure();
return (
<Page>
......@@ -27,9 +32,10 @@ const VerifiedAddresses = () => {
Find out more about verify address ownership.
</chakra.p>
</AccountPageDescription>
<Button size="lg">
<Button size="lg" onClick={ modalProps.onOpen }>
Add address
</Button>
<AddressVerificationModal isOpen={ modalProps.isOpen } onClose={ modalProps.onClose }/>
</Page>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment