Commit e938c242 authored by tom's avatar tom

skeletons for custom abi page

parent 8e5e0c6d
import type { PublicTag, AddressTag, TransactionTag, ApiKey } from 'types/api/account';
import type { PublicTag, AddressTag, TransactionTag, ApiKey, CustomAbi } from 'types/api/account';
import type { TWatchlistItem } from 'types/client/account';
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';
......@@ -61,3 +61,21 @@ export const API_KEY: ApiKey = {
api_key: '9c3ecf44-a1ca-4ff1-b28e-329e8b65f652',
name: 'placeholder',
};
export const CUSTOM_ABI: CustomAbi = {
abi: [
{
constant: false,
payable: false,
inputs: [ { name: 'target', type: 'address' } ],
name: 'unknownWriteMethod',
outputs: [ { name: 'result', type: 'address' } ],
stateMutability: 'nonpayable',
type: 'function',
},
],
contract_address: ADDRESS_PARAMS,
contract_address_hash: ADDRESS_HASH,
id: '1',
name: 'placeholder',
};
......@@ -102,7 +102,7 @@ export type CustomAbis = Array<CustomAbi>
export interface CustomAbi {
name: string;
id: number;
id: string;
contract_address_hash: string;
contract_address: AddressParam;
abi: Array<AbiItem>;
......@@ -119,7 +119,7 @@ export interface AbiItem {
}
interface AbiInputOutput {
type: 'uint256';
type: 'uint256' | 'address';
name: string;
}
......
......@@ -48,7 +48,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
const queryClient = useQueryClient();
const apiFetch = useApiFetch();
const customAbiKey = (data: Inputs & { id?: number }) => {
const customAbiKey = (data: Inputs & { id?: string }) => {
const body = { name: data.name, contract_address_hash: data.contract_address_hash, abi: data.abi };
if (!data.id) {
......
......@@ -8,11 +8,12 @@ import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props {
item: CustomAbi;
isLoading?: boolean;
onEditClick: (item: CustomAbi) => void;
onDeleteClick: (item: CustomAbi) => void;
}
const CustomAbiListItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const CustomAbiListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Props) => {
const onItemEditClick = useCallback(() => {
return onEditClick(item);
......@@ -24,8 +25,8 @@ const CustomAbiListItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return (
<ListItemMobile>
<AddressSnippet address={ item.contract_address } subtitle={ item.name }/>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick }/>
<AddressSnippet address={ item.contract_address } subtitle={ item.name } isLoading={ isLoading }/>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
</ListItemMobile>
);
};
......
......@@ -12,12 +12,13 @@ import type { CustomAbis, CustomAbi } from 'types/api/account';
import CustomAbiTableItem from './CustomAbiTableItem';
interface Props {
data: CustomAbis;
data?: CustomAbis;
isLoading?: boolean;
onEditClick: (item: CustomAbi) => void;
onDeleteClick: (item: CustomAbi) => void;
}
const CustomAbiTable = ({ data, onDeleteClick, onEditClick }: Props) => {
const CustomAbiTable = ({ data, isLoading, onDeleteClick, onEditClick }: Props) => {
return (
<Table variant="simple" minWidth="600px">
<Thead>
......@@ -27,10 +28,11 @@ const CustomAbiTable = ({ data, onDeleteClick, onEditClick }: Props) => {
</Tr>
</Thead>
<Tbody>
{ data.map((item) => (
{ data?.map((item, index) => (
<CustomAbiTableItem
key={ item.id + (isLoading ? index : '') }
item={ item }
key={ item.id }
isLoading={ isLoading }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
......
......@@ -11,11 +11,12 @@ import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props {
item: CustomAbi;
isLoading?: boolean;
onEditClick: (item: CustomAbi) => void;
onDeleteClick: (item: CustomAbi) => void;
}
const CustomAbiTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const CustomAbiTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Props) => {
const onItemEditClick = useCallback(() => {
return onEditClick(item);
......@@ -28,10 +29,10 @@ const CustomAbiTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return (
<Tr alignItems="top" key={ item.id }>
<Td>
<AddressSnippet address={ item.contract_address } subtitle={ item.name }/>
<AddressSnippet address={ item.contract_address } subtitle={ item.name } isLoading={ isLoading }/>
</Td>
<Td>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick }/>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
</Td>
</Tr>
);
......
import { Box, Button, HStack, Skeleton, useDisclosure } from '@chakra-ui/react';
import { Box, Button, Skeleton, useDisclosure } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import type { CustomAbi } from 'types/api/account';
import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import { CUSTOM_ABI } from 'stubs/account';
import CustomAbiModal from 'ui/customAbi/CustomAbiModal/CustomAbiModal';
import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem';
import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable';
......@@ -13,19 +13,20 @@ import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonListAccount from 'ui/shared/skeletons/SkeletonListAccount';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
const CustomAbiPage: React.FC = () => {
const customAbiModalProps = useDisclosure();
const deleteModalProps = useDisclosure();
const isMobile = useIsMobile();
useRedirectForInvalidAuthToken();
const [ customAbiModalData, setCustomAbiModalData ] = useState<CustomAbi>();
const [ deleteModalData, setDeleteModalData ] = useState<CustomAbi>();
const { data, isLoading, isError, error } = useApiQuery('custom_abi');
const { data, isPlaceholderData, isError, error } = useApiQuery('custom_abi', {
queryOptions: {
placeholderData: Array(3).fill(CUSTOM_ABI),
},
});
const onEditClick = useCallback((data: CustomAbi) => {
setCustomAbiModalData(data);
......@@ -54,22 +55,6 @@ const CustomAbiPage: React.FC = () => {
);
const content = (() => {
if (isLoading && !data) {
const loader = isMobile ? <SkeletonListAccount/> : (
<>
<SkeletonTable columns={ [ '100%', '108px' ] }/>
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
</>
);
return (
<>
{ description }
{ loader }
</>
);
}
if (isError) {
if (error.status === 403) {
throw new Error('Unverified email error', { cause: error });
......@@ -77,37 +62,42 @@ const CustomAbiPage: React.FC = () => {
return <DataFetchAlert/>;
}
const list = isMobile ? (
<Box>
{ data.map((item) => (
<CustomAbiListItem
item={ item }
key={ item.id }
const list = (
<>
<Box display={{ base: 'block', lg: 'none' }}>
{ data?.map((item, index) => (
<CustomAbiListItem
key={ item.id + (isPlaceholderData ? index : '') }
item={ item }
isLoading={ isPlaceholderData }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
)) }
</Box>
<Box display={{ base: 'none', lg: 'block' }}>
<CustomAbiTable
data={ data }
isLoading={ isPlaceholderData }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
)) }
</Box>
) : (
<CustomAbiTable
data={ data }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
</Box>
</>
);
return (
<>
{ description }
{ data.length > 0 && list }
<HStack marginTop={ 8 } spacing={ 5 }>
{ Boolean(data?.length) && list }
<Skeleton mt={ 8 } isLoaded={ !isPlaceholderData } display="inline-block">
<Button
size="lg"
onClick={ customAbiModalProps.onOpen }
>
Add custom ABI
</Button>
</HStack>
</Skeleton>
<CustomAbiModal { ...customAbiModalProps } onClose={ onCustomAbiModalClose } data={ customAbiModalData }/>
{ deleteModalData && <DeleteCustomAbiModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> }
</>
......
import { Text, Box } from '@chakra-ui/react';
import { Box, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { AddressParam } from 'types/api/addressParams';
......@@ -22,7 +22,11 @@ const AddressSnippet = ({ address, subtitle, isLoading }: Props) => {
<AddressLink type="address" hash={ address.hash } fontWeight="600" ml={ 2 } isLoading={ isLoading }/>
<CopyToClipboard text={ address.hash } ml={ 1 } isLoading={ isLoading }/>
</Address>
{ subtitle && <Text fontSize="sm" variant="secondary" mt={ 0.5 } ml={ 8 }>{ subtitle }</Text> }
{ subtitle && (
<Skeleton fontSize="sm" color="text_secondary" mt={ 0.5 } ml={ 8 } display="inline-block" isLoaded={ !isLoading }>
<span>{ subtitle }</span>
</Skeleton>
) }
</Box>
);
};
......
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