Commit ce6b8be1 authored by tom's avatar tom

file errors

parent ba13aab9
import { Text, Button, Box, chakra, Flex } from '@chakra-ui/react'; import { Text, Button, Box, chakra, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form'; import type { ControllerRenderProps, FieldPathValue, ValidateResult } 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';
import DragAndDropArea from 'ui/shared/forms/DragAndDropArea'; import DragAndDropArea from 'ui/shared/forms/DragAndDropArea';
import FieldError from 'ui/shared/forms/FieldError';
import FileInput from 'ui/shared/forms/FileInput'; import FileInput from 'ui/shared/forms/FileInput';
import FileSnippet from 'ui/shared/forms/FileSnippet'; import FileSnippet from 'ui/shared/forms/FileSnippet';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
type FileTypes = '.sol' | '.yul' | '.json' | '.vy'
interface Props { interface Props {
accept?: string; fileTypes: Array<FileTypes>;
multiple?: boolean; multiple?: boolean;
title: string; title: string;
className?: string; className?: string;
hint: string; hint: string;
} }
const ContractVerificationFieldSources = ({ accept, multiple, title, className, hint }: Props) => { const ContractVerificationFieldSources = ({ fileTypes, multiple, title, className, hint }: Props) => {
const { setValue, getValues, control, formState, clearErrors } = useFormContext<FormFields>(); const { setValue, getValues, control, formState, clearErrors } = useFormContext<FormFields>();
const error = 'sources' in formState.errors ? formState.errors.sources : undefined; const error = 'sources' in formState.errors ? formState.errors.sources : undefined;
const commonError = !error?.type?.startsWith('file_') ? error : undefined;
const fileError = error?.type?.startsWith('file_') ? error : undefined;
const handleFileRemove = React.useCallback((index?: number) => { const handleFileRemove = React.useCallback((index?: number) => {
if (index === undefined) { if (index === undefined) {
...@@ -37,25 +42,29 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className, ...@@ -37,25 +42,29 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className,
}, [ getValues, clearErrors, setValue ]); }, [ getValues, clearErrors, setValue ]);
const renderFiles = React.useCallback((files: Array<File>) => { const renderFiles = React.useCallback((files: Array<File>) => {
const errorList = fileError?.message?.split(';');
return ( return (
<Box display="grid" gridTemplateColumns={{ base: '1fr', lg: '1fr 1fr' }} columnGap={ 3 } rowGap={ 3 }> <Box display="grid" gridTemplateColumns={{ base: '1fr', lg: '1fr 1fr' }} columnGap={ 3 } rowGap={ 3 }>
{ files.map((file, index) => ( { files.map((file, index) => (
<FileSnippet <Box key={ file.name + file.lastModified + index }>
key={ file.name + file.lastModified + index } <FileSnippet
file={ file } file={ file }
maxW="initial" maxW="initial"
onRemove={ handleFileRemove } onRemove={ handleFileRemove }
index={ index } index={ index }
isDisabled={ formState.isSubmitting } isDisabled={ formState.isSubmitting }
/> />
{ errorList?.[index] && <FieldError message={ errorList?.[index] } mt={ 1 } px={ 3 }/> }
</Box>
)) } )) }
</Box> </Box>
); );
}, [ formState.isSubmitting, handleFileRemove ]); }, [ formState.isSubmitting, handleFileRemove, fileError ]);
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'sources'>}) => ( const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'sources'>}) => (
<> <>
<FileInput<FormFields, 'sources'> accept={ accept } multiple={ multiple } field={ field }> <FileInput<FormFields, 'sources'> accept={ fileTypes.join(',') } multiple={ multiple } field={ field }>
{ ({ onChange }) => ( { ({ onChange }) => (
<Flex <Flex
flexDir="column" flexDir="column"
...@@ -77,13 +86,38 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className, ...@@ -77,13 +86,38 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className,
) } ) }
</FileInput> </FileInput>
{ field.value && field.value.length > 0 && renderFiles(field.value) } { field.value && field.value.length > 0 && renderFiles(field.value) }
{ error && ( { commonError?.message && <FieldError message={ commonError.type === 'required' ? 'Field is required' : commonError.message }/> }
<Box fontSize="sm" mt={ 2 } color="error">
{ error.type === 'required' ? 'Field is required' : error.message }
</Box>
) }
</> </>
), [ accept, error, multiple, renderFiles ]); ), [ fileTypes, commonError, multiple, renderFiles ]);
const validateFileType = React.useCallback(async(value: FieldPathValue<FormFields, 'sources'>): Promise<ValidateResult> => {
if (Array.isArray(value)) {
const errors = value.map(({ name }) => fileTypes.some((ext) => name.endsWith(ext)) ? '' : 'Wrong file type');
if (errors.some((item) => item !== '')) {
return errors.join(';');
}
}
return true;
}, [ fileTypes ]);
const validateFileSize = React.useCallback(async(value: FieldPathValue<FormFields, 'sources'>): Promise<ValidateResult> => {
if (Array.isArray(value)) {
const FILE_SIZE_LIMIT = 260;
const errors = value.map(({ size }) => size > FILE_SIZE_LIMIT ? 'File is too big' : '');
if (errors.some((item) => item !== '')) {
return errors.join(';');
}
}
return true;
}, []);
const rules = React.useMemo(() => ({
required: true,
validate: {
file_type: validateFileType,
file_size: validateFileSize,
},
}), [ validateFileSize, validateFileType ]);
return ( return (
<> <>
...@@ -95,7 +129,7 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className, ...@@ -95,7 +129,7 @@ const ContractVerificationFieldSources = ({ accept, multiple, title, className,
name="sources" name="sources"
control={ control } control={ control }
render={ renderControl } render={ renderControl }
rules={{ required: true }} rules={ rules }
/> />
{ hint ? <span>{ hint }</span> : null } { hint ? <span>{ hint }</span> : null }
</ContractVerificationFormRow> </ContractVerificationFormRow>
......
...@@ -7,6 +7,8 @@ import ContractVerificationFieldLibraries from '../fields/ContractVerificationFi ...@@ -7,6 +7,8 @@ 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';
const FILE_TYPES = [ '.sol' as const, '.yul' as const ];
const ContractVerificationMultiPartFile = () => { const ContractVerificationMultiPartFile = () => {
return ( return (
<ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification"> <ContractVerificationMethod title="New Solidity/Yul Smart Contract Verification">
...@@ -14,7 +16,7 @@ const ContractVerificationMultiPartFile = () => { ...@@ -14,7 +16,7 @@ const ContractVerificationMultiPartFile = () => {
<ContractVerificationFieldEvmVersion/> <ContractVerificationFieldEvmVersion/>
<ContractVerificationFieldOptimization/> <ContractVerificationFieldOptimization/>
<ContractVerificationFieldSources <ContractVerificationFieldSources
accept=".sol,.yul" fileTypes={ FILE_TYPES }
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."
......
...@@ -3,11 +3,13 @@ import React from 'react'; ...@@ -3,11 +3,13 @@ import React from 'react';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
const FILE_TYPES = [ '.json' as const ];
const ContractVerificationSourcify = () => { const ContractVerificationSourcify = () => {
return ( return (
<ContractVerificationMethod title="New Smart Contract Verification"> <ContractVerificationMethod title="New Smart Contract Verification">
<ContractVerificationFieldSources <ContractVerificationFieldSources
accept=".json" fileTypes={ FILE_TYPES }
multiple multiple
title="Sources and Metadata JSON" mt={ 0 } title="Sources and Metadata JSON" mt={ 0 }
hint="Upload all Solidity contract source files and JSON metadata file(s) created during contract compilation." hint="Upload all Solidity contract source files and JSON metadata file(s) created during contract compilation."
......
...@@ -6,13 +6,15 @@ import ContractVerificationFieldCompiler from '../fields/ContractVerificationFie ...@@ -6,13 +6,15 @@ import ContractVerificationFieldCompiler from '../fields/ContractVerificationFie
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName'; import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
const FILE_TYPES = [ '.json' as const ];
const ContractVerificationStandardInput = () => { const ContractVerificationStandardInput = () => {
return ( return (
<ContractVerificationMethod title="New Smart Contract Verification"> <ContractVerificationMethod title="New Smart Contract Verification">
<ContractVerificationFieldName/> <ContractVerificationFieldName/>
<ContractVerificationFieldCompiler/> <ContractVerificationFieldCompiler/>
<ContractVerificationFieldSources <ContractVerificationFieldSources
accept=".json" fileTypes={ FILE_TYPES }
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."
/> />
......
...@@ -5,13 +5,15 @@ import ContractVerificationFieldCompiler from '../fields/ContractVerificationFie ...@@ -5,13 +5,15 @@ import ContractVerificationFieldCompiler from '../fields/ContractVerificationFie
import ContractVerificationFieldEvmVersion from '../fields/ContractVerificationFieldEvmVersion'; import ContractVerificationFieldEvmVersion from '../fields/ContractVerificationFieldEvmVersion';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
const FILE_TYPES = [ '.vy' as const ];
const ContractVerificationVyperMultiPartFile = () => { const ContractVerificationVyperMultiPartFile = () => {
return ( return (
<ContractVerificationMethod title="New Vyper Smart Contract Verification"> <ContractVerificationMethod title="New Vyper Smart Contract Verification">
<ContractVerificationFieldCompiler isVyper/> <ContractVerificationFieldCompiler isVyper/>
<ContractVerificationFieldEvmVersion isVyper/> <ContractVerificationFieldEvmVersion isVyper/>
<ContractVerificationFieldSources <ContractVerificationFieldSources
accept=".vy" fileTypes={ FILE_TYPES }
multiple multiple
title="Sources *.vy files" title="Sources *.vy files"
hint="Upload all Vyper contract source files." hint="Upload all Vyper contract source files."
......
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