Commit a2597538 authored by tom's avatar tom

Merge branch 'main' of github.com:tom2drum/block-scout into mobile-view-pages

parents 6a3398d7 32d9cf05
...@@ -27,7 +27,7 @@ export default function useNavItems() { ...@@ -27,7 +27,7 @@ export default function useNavItems() {
const accountNavItems = [ const accountNavItems = [
{ text: 'Watchlist', pathname: basePath + '/account/watchlist', icon: watchlistIcon }, { text: 'Watchlist', pathname: basePath + '/account/watchlist', icon: watchlistIcon },
{ text: 'Private tags', pathname: basePath + '/account/private_tags', icon: privateTagIcon }, { text: 'Private tags', pathname: basePath + '/account/tag_address', icon: privateTagIcon },
{ text: 'Public tags', pathname: basePath + '/account/public_tags_request', icon: publicTagIcon }, { text: 'Public tags', pathname: basePath + '/account/public_tags_request', icon: publicTagIcon },
{ text: 'API keys', pathname: basePath + '/account/api_key', icon: apiKeysIcon }, { text: 'API keys', pathname: basePath + '/account/api_key', icon: apiKeysIcon },
{ text: 'Custom ABI', pathname: basePath + '/account/custom_abi', icon: abiIcon }, { text: 'Custom ABI', pathname: basePath + '/account/custom_abi', icon: abiIcon },
......
...@@ -106,8 +106,12 @@ export const NETWORKS: Array<Network> = (() => { ...@@ -106,8 +106,12 @@ export const NETWORKS: Array<Network> = (() => {
// }, // },
// ]; // ];
export const ACCOUNT_ROUTES = [ '/watchlist', '/private-tags', '/public-tags', '/api-keys', '/custom-abi' ]; export const ACCOUNT_ROUTES = [ '/watchlist', '/tag_address', '/tag_transaction', '/public_tags_request', '/api_key', '/custom_abi' ];
export function isAccountRoute(route: string) { export function isAccountRoute(route: string) {
return ACCOUNT_ROUTES.includes(route); return ACCOUNT_ROUTES.includes(route);
} }
export function getAvailablePaths() {
return NETWORKS.map(({ type, subType }) => ({ params: { network_type: type, network_sub_type: subType } }));
}
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import ApiKeys from 'ui/pages/ApiKeys'; import ApiKeys from 'ui/pages/ApiKeys';
const ApiKeysPage: NextPage = () => { const ApiKeysPage: NextPage = () => {
...@@ -14,3 +15,13 @@ const ApiKeysPage: NextPage = () => { ...@@ -14,3 +15,13 @@ const ApiKeysPage: NextPage = () => {
}; };
export default ApiKeysPage; export default ApiKeysPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import CustomAbi from 'ui/pages/CustomAbi'; import CustomAbi from 'ui/pages/CustomAbi';
const CustomAbiPage: NextPage = () => { const CustomAbiPage: NextPage = () => {
...@@ -14,3 +15,13 @@ const CustomAbiPage: NextPage = () => { ...@@ -14,3 +15,13 @@ const CustomAbiPage: NextPage = () => {
}; };
export default CustomAbiPage; export default CustomAbiPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage } from 'next';
import Head from 'next/head';
import React, { useCallback, useState } from 'react';
import PrivateTags from 'ui/pages/PrivateTags';
const TABS = [ 'address', 'transaction' ];
const PrivateTagsPage: NextPage = () => {
const [ , setActiveTab ] = useState(TABS[0]);
const onChangeTab = useCallback((index: number) => {
setActiveTab(TABS[index]);
}, [ setActiveTab ]);
return (
<>
<Head><title>Private tags</title></Head>
<PrivateTags onChangeTab={ onChangeTab }/>
</>
);
};
export default PrivateTagsPage;
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import PublicTags from 'ui/pages/PublicTags'; import PublicTags from 'ui/pages/PublicTags';
const PublicTagsPage: NextPage = () => { const PublicTagsPage: NextPage = () => {
...@@ -14,3 +15,13 @@ const PublicTagsPage: NextPage = () => { ...@@ -14,3 +15,13 @@ const PublicTagsPage: NextPage = () => {
}; };
export default PublicTagsPage; export default PublicTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head';
import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import PrivateTags from 'ui/pages/PrivateTags';
const AddressTagsPage: NextPage = () => {
return (
<>
<Head><title>Public tags</title></Head>
<PrivateTags tab="address"/>
</>
);
};
export default AddressTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head';
import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import PrivateTags from 'ui/pages/PrivateTags';
const TransactionTagsPage: NextPage = () => {
return (
<>
<Head><title>Public tags</title></Head>
<PrivateTags tab="transaction"/>
</>
);
};
export default TransactionTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import WatchList from 'ui/pages/Watchlist'; import WatchList from 'ui/pages/Watchlist';
const WatchListPage: NextPage = () => { const WatchListPage: NextPage = () => {
...@@ -14,3 +15,13 @@ const WatchListPage: NextPage = () => { ...@@ -14,3 +15,13 @@ const WatchListPage: NextPage = () => {
}; };
export default WatchListPage; export default WatchListPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import MyProfile from 'ui/pages/MyProfile'; import MyProfile from 'ui/pages/MyProfile';
const MyProfilePage: NextPage = () => { const MyProfilePage: NextPage = () => {
...@@ -14,3 +15,13 @@ const MyProfilePage: NextPage = () => { ...@@ -14,3 +15,13 @@ const MyProfilePage: NextPage = () => {
}; };
export default MyProfilePage; export default MyProfilePage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
import { Center, VStack, Box } from '@chakra-ui/react'; import { Center, VStack, Box } from '@chakra-ui/react';
import type { NextPage } from 'next'; import type { NextPage, GetStaticPaths } from 'next';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { getAvailablePaths } from 'lib/networks';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const Home: NextPage = () => { const Home: NextPage = () => {
...@@ -22,3 +23,13 @@ const Home: NextPage = () => { ...@@ -22,3 +23,13 @@ const Home: NextPage = () => {
}; };
export default Home; export default Home;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
...@@ -92,7 +92,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -92,7 +92,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
const renderTokenInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'token'>}) => { const renderTokenInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'token'>}) => {
return ( return (
<FormControl variant="floating" id="address" isRequired> <FormControl variant="floating" id="address">
<Input <Input
{ ...field } { ...field }
disabled={ true } disabled={ true }
...@@ -134,6 +134,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -134,6 +134,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
control={ control } control={ control }
rules={{ rules={{
maxLength: NAME_MAX_LENGTH, maxLength: NAME_MAX_LENGTH,
required: true,
}} }}
render={ renderNameInput } render={ renderNameInput }
/> />
......
...@@ -142,7 +142,10 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -142,7 +142,10 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
name="contract_address_hash" name="contract_address_hash"
control={ control } control={ control }
render={ renderContractAddressInput } render={ renderContractAddressInput }
rules={{ pattern: ADDRESS_REGEXP }} rules={{
pattern: ADDRESS_REGEXP,
required: true,
}}
/> />
</Box> </Box>
<Box marginTop={ 5 }> <Box marginTop={ 5 }>
...@@ -150,6 +153,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -150,6 +153,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
name="name" name="name"
control={ control } control={ control }
render={ renderNameInput } render={ renderNameInput }
rules={{ required: true }}
/> />
</Box> </Box>
<Box marginTop={ 5 }> <Box marginTop={ 5 }>
...@@ -157,6 +161,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -157,6 +161,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
name="abi" name="abi"
control={ control } control={ control }
render={ renderAbiInput } render={ renderAbiInput }
rules={{ required: true }}
/> />
</Box> </Box>
<Box marginTop={ 8 }> <Box marginTop={ 8 }>
......
...@@ -6,27 +6,38 @@ import { ...@@ -6,27 +6,38 @@ import {
TabPanel, TabPanel,
TabPanels, TabPanels,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback, useState } from 'react';
import useBasePath from 'lib/hooks/useBasePath';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags'; import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags'; import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
import AccountPageHeader from 'ui/shared/AccountPageHeader'; import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const TABS = [ 'address', 'transaction' ] as const;
type TabName = typeof TABS[number];
type Props = { type Props = {
onChangeTab: (index: number) => void; tab: TabName;
} }
const PrivateTags = ({ onChangeTab: onChangeTabProps }: Props) => { const PrivateTags = ({ tab }: Props) => {
const onTabChange = useCallback((index: number) => { const [ , setActiveTab ] = useState<TabName>(tab);
onChangeTabProps(index);
}, [ onChangeTabProps ]); const basePath = useBasePath();
const onChangeTab = useCallback((index: number) => {
setActiveTab(TABS[index]);
const newUrl = basePath + '/account/' + (TABS[index] === 'address' ? 'tag_address' : 'tag_transaction');
history.replaceState(history.state, '', newUrl);
}, [ setActiveTab, basePath ]);
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<AccountPageHeader text="Private tags"/> <AccountPageHeader text="Private tags"/>
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ onTabChange }> <Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ onChangeTab } defaultIndex={ TABS.indexOf(tab) }>
<TabList marginBottom={{ base: 6, lg: 8 }}> <TabList marginBottom={{ base: 6, lg: 8 }}>
<Tab>Address</Tab> <Tab>Address</Tab>
<Tab>Transaction</Tab> <Tab>Transaction</Tab>
......
...@@ -98,6 +98,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -98,6 +98,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
control={ control } control={ control }
rules={{ rules={{
pattern: ADDRESS_REGEXP, pattern: ADDRESS_REGEXP,
required: true,
}} }}
render={ renderAddressInput } render={ renderAddressInput }
/> />
...@@ -108,6 +109,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -108,6 +109,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
control={ control } control={ control }
rules={{ rules={{
maxLength: TAG_MAX_LENGTH, maxLength: TAG_MAX_LENGTH,
required: true,
}} }}
render={ renderTagInput } render={ renderTagInput }
/> />
......
...@@ -97,6 +97,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => ...@@ -97,6 +97,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) =>
control={ control } control={ control }
rules={{ rules={{
pattern: TRANSACTION_HASH_REGEXP, pattern: TRANSACTION_HASH_REGEXP,
required: true,
}} }}
render={ renderTransactionInput } render={ renderTransactionInput }
/> />
...@@ -107,6 +108,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => ...@@ -107,6 +108,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) =>
control={ control } control={ control }
rules={{ rules={{
maxLength: TAG_MAX_LENGTH, maxLength: TAG_MAX_LENGTH,
required: true,
}} }}
render={ renderTagInput } render={ renderTagInput }
/> />
......
...@@ -40,7 +40,10 @@ export default function PublicTagFormAction({ control, index, fieldsLength, erro ...@@ -40,7 +40,10 @@ export default function PublicTagFormAction({ control, index, fieldsLength, erro
name={ `addresses.${ index }.address` } name={ `addresses.${ index }.address` }
control={ control } control={ control }
render={ renderAddressInput } render={ renderAddressInput }
rules={{ pattern: ADDRESS_REGEXP }} rules={{
pattern: ADDRESS_REGEXP,
required: index === 0,
}}
/> />
<Flex <Flex
columnGap={ 5 } columnGap={ 5 }
......
...@@ -36,7 +36,10 @@ export default function PublicTagFormComment({ control, error, size }: Props) { ...@@ -36,7 +36,10 @@ export default function PublicTagFormComment({ control, error, size }: Props) {
name="comment" name="comment"
control={ control } control={ control }
render={ renderComment } render={ renderComment }
rules={{ maxLength: TEXT_INPUT_MAX_LENGTH }} rules={{
maxLength: TEXT_INPUT_MAX_LENGTH,
required: true,
}}
/> />
); );
} }
...@@ -133,7 +133,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { ...@@ -133,7 +133,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
e.error?.full_name && setError('fullName', { type: 'custom', message: getErrorMessage(e.error, 'full_name') }); e.error?.full_name && setError('fullName', { type: 'custom', message: getErrorMessage(e.error, 'full_name') });
e.error?.email && setError('email', { type: 'custom', message: getErrorMessage(e.error, 'email') }); e.error?.email && setError('email', { type: 'custom', message: getErrorMessage(e.error, 'email') });
e.error?.tags && setError('tags', { type: 'custom', message: getErrorMessage(e.error, 'tags') }); e.error?.tags && setError('tags', { type: 'custom', message: getErrorMessage(e.error, 'tags') });
e.error?.addresses && setError('addresses.0', { type: 'custom', message: getErrorMessage(e.error, 'addresses') }); e.error?.addresses && setError('addresses.0.address', { type: 'custom', message: getErrorMessage(e.error, 'addresses') });
e.error?.additional_comment && setError('comment', { type: 'custom', message: getErrorMessage(e.error, 'additional_comment') }); e.error?.additional_comment && setError('comment', { type: 'custom', message: getErrorMessage(e.error, 'additional_comment') });
} else { } else {
setAlertVisible(true); setAlertVisible(true);
...@@ -215,7 +215,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { ...@@ -215,7 +215,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
<Box position="relative" key={ field.id } marginBottom={ 4 }> <Box position="relative" key={ field.id } marginBottom={ 4 }>
<PublicTagFormAddressInput <PublicTagFormAddressInput
control={ control } control={ control }
error={ errors?.addresses?.[index] as FieldError } error={ errors?.addresses?.[index]?.address as FieldError }
index={ index } index={ index }
fieldsLength={ fields.length } fieldsLength={ fields.length }
onAddFieldClick={ onAddFieldClick } onAddFieldClick={ onAddFieldClick }
......
...@@ -45,7 +45,7 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({ ...@@ -45,7 +45,7 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({
name={ fieldName } name={ fieldName }
control={ control } control={ control }
render={ renderInput } render={ renderInput }
rules={{ pattern }} rules={{ pattern, required }}
/> />
); );
} }
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