Commit 0c8a5b7c authored by tom's avatar tom

preview for token icon

parent 3d0f35ba
import { Image, chakra, useColorModeValue, Icon } from '@chakra-ui/react';
import { Image, chakra } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
import tokenPlaceholderIcon from 'icons/token-placeholder.svg';
const EmptyElement = ({ className }: { className?: string }) => {
const bgColor = useColorModeValue('gray.200', 'gray.600');
const color = useColorModeValue('gray.400', 'gray.200');
return (
<Icon
className={ className }
fontWeight={ 600 }
bgColor={ bgColor }
color={ color }
borderRadius="base"
as={ tokenPlaceholderIcon }
transitionProperty="background-color,color"
transitionDuration="normal"
transitionTimingFunction="ease"
/>
);
};
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
interface Props {
hash?: string;
......@@ -44,7 +25,7 @@ const TokenLogo = ({ hash, name, className }: Props) => {
className={ className }
src={ logoSrc }
alt={ `${ name || 'token' } logo` }
fallback={ <EmptyElement className={ className }/> }
fallback={ <TokenLogoPlaceholder className={ className }/> }
/>
);
};
......
import { chakra, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import tokenPlaceholderIcon from 'icons/token-placeholder.svg';
const TokenLogoPlaceholder = ({ className }: { className?: string }) => {
const bgColor = useColorModeValue('gray.200', 'gray.600');
const color = useColorModeValue('gray.400', 'gray.200');
return (
<Icon
className={ className }
fontWeight={ 600 }
bgColor={ bgColor }
color={ color }
borderRadius="base"
as={ tokenPlaceholderIcon }
transitionProperty="background-color,color"
transitionDuration="normal"
transitionTimingFunction="ease"
/>
);
};
export default chakra(TokenLogoPlaceholder);
......@@ -41,7 +41,7 @@ const TokenInfoForm = ({ id }: Props) => {
address: '0x9d2a7b2b09b1d4786e36699d9f56b8c04e92cbb9',
},
});
const { handleSubmit, formState, control } = formApi;
const { handleSubmit, formState, control, trigger } = formApi;
const onFormSubmit: SubmitHandler<Fields> = React.useCallback(async(data) => {
// eslint-disable-next-line no-console
......@@ -79,7 +79,7 @@ const TokenInfoForm = ({ id }: Props) => {
<TokenInfoFieldDocs { ...fieldProps }/>
<TokenInfoFieldSupport { ...fieldProps }/>
<GridItem colSpan={ 2 }>
<TokenInfoFieldIconUrl { ...fieldProps }/>
<TokenInfoFieldIconUrl { ...fieldProps } trigger={ trigger }/>
</GridItem>
<GridItem colSpan={ 2 }>
<TokenInfoFieldProjectDescription { ...fieldProps }/>
......
import { Center, Image, Skeleton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
interface Props {
url: string | undefined;
onLoad?: () => void;
onError?: () => void;
isInvalid: boolean;
}
const TokenInfoIconPreview = ({ url, onError, onLoad, isInvalid }: Props) => {
const borderColor = useColorModeValue('gray.100', 'gray.700');
const borderColorFilled = useColorModeValue('gray.300', 'gray.600');
const borderColorError = useColorModeValue('red.400', 'red.300');
const borderColorActive = isInvalid ? borderColorError : borderColorFilled;
return (
<Center
boxSize="80px"
flexShrink={ 0 }
borderWidth="2px"
borderColor={ url ? borderColorActive : borderColor }
borderRadius="base"
>
<Image
borderRadius="base"
src={ url }
alt="Token logo preview"
boxSize={ 12 }
objectFit="cover"
fallback={ url && !isInvalid ? <Skeleton boxSize={ 12 }/> : <TokenLogoPlaceholder boxSize={ 12 }/> }
onError={ onError }
onLoad={ onLoad }
/>
</Center>
);
};
export default React.memo(TokenInfoIconPreview);
import { FormControl, Input } from '@chakra-ui/react';
import { FormControl, Flex, Input } from '@chakra-ui/react';
import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import type { Control, UseFormTrigger } from 'react-hook-form';
import { useController } from 'react-hook-form';
import type { Fields } from '../types';
import { times } from 'lib/html-entities';
import { validator } from 'lib/validations/url';
import { validator as validateUrl } from 'lib/validations/url';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
import TokenInfoIconPreview from '../TokenInfoIconPreview';
interface Props {
formState: FormState<Fields>;
control: Control<Fields>;
isReadOnly?: boolean;
trigger: UseFormTrigger<Fields>;
}
const TokenInfoFieldIconUrl = ({ formState, control, isReadOnly }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'icon_url'>}) => {
const error = 'icon_url' in formState.errors ? formState.errors.icon_url : undefined;
const TokenInfoFieldIconUrl = ({ control, isReadOnly, trigger }: Props) => {
const [ valueForPreview, setValueForPreview ] = React.useState<string>();
const imageLoadError = React.useRef(false);
const validatePreview = React.useCallback(() => {
return imageLoadError.current ? 'Unable to load image' : true;
}, [ ]);
const { field, formState, fieldState } = useController({
name: 'icon_url',
control,
rules: {
required: true,
validate: { url: validateUrl, preview: validatePreview },
},
});
const handleImageLoadSuccess = React.useCallback(() => {
imageLoadError.current = false;
trigger('icon_url');
}, [ trigger ]);
return (
const handleImageLoadError = React.useCallback(() => {
imageLoadError.current = true;
trigger('icon_url');
}, [ trigger ]);
const handleBlur = React.useCallback(() => {
// make trigger()
field.onBlur();
const isValidUrl = validateUrl(field.value);
isValidUrl === true && setValueForPreview(field.value);
}, [ field ]);
return (
<Flex columnGap={ 5 }>
<FormControl variant="floating" id={ field.name } size="lg" isRequired>
<Input
{ ...field }
isInvalid={ Boolean(error) }
onBlur={ handleBlur }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
autoComplete="off"
required
/>
<InputPlaceholder text={ `Link to icon URL, link to download a SVG or 48${ times }48 PNG icon logo` } error={ error }/>
<InputPlaceholder text={ `Link to icon URL, link to download a SVG or 48${ times }48 PNG icon logo` } error={ fieldState.error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting, isReadOnly ]);
return (
<Controller
name="icon_url"
control={ control }
render={ renderControl }
rules={{ required: true, validate: validator }}
/>
<TokenInfoIconPreview
url={ fieldState.error?.type === 'url' ? undefined : valueForPreview }
onLoad={ handleImageLoadSuccess }
onError={ !isReadOnly ? handleImageLoadError : undefined }
isInvalid={ fieldState.error?.type === 'preview' }
/>
</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