Commit aaf56ae0 authored by tom's avatar tom

error and disabled state

parent aa344285
...@@ -5,6 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form'; ...@@ -5,6 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form';
import type { FormFields } from './types'; import type { FormFields } from './types';
import delay from 'lib/delay';
import ContractVerificationFieldMethod from './fields/ContractVerificationFieldMethod'; import ContractVerificationFieldMethod from './fields/ContractVerificationFieldMethod';
import ContractVerificationFlattenSourceCode from './methods/ContractVerificationFlattenSourceCode'; import ContractVerificationFlattenSourceCode from './methods/ContractVerificationFlattenSourceCode';
import ContractVerificationMultiPartFile from './methods/ContractVerificationMultiPartFile'; import ContractVerificationMultiPartFile from './methods/ContractVerificationMultiPartFile';
...@@ -12,26 +14,29 @@ import ContractVerificationSourcify from './methods/ContractVerificationSourcify ...@@ -12,26 +14,29 @@ import ContractVerificationSourcify from './methods/ContractVerificationSourcify
import ContractVerificationStandardInput from './methods/ContractVerificationStandardInput'; import ContractVerificationStandardInput from './methods/ContractVerificationStandardInput';
import ContractVerificationVyperContract from './methods/ContractVerificationVyperContract'; import ContractVerificationVyperContract from './methods/ContractVerificationVyperContract';
const METHODS = {
flatten_source_code: <ContractVerificationFlattenSourceCode/>,
standard_input: <ContractVerificationStandardInput/>,
sourcify: <ContractVerificationSourcify/>,
multi_part_file: <ContractVerificationMultiPartFile/>,
vyper_contract: <ContractVerificationVyperContract/>,
};
const ContractVerificationForm = () => { const ContractVerificationForm = () => {
const formApi = useForm<FormFields>(); const formApi = useForm<FormFields>({
const { control, handleSubmit, watch } = formApi; mode: 'onBlur',
});
const { control, handleSubmit, watch, formState } = formApi;
const onFormSubmit: SubmitHandler<FormFields> = React.useCallback((data) => { const onFormSubmit: SubmitHandler<FormFields> = React.useCallback(async(data) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('__>__', data); console.log('__>__', data);
await delay(5_000);
}, []); }, []);
const methods = React.useMemo(() => ({
flatten_source_code: <ContractVerificationFlattenSourceCode control={ control }/>,
standard_input: <ContractVerificationStandardInput control={ control }/>,
sourcify: <ContractVerificationSourcify control={ control }/>,
multi_part_file: <ContractVerificationMultiPartFile control={ control }/>,
vyper_contract: <ContractVerificationVyperContract control={ control }/>,
}), [ control ]);
const method = watch('method'); const method = watch('method');
const content = methods[method] || null; const content = METHODS[method] || null;
return ( return (
<FormProvider { ...formApi }> <FormProvider { ...formApi }>
...@@ -40,7 +45,7 @@ const ContractVerificationForm = () => { ...@@ -40,7 +45,7 @@ const ContractVerificationForm = () => {
onSubmit={ handleSubmit(onFormSubmit) } onSubmit={ handleSubmit(onFormSubmit) }
mt={ 12 } mt={ 12 }
> >
<ContractVerificationFieldMethod control={ control }/> <ContractVerificationFieldMethod control={ control } isDisabled={ Boolean(method) }/>
{ content } { content }
{ Boolean(method) && ( { Boolean(method) && (
<Button <Button
...@@ -48,6 +53,9 @@ const ContractVerificationForm = () => { ...@@ -48,6 +53,9 @@ const ContractVerificationForm = () => {
size="lg" size="lg"
type="submit" type="submit"
mt={ 12 } mt={ 12 }
isDisabled={ !formState.isValid || !formState.isDirty }
isLoading={ formState.isSubmitting }
loadingText="Verify & publish"
> >
Verify & publish Verify & publish
</Button> </Button>
......
...@@ -7,8 +7,14 @@ interface Props { ...@@ -7,8 +7,14 @@ interface Props {
} }
const ContractVerificationMethod = ({ title, children }: Props) => { const ContractVerificationMethod = ({ title, children }: Props) => {
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
ref.current?.scrollIntoView({ behavior: 'smooth' });
}, []);
return ( return (
<section> <section ref={ ref }>
<Text variant="secondary" mt={ 12 } mb={ 5 } fontSize="sm">{ title }</Text> <Text variant="secondary" mt={ 12 } mb={ 5 } fontSize="sm">{ title }</Text>
<Grid columnGap="30px" rowGap={{ base: 2, lg: 4 }} templateColumns={{ base: '1fr', lg: 'minmax(auto, 680px) minmax(0, 340px)' }}> <Grid columnGap="30px" rowGap={{ base: 2, lg: 4 }} templateColumns={{ base: '1fr', lg: 'minmax(auto, 680px) minmax(0, 340px)' }}>
{ children } { children }
......
import { FormControl, Link, Textarea } from '@chakra-ui/react'; import { FormControl, Link, Textarea } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -9,22 +9,21 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder'; ...@@ -9,22 +9,21 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { const ContractVerificationFieldAbiEncodedArgs = () => {
control: Control<FormFields>; const { formState, control } = useFormContext<FormFields>();
}
const ContractVerificationFieldAbiEncodedArgs = ({ control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'abi_encoded_args'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'abi_encoded_args'>}) => {
return ( return (
<FormControl variant="floating" id={ field.name } size={{ base: 'md', lg: 'lg' }}> <FormControl variant="floating" id={ field.name } size={{ base: 'md', lg: 'lg' }}>
<Textarea <Textarea
{ ...field } { ...field }
maxLength={ 255 } maxLength={ 255 }
isDisabled={ formState.isSubmitting }
/> />
<InputPlaceholder text="ABI-encoded Constructor Arguments"/> <InputPlaceholder text="ABI-encoded Constructor Arguments"/>
</FormControl> </FormControl>
); );
}, []); }, [ formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import { FormControl, Link, Textarea } from '@chakra-ui/react'; import { FormControl, Link, Textarea } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -10,23 +10,28 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder'; ...@@ -10,23 +10,28 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { interface Props {
control: Control<FormFields>;
isVyper?: boolean; isVyper?: boolean;
} }
const ContractVerificationFieldCode = ({ control, isVyper }: Props) => { const ContractVerificationFieldCode = ({ isVyper }: Props) => {
const { formState, control } = useFormContext<FormFields>();
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'code'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'code'>}) => {
const error = 'code' in formState.errors ? formState.errors.code : undefined;
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}> <FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}>
<Textarea <Textarea
{ ...field } { ...field }
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
required required
maxLength={ 255 } maxLength={ 255 }
/> />
<InputPlaceholder text="Contract code"/> <InputPlaceholder text="Contract code" error={ error }/>
</FormControl> </FormControl>
); );
}, []); }, [ formState.errors, formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import { Code, Select, Checkbox } from '@chakra-ui/react'; import { Code, Select, Checkbox } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -20,28 +20,32 @@ const COMPILERS_NIGHTLY = [ ...@@ -20,28 +20,32 @@ const COMPILERS_NIGHTLY = [
]; ];
interface Props { interface Props {
control: Control<FormFields>;
isVyper?: boolean; isVyper?: boolean;
} }
const ContractVerificationFieldCompiler = ({ control, isVyper }: Props) => { const ContractVerificationFieldCompiler = ({ isVyper }: Props) => {
const [ isNightly, setIsNightly ] = React.useState(false); const [ isNightly, setIsNightly ] = React.useState(false);
const { formState, control } = useFormContext<FormFields>();
const handleCheckboxChange = React.useCallback(() => { const handleCheckboxChange = React.useCallback(() => {
setIsNightly(prev => !prev); setIsNightly(prev => !prev);
}, []); }, []);
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'compiler'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'compiler'>}) => {
const error = 'compiler' in formState.errors ? formState.errors.compiler : undefined;
return ( return (
<Select <Select
{ ...field } { ...field }
size={{ base: 'md', lg: 'lg' }} size={{ base: 'md', lg: 'lg' }}
placeholder="Compiler" placeholder="Compiler"
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
> >
{ [ ...COMPILERS, ...(isNightly ? COMPILERS_NIGHTLY : []) ].map((option) => <option key={ option } value={ option }>{ option }</option>) } { [ ...COMPILERS, ...(isNightly ? COMPILERS_NIGHTLY : []) ].map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select> </Select>
); );
}, [ isNightly ]); }, [ formState.errors, formState.isSubmitting, isNightly ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
...@@ -57,6 +61,7 @@ const ContractVerificationFieldCompiler = ({ control, isVyper }: Props) => { ...@@ -57,6 +61,7 @@ const ContractVerificationFieldCompiler = ({ control, isVyper }: Props) => {
size="lg" size="lg"
mt={ 3 } mt={ 3 }
onChange={ handleCheckboxChange } onChange={ handleCheckboxChange }
isDisabled={ formState.isSubmitting }
> >
Include nightly builds Include nightly builds
</Checkbox> </Checkbox>
......
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -8,14 +8,16 @@ import CheckboxInput from 'ui/shared/CheckboxInput'; ...@@ -8,14 +8,16 @@ import CheckboxInput from 'ui/shared/CheckboxInput';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { const ContractVerificationFieldConstArgs = () => {
control: Control<FormFields>; const { formState, control } = useFormContext<FormFields>();
}
const ContractVerificationFieldConstArgs = ({ control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'constructor_args'>}) => ( const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'constructor_args'>}) => (
<CheckboxInput<FormFields, 'constructor_args'> text="Try to fetch constructor arguments automatically" field={ field }/> <CheckboxInput<FormFields, 'constructor_args'>
), []); text="Try to fetch constructor arguments automatically"
field={ field }
isDisabled={ formState.isSubmitting }
/>
), [ formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import { Link, Select } from '@chakra-ui/react'; import { Link, Select } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -13,22 +13,24 @@ const VERSIONS = [ ...@@ -13,22 +13,24 @@ const VERSIONS = [
'berlin', 'berlin',
]; ];
interface Props { const ContractVerificationFieldEvmVersion = () => {
control: Control<FormFields>; const { formState, control } = useFormContext<FormFields>();
}
const ContractVerificationFieldEvmVersion = ({ control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'evm_version'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'evm_version'>}) => {
const error = 'evm_version' in formState.errors ? formState.errors.evm_version : undefined;
return ( return (
<Select <Select
{ ...field } { ...field }
size={{ base: 'md', lg: 'lg' }} size={{ base: 'md', lg: 'lg' }}
placeholder="EVM Version" placeholder="EVM Version"
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
> >
{ VERSIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) } { VERSIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select> </Select>
); );
}, [ ]); }, [ formState.errors, formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -8,14 +8,12 @@ import CheckboxInput from 'ui/shared/CheckboxInput'; ...@@ -8,14 +8,12 @@ import CheckboxInput from 'ui/shared/CheckboxInput';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { const ContractVerificationFieldIsYul = () => {
control: Control<FormFields>; const { formState, control } = useFormContext<FormFields>();
}
const ContractVerificationFieldIsYul = ({ control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'is_yul'>}) => ( const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'is_yul'>}) => (
<CheckboxInput<FormFields, 'is_yul'> text="Is Yul contract" field={ field }/> <CheckboxInput<FormFields, 'is_yul'> text="Is Yul contract" field={ field } isDisabled={ formState.isSubmitting }/>
), []); ), [ formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import { Checkbox } from '@chakra-ui/react'; import { Checkbox } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form'; import { useFieldArray, useFormContext } from 'react-hook-form';
import { useFieldArray } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
import ContractVerificationFieldLibraryItem from './ContractVerificationFieldLibraryItem'; import ContractVerificationFieldLibraryItem from './ContractVerificationFieldLibraryItem';
interface Props { const ContractVerificationFieldLibraries = () => {
control: Control<FormFields>; const { formState, control } = useFormContext<FormFields>();
}
const ContractVerificationFieldLibraries = ({ control }: Props) => {
const [ isEnabled, setIsEnabled ] = React.useState(false);
const { fields, append, remove, insert } = useFieldArray({ const { fields, append, remove, insert } = useFieldArray({
name: 'libraries', name: 'libraries',
control, control,
}); });
const [ isEnabled, setIsEnabled ] = React.useState(fields.length > 0);
const handleCheckboxChange = React.useCallback(() => { const handleCheckboxChange = React.useCallback(() => {
if (!isEnabled) { if (!isEnabled) {
...@@ -56,6 +51,7 @@ const ContractVerificationFieldLibraries = ({ control }: Props) => { ...@@ -56,6 +51,7 @@ const ContractVerificationFieldLibraries = ({ control }: Props) => {
fieldsLength={ fields.length } fieldsLength={ fields.length }
onAddFieldClick={ handleAddFieldClick } onAddFieldClick={ handleAddFieldClick }
onRemoveFieldClick={ handleRemoveFieldClick } onRemoveFieldClick={ handleRemoveFieldClick }
error={ 'libraries' in formState.errors ? formState.errors.libraries?.[index] : undefined }
/> />
)) } )) }
</> </>
......
import { Flex, FormControl, Icon, IconButton, Input, Text } from '@chakra-ui/react'; import { Flex, FormControl, Icon, IconButton, Input, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Control, ControllerRenderProps } from 'react-hook-form'; import type { Control, ControllerRenderProps, FieldError } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -18,11 +18,15 @@ interface Props { ...@@ -18,11 +18,15 @@ interface Props {
control: Control<FormFields>; control: Control<FormFields>;
index: number; index: number;
fieldsLength: number; fieldsLength: number;
error?: {
name?: FieldError;
address?: FieldError;
};
onAddFieldClick: (index: number) => void; onAddFieldClick: (index: number) => void;
onRemoveFieldClick: (index: number) => void; onRemoveFieldClick: (index: number) => void;
} }
const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, onAddFieldClick, onRemoveFieldClick }: Props) => { const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, onAddFieldClick, onRemoveFieldClick, error }: Props) => {
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
const renderNameControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.name`>}) => { const renderNameControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.name`>}) => {
...@@ -31,24 +35,26 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on ...@@ -31,24 +35,26 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
<Input <Input
{ ...field } { ...field }
required required
isInvalid={ Boolean(error?.name) }
maxLength={ 255 } maxLength={ 255 }
/> />
<InputPlaceholder text="Library name (.sol file)"/> <InputPlaceholder text="Library name (.sol file)" error={ error?.name }/>
</FormControl> </FormControl>
); );
}, []); }, [ error?.name ]);
const renderAddressControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.address`>}) => { const renderAddressControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.address`>}) => {
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}> <FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}>
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(error?.address) }
required required
/> />
<InputPlaceholder text="Library address (0x...)"/> <InputPlaceholder text="Library address (0x...)" error={ error?.address }/>
</FormControl> </FormControl>
); );
}, []); }, [ error?.address ]);
const handleAddButtonClick = React.useCallback(() => { const handleAddButtonClick = React.useCallback(() => {
onAddFieldClick(index); onAddFieldClick(index);
......
import { chakra, Code, FormControl, Input } from '@chakra-ui/react'; import { chakra, Code, FormControl, Input } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -10,23 +10,28 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder'; ...@@ -10,23 +10,28 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { interface Props {
control: Control<FormFields>;
hint?: string; hint?: string;
} }
const ContractVerificationFieldName = ({ control, hint }: Props) => { const ContractVerificationFieldName = ({ hint }: Props) => {
const { formState, control } = useFormContext<FormFields>();
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'name'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'name'>}) => {
const error = 'name' in formState.errors ? formState.errors.name : undefined;
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}> <FormControl variant="floating" id={ field.name } isRequired size={{ base: 'md', lg: 'lg' }}>
<Input <Input
{ ...field } { ...field }
required required
isInvalid={ Boolean(error) }
maxLength={ 255 } maxLength={ 255 }
isDisabled={ formState.isSubmitting }
/> />
<InputPlaceholder text="Contract name"/> <InputPlaceholder text="Contract name" error={ error }/>
</FormControl> </FormControl>
); );
}, []); }, [ formState.errors, formState.isSubmitting ]);
return ( return (
<ContractVerificationFormRow> <ContractVerificationFormRow>
......
import { FormControl, Input } from '@chakra-ui/react'; import { FormControl, Input } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -10,20 +10,22 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder'; ...@@ -10,20 +10,22 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { const ContractVerificationFieldOptimization = () => {
control: Control<FormFields>;
}
const ContractVerificationFieldOptimization = ({ control }: Props) => {
const [ isEnabled, setIsEnabled ] = React.useState(false); const [ isEnabled, setIsEnabled ] = React.useState(false);
const { formState, control } = useFormContext<FormFields>();
const handleCheckboxChange = React.useCallback(() => { const handleCheckboxChange = React.useCallback(() => {
setIsEnabled(prev => !prev); setIsEnabled(prev => !prev);
}, []); }, []);
const renderCheckboxControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'is_optimization_enabled'>}) => ( const renderCheckboxControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'is_optimization_enabled'>}) => (
<CheckboxInput<FormFields, 'is_optimization_enabled'> text="Optimization enabled" field={ field } onChange={ handleCheckboxChange }/> <CheckboxInput<FormFields, 'is_optimization_enabled'>
), [ handleCheckboxChange ]); text="Optimization enabled"
field={ field }
onChange={ handleCheckboxChange }
isDisabled={ formState.isSubmitting }
/>
), [ formState.isSubmitting, handleCheckboxChange ]);
const renderInputControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'optimization_runs'>}) => { const renderInputControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'optimization_runs'>}) => {
return ( return (
...@@ -32,11 +34,12 @@ const ContractVerificationFieldOptimization = ({ control }: Props) => { ...@@ -32,11 +34,12 @@ const ContractVerificationFieldOptimization = ({ control }: Props) => {
{ ...field } { ...field }
required required
maxLength={ 255 } maxLength={ 255 }
isDisabled={ formState.isSubmitting }
/> />
<InputPlaceholder text="Optimization runs"/> <InputPlaceholder text="Optimization runs"/>
</FormControl> </FormControl>
); );
}, []); }, [ formState.isSubmitting ]);
return ( return (
<> <>
...@@ -53,6 +56,7 @@ const ContractVerificationFieldOptimization = ({ control }: Props) => { ...@@ -53,6 +56,7 @@ const ContractVerificationFieldOptimization = ({ control }: Props) => {
name="optimization_runs" name="optimization_runs"
control={ control } control={ control }
render={ renderInputControl } render={ renderInputControl }
rules={{ required: true }}
/> />
</ContractVerificationFormRow> </ContractVerificationFormRow>
) } ) }
......
import { Text, Button, Box, chakra } from '@chakra-ui/react'; import { Text, Button, Box, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
...@@ -11,7 +11,6 @@ import FileSnippet from 'ui/shared/forms/FileSnippet'; ...@@ -11,7 +11,6 @@ import FileSnippet from 'ui/shared/forms/FileSnippet';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props { interface Props {
control: Control<FormFields>;
accept?: string; accept?: string;
multiple?: boolean; multiple?: boolean;
title: string; title: string;
...@@ -19,8 +18,8 @@ interface Props { ...@@ -19,8 +18,8 @@ interface Props {
hint: string; hint: string;
} }
const ContractVerificationFieldSources = ({ control, accept, multiple, title, className, hint }: Props) => { const ContractVerificationFieldSources = ({ accept, multiple, title, className, hint }: Props) => {
const { setValue, getValues } = useFormContext(); const { setValue, getValues, control, formState } = useFormContext<FormFields>();
const handleFileRemove = React.useCallback((index?: number) => { const handleFileRemove = React.useCallback((index?: number) => {
if (index === undefined) { if (index === undefined) {
...@@ -43,11 +42,12 @@ const ContractVerificationFieldSources = ({ control, accept, multiple, title, cl ...@@ -43,11 +42,12 @@ const ContractVerificationFieldSources = ({ control, accept, multiple, title, cl
maxW="initial" maxW="initial"
onRemove={ handleFileRemove } onRemove={ handleFileRemove }
index={ index } index={ index }
isDisabled={ formState.isSubmitting }
/> />
)) } )) }
</Box> </Box>
); );
}, [ handleFileRemove ]); }, [ formState.isSubmitting, handleFileRemove ]);
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'sources'>}) => ( const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'sources'>}) => (
<> <>
...@@ -70,6 +70,7 @@ const ContractVerificationFieldSources = ({ control, accept, multiple, title, cl ...@@ -70,6 +70,7 @@ const ContractVerificationFieldSources = ({ control, accept, multiple, title, cl
name="sources" name="sources"
control={ control } control={ control }
render={ renderControl } render={ renderControl }
rules={{ required: true }}
/> />
{ hint ? <span>{ hint }</span> : null } { hint ? <span>{ hint }</span> : null }
</ContractVerificationFormRow> </ContractVerificationFormRow>
......
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form';
import type { FormFields } from '../types';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCode'; import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCode';
...@@ -13,21 +10,17 @@ import ContractVerificationFieldLibraries from '../fields/ContractVerificationFi ...@@ -13,21 +10,17 @@ import ContractVerificationFieldLibraries from '../fields/ContractVerificationFi
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName'; import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
import ContractVerificationFieldOptimization from '../fields/ContractVerificationFieldOptimization'; import ContractVerificationFieldOptimization from '../fields/ContractVerificationFieldOptimization';
interface Props { const ContractVerificationFlattenSourceCode = () => {
control: Control<FormFields>;
}
const ContractVerificationFlattenSourceCode = ({ control }: Props) => {
return ( return (
<ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification"> <ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification">
<ContractVerificationFieldIsYul control={ control }/> <ContractVerificationFieldIsYul/>
<ContractVerificationFieldName control={ control }/> <ContractVerificationFieldName/>
<ContractVerificationFieldCompiler control={ control }/> <ContractVerificationFieldCompiler/>
<ContractVerificationFieldEvmVersion control={ control }/> <ContractVerificationFieldEvmVersion/>
<ContractVerificationFieldOptimization control={ control }/> <ContractVerificationFieldOptimization/>
<ContractVerificationFieldCode control={ control }/> <ContractVerificationFieldCode/>
<ContractVerificationFieldConstArgs control={ control }/> <ContractVerificationFieldConstArgs/>
<ContractVerificationFieldLibraries control={ control }/> <ContractVerificationFieldLibraries/>
</ContractVerificationMethod> </ContractVerificationMethod>
); );
}; };
......
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form';
import type { FormFields } from '../types';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler'; import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler';
...@@ -10,24 +7,19 @@ import ContractVerificationFieldLibraries from '../fields/ContractVerificationFi ...@@ -10,24 +7,19 @@ import ContractVerificationFieldLibraries from '../fields/ContractVerificationFi
import ContractVerificationFieldOptimization from '../fields/ContractVerificationFieldOptimization'; import ContractVerificationFieldOptimization from '../fields/ContractVerificationFieldOptimization';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
interface Props { const ContractVerificationMultiPartFile = () => {
control: Control<FormFields>;
}
const ContractVerificationMultiPartFile = ({ control }: Props) => {
return ( return (
<ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification"> <ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification">
<ContractVerificationFieldCompiler control={ control }/> <ContractVerificationFieldCompiler/>
<ContractVerificationFieldEvmVersion control={ control }/> <ContractVerificationFieldEvmVersion/>
<ContractVerificationFieldOptimization control={ control }/> <ContractVerificationFieldOptimization/>
<ContractVerificationFieldSources <ContractVerificationFieldSources
control={ control }
accept=".sol,.yul" accept=".sol,.yul"
multiple multiple
title="Sources *.sol or *.yul files" title="Sources *.sol or *.yul files"
hint="Upload all Solidity or Yul contract source files." hint="Upload all Solidity or Yul contract source files."
/> />
<ContractVerificationFieldLibraries control={ control }/> <ContractVerificationFieldLibraries/>
</ContractVerificationMethod> </ContractVerificationMethod>
); );
}; };
......
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form';
import type { FormFields } from '../types';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
interface Props { const ContractVerificationSourcify = () => {
control: Control<FormFields>;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ContractVerificationSourcify = ({ control }: Props) => {
return ( return (
<ContractVerificationMethod title="New Smart Contract Verification"> <ContractVerificationMethod title="New Smart Contract Verification">
<ContractVerificationFieldSources <ContractVerificationFieldSources
control={ control }
accept=".json" accept=".json"
multiple multiple
title="Sources and Metadata JSON" mt={ 0 } title="Sources and Metadata JSON" mt={ 0 }
......
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form';
import type { FormFields } from '../types';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler'; import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler';
...@@ -9,23 +6,17 @@ import ContractVerificationFieldConstArgs from '../fields/ContractVerificationFi ...@@ -9,23 +6,17 @@ import ContractVerificationFieldConstArgs from '../fields/ContractVerificationFi
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName'; import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
interface Props { const ContractVerificationStandardInput = () => {
control: Control<FormFields>;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ContractVerificationStandardInput = ({ control }: Props) => {
return ( return (
<ContractVerificationMethod title="New Smart Contract Verification"> <ContractVerificationMethod title="New Smart Contract Verification">
<ContractVerificationFieldName control={ control }/> <ContractVerificationFieldName/>
<ContractVerificationFieldCompiler control={ control }/> <ContractVerificationFieldCompiler/>
<ContractVerificationFieldSources <ContractVerificationFieldSources
control={ control }
accept=".json" accept=".json"
title="Standard Input JSON" title="Standard Input JSON"
hint="Upload the standard input JSON file created during contract compilation." hint="Upload the standard input JSON file created during contract compilation."
/> />
<ContractVerificationFieldConstArgs control={ control }/> <ContractVerificationFieldConstArgs/>
</ContractVerificationMethod> </ContractVerificationMethod>
); );
}; };
......
import React from 'react'; import React from 'react';
import type { Control } from 'react-hook-form';
import type { FormFields } from '../types';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldAbiEncodedArgs from '../fields/ContractVerificationFieldAbiEncodedArgs'; import ContractVerificationFieldAbiEncodedArgs from '../fields/ContractVerificationFieldAbiEncodedArgs';
...@@ -9,18 +6,13 @@ import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCo ...@@ -9,18 +6,13 @@ import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCo
import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler'; import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler';
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName'; import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
interface Props { const ContractVerificationVyperContract = () => {
control: Control<FormFields>;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ContractVerificationVyperContract = ({ control }: Props) => {
return ( return (
<ContractVerificationMethod title="New Vyper Smart Contract Verification"> <ContractVerificationMethod title="New Vyper Smart Contract Verification">
<ContractVerificationFieldName control={ control } hint="Must match the name specified in the code."/> <ContractVerificationFieldName hint="Must match the name specified in the code."/>
<ContractVerificationFieldCompiler control={ control } isVyper/> <ContractVerificationFieldCompiler isVyper/>
<ContractVerificationFieldCode control={ control } isVyper/> <ContractVerificationFieldCode isVyper/>
<ContractVerificationFieldAbiEncodedArgs control={ control }/> <ContractVerificationFieldAbiEncodedArgs/>
</ContractVerificationMethod> </ContractVerificationMethod>
); );
}; };
......
...@@ -8,6 +8,7 @@ type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = { ...@@ -8,6 +8,7 @@ type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
field: ControllerRenderProps<TInputs, TInputName>; field: ControllerRenderProps<TInputs, TInputName>;
text: string; text: string;
onChange?: () => void; onChange?: () => void;
isDisabled?: boolean;
} }
export default function CheckboxInput<Inputs extends FieldValues, Name extends Path<Inputs>>( export default function CheckboxInput<Inputs extends FieldValues, Name extends Path<Inputs>>(
...@@ -15,6 +16,7 @@ export default function CheckboxInput<Inputs extends FieldValues, Name extends P ...@@ -15,6 +16,7 @@ export default function CheckboxInput<Inputs extends FieldValues, Name extends P
field, field,
text, text,
onChange, onChange,
isDisabled,
}: Props<Inputs, Name>) { }: Props<Inputs, Name>) {
const handleChange: typeof field.onChange = React.useCallback((...args) => { const handleChange: typeof field.onChange = React.useCallback((...args) => {
...@@ -29,6 +31,7 @@ export default function CheckboxInput<Inputs extends FieldValues, Name extends P ...@@ -29,6 +31,7 @@ export default function CheckboxInput<Inputs extends FieldValues, Name extends P
ref={ field.ref } ref={ field.ref }
colorScheme="blue" colorScheme="blue"
size="lg" size="lg"
isDisabled={ isDisabled }
> >
{ text } { text }
</Checkbox> </Checkbox>
......
...@@ -4,7 +4,7 @@ import type { FieldError } from 'react-hook-form'; ...@@ -4,7 +4,7 @@ import type { FieldError } from 'react-hook-form';
interface Props { interface Props {
text: string; text: string;
error?: FieldError; error?: Partial<FieldError>;
} }
const InputPlaceholder = ({ text, error }: Props) => { const InputPlaceholder = ({ text, error }: Props) => {
......
...@@ -21,14 +21,19 @@ const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ chi ...@@ -21,14 +21,19 @@ const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ chi
const files = Array.from(fileList); const files = Array.from(fileList);
field.onChange(files); field.onChange(files);
field.onBlur();
}, [ field ]); }, [ field ]);
const handleClick = React.useCallback(() => { const handleClick = React.useCallback(() => {
ref.current?.click(); ref.current?.click();
}, []); }, []);
const handleInputBlur = React.useCallback(() => {
field.onBlur();
}, [ field ]);
return ( return (
<InputGroup onClick={ handleClick }> <InputGroup onClick={ handleClick } onBlur={ handleInputBlur }>
<VisuallyHiddenInput <VisuallyHiddenInput
type="file" type="file"
onChange={ handleInputChange } onChange={ handleInputChange }
......
import { Box, Flex, Icon, Text, useColorModeValue, chakra } from '@chakra-ui/react'; import { Box, Flex, Icon, Text, useColorModeValue, IconButton, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import crossIcon from 'icons/cross.svg'; import CrossIcon from 'icons/cross.svg';
import imageIcon from 'icons/image.svg'; import imageIcon from 'icons/image.svg';
import { shortenNumberWithLetter } from 'lib/formatters'; import { shortenNumberWithLetter } from 'lib/formatters';
...@@ -10,9 +10,10 @@ interface Props { ...@@ -10,9 +10,10 @@ interface Props {
className?: string; className?: string;
index?: number; index?: number;
onRemove?: (index?: number) => void; onRemove?: (index?: number) => void;
isDisabled?: boolean;
} }
const FileSnippet = ({ file, className, index, onRemove }: Props) => { const FileSnippet = ({ file, className, index, onRemove, isDisabled }: Props) => {
const handleRemove = React.useCallback(() => { const handleRemove = React.useCallback(() => {
onRemove?.(index); onRemove?.(index);
...@@ -33,14 +34,16 @@ const FileSnippet = ({ file, className, index, onRemove }: Props) => { ...@@ -33,14 +34,16 @@ const FileSnippet = ({ file, className, index, onRemove }: Props) => {
<Text fontWeight={ 600 } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ file.name }</Text> <Text fontWeight={ 600 } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ file.name }</Text>
<Text variant="secondary" mt={ 1 }>{ shortenNumberWithLetter(file.size) }B</Text> <Text variant="secondary" mt={ 1 }>{ shortenNumberWithLetter(file.size) }B</Text>
</Box> </Box>
<Icon <IconButton
as={ crossIcon } aria-label="remove"
icon={ <CrossIcon/> }
boxSize={ 6 } boxSize={ 6 }
color="link" variant="simple"
_hover={{ color: 'link_hovered' }} display="inline-block"
cursor="pointer" flexShrink={ 0 }
ml="auto" ml="auto"
onClick={ handleRemove } onClick={ handleRemove }
isDisabled={ isDisabled }
/> />
</Flex> </Flex>
); );
......
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