Commit bb9842bc authored by tom's avatar tom

search results page

parent e52386b8
...@@ -8,12 +8,12 @@ import PageNextJs from 'nextjs/PageNextJs'; ...@@ -8,12 +8,12 @@ import PageNextJs from 'nextjs/PageNextJs';
import LayoutSearchResults from 'ui/shared/layout/LayoutSearchResults'; import LayoutSearchResults from 'ui/shared/layout/LayoutSearchResults';
// const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false }); const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false });
const Page: NextPageWithLayout<Props> = (props: Props) => { const Page: NextPageWithLayout<Props> = (props: Props) => {
return ( return (
<PageNextJs pathname="/search-results" query={ props.query }> <PageNextJs pathname="/search-results" query={ props.query }>
{ /* <SearchResults/> */ } <SearchResults/>
</PageNextJs> </PageNextJs>
); );
}; };
......
import { Box, chakra, Table, Tbody, Tr, Th, Show, Hide } from '@chakra-ui/react'; import { Box, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { FormEvent } from 'react'; import type { FormEvent } from 'react';
import React from 'react'; import React from 'react';
...@@ -11,20 +11,20 @@ import { useSettingsContext } from 'lib/contexts/settings'; ...@@ -11,20 +11,20 @@ import { useSettingsContext } from 'lib/contexts/settings';
import * as regexp from 'lib/regexp'; import * as regexp from 'lib/regexp';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam'; import removeQueryParam from 'lib/router/removeQueryParam';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps'; import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps';
import SearchResultListItem from 'ui/searchResults/SearchResultListItem'; import SearchResultListItem from 'ui/searchResults/SearchResultListItem';
import SearchResultsInput from 'ui/searchResults/SearchResultsInput'; import SearchResultsInput from 'ui/searchResults/SearchResultsInput';
import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem'; import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import Skeleton from 'ui/shared/chakra/Skeleton';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import * as Layout from 'ui/shared/layout/components'; import * as Layout from 'ui/shared/layout/components';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import Thead from 'ui/shared/TheadSticky';
import HeaderAlert from 'ui/snippets/header/HeaderAlert'; import HeaderAlert from 'ui/snippets/header/HeaderAlert';
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop'; import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile'; import HeaderMobile from 'ui/snippets/header/HeaderMobile';
...@@ -144,7 +144,7 @@ const SearchResultsPageContent = () => { ...@@ -144,7 +144,7 @@ const SearchResultsPageContent = () => {
return ( return (
<> <>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
{ displayedItems.map((item, index) => ( { displayedItems.map((item, index) => (
<SearchResultListItem <SearchResultListItem
key={ (isLoading ? 'placeholder_' : 'actual_') + index } key={ (isLoading ? 'placeholder_' : 'actual_') + index }
...@@ -154,18 +154,18 @@ const SearchResultsPageContent = () => { ...@@ -154,18 +154,18 @@ const SearchResultsPageContent = () => {
addressFormat={ settingsContext?.addressFormat } addressFormat={ settingsContext?.addressFormat }
/> />
)) } )) }
</Show> </Box>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
<Table fontWeight={ 500 }> <TableRoot fontWeight={ 500 }>
<Thead top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }> <TableHeaderSticky top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }>
<Tr> <TableRow>
<Th width="30%">Search result</Th> <TableColumnHeader width="30%">Search result</TableColumnHeader>
<Th width="35%"/> <TableColumnHeader width="35%"/>
<Th width="35%" pr={ 10 }/> <TableColumnHeader width="35%" pr={ 10 }/>
<Th width="150px">Category</Th> <TableColumnHeader width="150px">Category</TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ displayedItems.map((item, index) => ( { displayedItems.map((item, index) => (
<SearchResultTableItem <SearchResultTableItem
key={ (isLoading ? 'placeholder_' : 'actual_') + index } key={ (isLoading ? 'placeholder_' : 'actual_') + index }
...@@ -175,9 +175,9 @@ const SearchResultsPageContent = () => { ...@@ -175,9 +175,9 @@ const SearchResultsPageContent = () => {
addressFormat={ settingsContext?.addressFormat } addressFormat={ settingsContext?.addressFormat }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
</Hide> </Box>
</> </>
); );
})(); })();
...@@ -190,7 +190,7 @@ const SearchResultsPageContent = () => { ...@@ -190,7 +190,7 @@ const SearchResultsPageContent = () => {
const resultsCount = pagination.page === 1 && !data?.next_page_params ? displayedItems.length : '50+'; const resultsCount = pagination.page === 1 && !data?.next_page_params ? displayedItems.length : '50+';
const text = isLoading && pagination.page === 1 ? ( const text = isLoading && pagination.page === 1 ? (
<Skeleton h={ 6 } w="280px" borderRadius="full" mb={ pagination.isVisible ? 0 : 6 }/> <Skeleton loading h={ 6 } w="280px" borderRadius="full" mb={ pagination.isVisible ? 0 : 6 }/>
) : ( ) : (
( (
<> <>
...@@ -214,9 +214,9 @@ const SearchResultsPageContent = () => { ...@@ -214,9 +214,9 @@ const SearchResultsPageContent = () => {
return ( return (
<> <>
<Box display={{ base: 'block', lg: 'none' }}>{ text }</Box> <Box hideFrom="lg">{ text }</Box>
<ActionBar mt={{ base: 0, lg: -6 }} alignItems="center"> <ActionBar mt={{ base: 0, lg: -6 }} alignItems="center">
<Box display={{ base: 'none', lg: 'block' }}>{ text }</Box> <Box hideBelow="lg">{ text }</Box>
<Pagination { ...pagination }/> <Pagination { ...pagination }/>
</ActionBar> </ActionBar>
</> </>
......
import { chakra, Flex, Grid, Image, Box, Text, useColorMode, Tag } from '@chakra-ui/react'; import { chakra, Flex, Grid, Box, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import xss from 'xss'; import xss from 'xss';
...@@ -12,7 +12,11 @@ import dayjs from 'lib/date/dayjs'; ...@@ -12,7 +12,11 @@ import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { useColorMode } from 'toolkit/chakra/color-mode';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tag } from 'toolkit/chakra/tag';
import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity'; import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as BlobEntity from 'ui/shared/entities/blob/BlobEntity'; import * as BlobEntity from 'ui/shared/entities/blob/BlobEntity';
...@@ -25,7 +29,6 @@ import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon'; ...@@ -25,7 +29,6 @@ import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon';
import { ADDRESS_REGEXP } from 'ui/shared/forms/validators/address'; import { ADDRESS_REGEXP } from 'ui/shared/forms/validators/address';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
...@@ -58,22 +61,22 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -58,22 +61,22 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
return ( return (
<Flex alignItems="center" overflow="hidden"> <Flex alignItems="center" overflow="hidden">
<TokenEntity.Icon token={{ ...data, type: data.token_type }} isLoading={ isLoading }/> <TokenEntity.Icon token={{ ...data, type: data.token_type }} isLoading={ isLoading }/>
<LinkInternal <Link
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
overflow="hidden" overflow="hidden"
> >
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }} dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}
whiteSpace="nowrap" whiteSpace="nowrap"
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
/> />
</LinkInternal> </Link>
{ data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> } { data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> }
{ data.is_verified_via_admin_panel && !data.certified && <IconSvg name="certified" boxSize={ 4 } ml={ 1 } color="green.500"/> } { data.is_verified_via_admin_panel && !data.certified && <IconSvg name="certified" boxSize={ 4 } ml={ 1 } color="green.500"/> }
</Flex> </Flex>
...@@ -108,8 +111,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -108,8 +111,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
<AddressEntity.Content <AddressEntity.Content
asProp={ shouldHighlightHash ? 'mark' : 'span' } asProp={ shouldHighlightHash ? 'mark' : 'span' }
address={{ ...address, hash }} address={{ ...address, hash }}
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</AddressEntity.Link> </AddressEntity.Link>
...@@ -122,15 +124,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -122,15 +124,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
return ( return (
<Flex alignItems="center"> <Flex alignItems="center">
<IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/> <IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal <Link
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
<span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/>
</LinkInternal> </Link>
</Flex> </Flex>
); );
} }
...@@ -146,18 +148,18 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -146,18 +148,18 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
src={ colorMode === 'dark' && data.app.logoDarkMode ? data.app.logoDarkMode : data.app.logo } src={ colorMode === 'dark' && data.app.logoDarkMode ? data.app.logoDarkMode : data.app.logo }
alt={ `${ data.app.title } app icon` } alt={ `${ data.app.title } app icon` }
/> />
<LinkInternal <Link
href={ data.app.external ? href={ data.app.external ?
route({ pathname: '/apps', query: { selectedAppId: data.app.id } }) : route({ pathname: '/apps', query: { selectedAppId: data.app.id } }) :
route({ pathname: '/apps/[id]', query: { id: data.app.id } }) route({ pathname: '/apps/[id]', query: { id: data.app.id } })
} }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
{ title } { title }
</LinkInternal> </Link>
</Flex> </Flex>
); );
} }
...@@ -203,8 +205,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -203,8 +205,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
<TxEntity.Content <TxEntity.Content
asProp="mark" asProp="mark"
hash={ data.transaction_hash } hash={ data.transaction_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</TxEntity.Link> </TxEntity.Link>
...@@ -224,8 +225,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -224,8 +225,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
<BlobEntity.Content <BlobEntity.Content
asProp="mark" asProp="mark"
hash={ data.blob_hash } hash={ data.blob_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</BlobEntity.Link> </BlobEntity.Link>
...@@ -245,8 +245,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -245,8 +245,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
<UserOpEntity.Content <UserOpEntity.Content
asProp="mark" asProp="mark"
hash={ data.user_operation_hash } hash={ data.user_operation_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</UserOpEntity.Link> </UserOpEntity.Link>
...@@ -258,22 +257,21 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -258,22 +257,21 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
return ( return (
<EnsEntity.Container> <EnsEntity.Container>
<EnsEntity.Icon protocol={ data.ens_info.protocol }/> <EnsEntity.Icon protocol={ data.ens_info.protocol }/>
<LinkInternal <Link
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
overflow="hidden" overflow="hidden"
> >
<Skeleton <Box
isLoaded={ !isLoading }
dangerouslySetInnerHTML={{ __html: highlightText(data.ens_info.name, searchTerm) }} dangerouslySetInnerHTML={{ __html: highlightText(data.ens_info.name, searchTerm) }}
whiteSpace="nowrap" whiteSpace="nowrap"
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
/> />
</LinkInternal> </Link>
</EnsEntity.Container> </EnsEntity.Container>
); );
} }
...@@ -289,13 +287,13 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -289,13 +287,13 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
return ( return (
<Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }> <Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }>
<Skeleton isLoaded={ !isLoading } overflow="hidden" display="flex" alignItems="center"> <Skeleton loading={ isLoading } overflow="hidden" display="flex" alignItems="center">
<Text whiteSpace="nowrap" overflow="hidden"> <Text whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ hash } isTooltipDisabled/> <HashStringShortenDynamic hash={ hash } isTooltipDisabled/>
</Text> </Text>
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> } { data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }> <Skeleton loading={ isLoading } overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }>
{ data.token_type === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` } { data.token_type === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` }
{ data.token_type !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` } { data.token_type !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` }
</Skeleton> </Skeleton>
...@@ -307,15 +305,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -307,15 +305,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
const isFutureBlock = data.timestamp === undefined; const isFutureBlock = data.timestamp === undefined;
if (isFutureBlock) { if (isFutureBlock) {
return <Skeleton isLoaded={ !isLoading }>Learn estimated time for this block to be created.</Skeleton>; return <Skeleton loading={ isLoading }>Learn estimated time for this block to be created.</Skeleton>;
} }
return ( return (
<> <>
<Skeleton isLoaded={ !isLoading } as={ shouldHighlightHash ? 'mark' : 'span' } display="block" whiteSpace="nowrap" overflow="hidden" mb={ 1 }> <Skeleton loading={ isLoading } as={ shouldHighlightHash ? 'mark' : 'span' } display="block" whiteSpace="nowrap" overflow="hidden" mb={ 1 }>
<HashStringShortenDynamic hash={ data.block_hash }/> <HashStringShortenDynamic hash={ data.block_hash }/>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary" mr={ 2 }> <Skeleton loading={ isLoading } color="text.secondary" mr={ 2 }>
<span>{ dayjs(data.timestamp).format('llll') }</span> <span>{ dayjs(data.timestamp).format('llll') }</span>
</Skeleton> </Skeleton>
</> </>
...@@ -323,13 +321,13 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -323,13 +321,13 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
} }
case 'transaction': { case 'transaction': {
return ( return (
<Text variant="secondary">{ dayjs(data.timestamp).format('llll') }</Text> <Text color="text.secondary">{ dayjs(data.timestamp).format('llll') }</Text>
); );
} }
case 'user_operation': { case 'user_operation': {
return ( return (
<Text variant="secondary">{ dayjs(data.timestamp).format('llll') }</Text> <Text color="text.secondary">{ dayjs(data.timestamp).format('llll') }</Text>
); );
} }
case 'label': { case 'label': {
...@@ -370,8 +368,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -370,8 +368,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/>
{ data.ens_info && ( { data.ens_info && (
data.ens_info.names_count > 1 ? data.ens_info.names_count > 1 ?
<chakra.span color="text_secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> : <chakra.span color="text.secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> :
<chakra.span color="text_secondary">{ expiresText }</chakra.span> <chakra.span color="text.secondary">{ expiresText }</chakra.span>
) } ) }
</Text> </Text>
{ data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> } { data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> }
...@@ -399,8 +397,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -399,8 +397,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
</Box> </Box>
{ {
data.ens_info.names_count > 1 ? data.ens_info.names_count > 1 ?
<chakra.span color="text_secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> : <chakra.span color="text.secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> :
<chakra.span color="text_secondary">{ expiresText }</chakra.span> <chakra.span color="text.secondary">{ expiresText }</chakra.span>
} }
</Flex> </Flex>
); );
...@@ -414,10 +412,10 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr ...@@ -414,10 +412,10 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
const category = getItemCategory(data); const category = getItemCategory(data);
return ( return (
<ListItemMobile py={ 3 } fontSize="sm" rowGap={ 2 }> <ListItemMobile py={ 3 } textStyle="sm" rowGap={ 2 }>
<Grid templateColumns="1fr auto" w="100%" overflow="hidden" lineHeight={ 6 }> <Grid templateColumns="1fr auto" w="100%" overflow="hidden">
{ firstRow } { firstRow }
<Skeleton isLoaded={ !isLoading } color="text_secondary" ml={ 8 } textTransform="capitalize"> <Skeleton loading={ isLoading } color="text.secondary" ml={ 8 } textTransform="capitalize">
<span>{ category ? searchItemTitles[category].itemTitleShort : '' }</span> <span>{ category ? searchItemTitles[category].itemTitleShort : '' }</span>
</Skeleton> </Skeleton>
</Grid> </Grid>
......
import { chakra, Tr, Td, Text, Flex, Image, Box, useColorMode, Tag } from '@chakra-ui/react'; import { chakra, Text, Flex, Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import xss from 'xss'; import xss from 'xss';
...@@ -12,7 +12,12 @@ import dayjs from 'lib/date/dayjs'; ...@@ -12,7 +12,12 @@ import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { useColorMode } from 'toolkit/chakra/color-mode';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tag } from 'toolkit/chakra/tag';
import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel'; import ContractCertifiedLabel from 'ui/shared/ContractCertifiedLabel';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity'; import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as BlobEntity from 'ui/shared/entities/blob/BlobEntity'; import * as BlobEntity from 'ui/shared/entities/blob/BlobEntity';
...@@ -25,7 +30,6 @@ import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon'; ...@@ -25,7 +30,6 @@ import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon';
import { ADDRESS_REGEXP } from 'ui/shared/forms/validators/address'; import { ADDRESS_REGEXP } from 'ui/shared/forms/validators/address';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
...@@ -57,45 +61,45 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -57,45 +61,45 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
return ( return (
<> <>
<Td fontSize="sm"> <TableCell>
<Flex alignItems="center"> <Flex alignItems="center">
<TokenEntity.Icon token={{ ...data, type: data.token_type }} isLoading={ isLoading }/> <TokenEntity.Icon token={{ ...data, type: data.token_type }} isLoading={ isLoading }/>
<LinkInternal <Link
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
overflow="hidden" overflow="hidden"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
whiteSpace="nowrap" whiteSpace="nowrap"
dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }} dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}
/> />
</LinkInternal> </Link>
{ data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> } { data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } ml={ 1 }/> }
{ data.is_verified_via_admin_panel && !data.certified && <IconSvg name="certified" boxSize={ 4 } ml={ 1 } color="green.500"/> } { data.is_verified_via_admin_panel && !data.certified && <IconSvg name="certified" boxSize={ 4 } ml={ 1 } color="green.500"/> }
</Flex> </Flex>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle"> <TableCell verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } whiteSpace="nowrap" overflow="hidden" display="flex" alignItems="center"> <Skeleton loading={ isLoading } whiteSpace="nowrap" overflow="hidden" display="flex" alignItems="center">
<Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }> <Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }>
<HashStringShortenDynamic hash={ hash }/> <HashStringShortenDynamic hash={ hash }/>
</Box> </Box>
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> } { data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" isNumeric> <TableCell verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } whiteSpace="nowrap" overflow="hidden"> <Skeleton loading={ isLoading } whiteSpace="nowrap" overflow="hidden">
<Text overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }> <Text overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontWeight={ 700 }>
{ data.token_type === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` } { data.token_type === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` }
{ data.token_type !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` } { data.token_type !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` }
</Text> </Text>
</Skeleton> </Skeleton>
</Td> </TableCell>
</> </>
); );
} }
...@@ -121,7 +125,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -121,7 +125,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
return ( return (
<> <>
<Td fontSize="sm" colSpan={ (addressName || data.type === 'metadata_tag') ? 1 : 3 } verticalAlign="middle"> <TableCell colSpan={ (addressName || data.type === 'metadata_tag') ? 1 : 3 } verticalAlign="middle">
<AddressEntity.Container> <AddressEntity.Container>
<AddressEntity.Icon address={ address }/> <AddressEntity.Icon address={ address }/>
<AddressEntity.Link <AddressEntity.Link
...@@ -131,16 +135,15 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -131,16 +135,15 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<AddressEntity.Content <AddressEntity.Content
asProp={ shouldHighlightHash ? 'mark' : 'span' } asProp={ shouldHighlightHash ? 'mark' : 'span' }
address={{ ...address, hash }} address={{ ...address, hash }}
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</AddressEntity.Link> </AddressEntity.Link>
<AddressEntity.Copy address={{ ...address, hash }}/> <AddressEntity.Copy address={{ ...address, hash }}/>
</AddressEntity.Container> </AddressEntity.Container>
</Td> </TableCell>
{ addressName && ( { addressName && (
<Td colSpan={ data.type === 'metadata_tag' ? 1 : 2 } fontSize="sm" verticalAlign="middle"> <TableCell colSpan={ data.type === 'metadata_tag' ? 1 : 2 } verticalAlign="middle">
<Flex alignItems="center"> <Flex alignItems="center">
<Text <Text
overflow="hidden" overflow="hidden"
...@@ -150,19 +153,19 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -150,19 +153,19 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/>
{ data.ens_info && ( { data.ens_info && (
data.ens_info.names_count > 1 ? ( data.ens_info.names_count > 1 ? (
<chakra.span color="text_secondary"> <chakra.span color="text.secondary">
{ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` } { data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` }
</chakra.span> </chakra.span>
) : ) :
<chakra.span color="text_secondary">{ expiresText }</chakra.span> <chakra.span color="text.secondary">{ expiresText }</chakra.span>
) } ) }
</Text> </Text>
{ data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } mx={ 1 }/> } { data.certified && <ContractCertifiedLabel iconSize={ 4 } boxSize={ 4 } mx={ 1 }/> }
</Flex> </Flex>
</Td> </TableCell>
) } ) }
{ data.type === 'metadata_tag' && ( { data.type === 'metadata_tag' && (
<Td colSpan={ addressName ? 1 : 2 } fontSize="sm" verticalAlign="middle"> <TableCell colSpan={ addressName ? 1 : 2 } verticalAlign="middle">
<Flex justifyContent="flex-end"> <Flex justifyContent="flex-end">
{ /* we show regular tag because we don't need all meta info here, but need to highlight search term */ } { /* we show regular tag because we don't need all meta info here, but need to highlight search term */ }
<Tag display="flex" alignItems="center"> <Tag display="flex" alignItems="center">
...@@ -170,7 +173,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -170,7 +173,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<span dangerouslySetInnerHTML={{ __html: highlightText(data.metadata.name, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: highlightText(data.metadata.name, searchTerm) }}/>
</Tag> </Tag>
</Flex> </Flex>
</Td> </TableCell>
) } ) }
</> </>
); );
...@@ -181,29 +184,29 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -181,29 +184,29 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
return ( return (
<> <>
<Td fontSize="sm"> <TableCell>
<Flex alignItems="center"> <Flex alignItems="center">
<IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/> <IconSvg name="publictags_slim" boxSize={ 6 } mr={ 2 } color="gray.500"/>
<LinkInternal <Link
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
<span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/>
</LinkInternal> </Link>
</Flex> </Flex>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle"> <TableCell verticalAlign="middle">
<Flex alignItems="center" overflow="hidden"> <Flex alignItems="center" overflow="hidden">
<Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }> <Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }>
<HashStringShortenDynamic hash={ hash }/> <HashStringShortenDynamic hash={ hash }/>
</Box> </Box>
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> } { data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Flex> </Flex>
</Td> </TableCell>
<Td></Td> <TableCell/>
</> </>
); );
} }
...@@ -212,7 +215,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -212,7 +215,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
const title = <span dangerouslySetInnerHTML={{ __html: highlightText(data.app.title, searchTerm) }}/>; const title = <span dangerouslySetInnerHTML={{ __html: highlightText(data.app.title, searchTerm) }}/>;
return ( return (
<> <>
<Td fontSize="sm"> <TableCell>
<Flex alignItems="center"> <Flex alignItems="center">
<Image <Image
borderRadius="base" borderRadius="base"
...@@ -221,21 +224,21 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -221,21 +224,21 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
src={ colorMode === 'dark' && data.app.logoDarkMode ? data.app.logoDarkMode : data.app.logo } src={ colorMode === 'dark' && data.app.logoDarkMode ? data.app.logoDarkMode : data.app.logo }
alt={ `${ data.app.title } app icon` } alt={ `${ data.app.title } app icon` }
/> />
<LinkInternal <Link
href={ data.app.external ? href={ data.app.external ?
route({ pathname: '/apps', query: { selectedAppId: data.app.id } }) : route({ pathname: '/apps', query: { selectedAppId: data.app.id } }) :
route({ pathname: '/apps/[id]', query: { id: data.app.id } }) route({ pathname: '/apps/[id]', query: { id: data.app.id } })
} }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
{ title } { title }
</LinkInternal> </Link>
</Flex> </Flex>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" colSpan={ 2 }> <TableCell verticalAlign="middle" colSpan={ 2 }>
<Text <Text
overflow="hidden" overflow="hidden"
whiteSpace="nowrap" whiteSpace="nowrap"
...@@ -243,7 +246,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -243,7 +246,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
> >
{ data.app.description } { data.app.description }
</Text> </Text>
</Td> </TableCell>
</> </>
); );
} }
...@@ -257,7 +260,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -257,7 +260,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
return ( return (
<> <>
<Td fontSize="sm"> <TableCell>
<BlockEntity.Container> <BlockEntity.Container>
<BlockEntity.Icon isLoading={ isLoading }/> <BlockEntity.Icon isLoading={ isLoading }/>
<BlockEntity.Link <BlockEntity.Link
...@@ -268,33 +271,32 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -268,33 +271,32 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<BlockEntity.Content <BlockEntity.Content
asProp={ shouldHighlightHash ? 'span' : 'mark' } asProp={ shouldHighlightHash ? 'span' : 'mark' }
number={ Number(data.block_number) } number={ Number(data.block_number) }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
isLoading={ isLoading } isLoading={ isLoading }
/> />
</BlockEntity.Link> </BlockEntity.Link>
</BlockEntity.Container> </BlockEntity.Container>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" colSpan={ isFutureBlock ? 2 : 1 }> <TableCell fontSize="sm" verticalAlign="middle" colSpan={ isFutureBlock ? 2 : 1 }>
{ isFutureBlock ? ( { isFutureBlock ? (
<Skeleton isLoaded={ !isLoading }>Learn estimated time for this block to be created.</Skeleton> <Skeleton loading={ isLoading }>Learn estimated time for this block to be created.</Skeleton>
) : ( ) : (
<Flex columnGap={ 2 } alignItems="center"> <Flex columnGap={ 2 } alignItems="center">
{ data.block_type === 'reorg' && !isLoading && <Tag flexShrink={ 0 }>Reorg</Tag> } { data.block_type === 'reorg' && !isLoading && <Tag flexShrink={ 0 }>Reorg</Tag> }
{ data.block_type === 'uncle' && !isLoading && <Tag flexShrink={ 0 }>Uncle</Tag> } { data.block_type === 'uncle' && !isLoading && <Tag flexShrink={ 0 }>Uncle</Tag> }
<Skeleton isLoaded={ !isLoading } overflow="hidden" whiteSpace="nowrap" as={ shouldHighlightHash ? 'mark' : 'span' } display="block"> <Skeleton loading={ isLoading } overflow="hidden" whiteSpace="nowrap" as={ shouldHighlightHash ? 'mark' : 'span' } display="block">
<HashStringShortenDynamic hash={ data.block_hash }/> <HashStringShortenDynamic hash={ data.block_hash }/>
</Skeleton> </Skeleton>
</Flex> </Flex>
) } ) }
</Td> </TableCell>
{ !isFutureBlock && ( { !isFutureBlock && (
<Td fontSize="sm" verticalAlign="middle" isNumeric> <TableCell fontSize="sm" verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } color="text_secondary"> <Skeleton loading={ isLoading } color="text.secondary">
<span>{ dayjs(data.timestamp).format('llll') }</span> <span>{ dayjs(data.timestamp).format('llll') }</span>
</Skeleton> </Skeleton>
</Td> </TableCell>
) } ) }
</> </>
); );
...@@ -303,7 +305,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -303,7 +305,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
case 'transaction': { case 'transaction': {
return ( return (
<> <>
<Td colSpan={ 2 } fontSize="sm"> <TableCell colSpan={ 2 } fontSize="sm">
<TxEntity.Container> <TxEntity.Container>
<TxEntity.Icon/> <TxEntity.Icon/>
<TxEntity.Link <TxEntity.Link
...@@ -314,23 +316,22 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -314,23 +316,22 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<TxEntity.Content <TxEntity.Content
asProp="mark" asProp="mark"
hash={ data.transaction_hash } hash={ data.transaction_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</TxEntity.Link> </TxEntity.Link>
</TxEntity.Container> </TxEntity.Container>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" isNumeric> <TableCell fontSize="sm" verticalAlign="middle" isNumeric>
<Text variant="secondary">{ dayjs(data.timestamp).format('llll') }</Text> <Text color="text.secondary">{ dayjs(data.timestamp).format('llll') }</Text>
</Td> </TableCell>
</> </>
); );
} }
case 'blob': { case 'blob': {
return ( return (
<Td colSpan={ 3 } fontSize="sm"> <TableCell colSpan={ 3 } fontSize="sm">
<BlobEntity.Container> <BlobEntity.Container>
<BlobEntity.Icon/> <BlobEntity.Icon/>
<BlobEntity.Link <BlobEntity.Link
...@@ -341,20 +342,19 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -341,20 +342,19 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<BlobEntity.Content <BlobEntity.Content
asProp="mark" asProp="mark"
hash={ data.blob_hash } hash={ data.blob_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</BlobEntity.Link> </BlobEntity.Link>
</BlobEntity.Container> </BlobEntity.Container>
</Td> </TableCell>
); );
} }
case 'user_operation': { case 'user_operation': {
return ( return (
<> <>
<Td colSpan={ 2 } fontSize="sm"> <TableCell colSpan={ 2 } fontSize="sm">
<UserOpEntity.Container> <UserOpEntity.Container>
<UserOpEntity.Icon/> <UserOpEntity.Icon/>
<UserOpEntity.Link <UserOpEntity.Link
...@@ -365,16 +365,15 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -365,16 +365,15 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<UserOpEntity.Content <UserOpEntity.Content
asProp="mark" asProp="mark"
hash={ data.user_operation_hash } hash={ data.user_operation_hash }
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</UserOpEntity.Link> </UserOpEntity.Link>
</UserOpEntity.Container> </UserOpEntity.Container>
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" isNumeric> <TableCell fontSize="sm" verticalAlign="middle" isNumeric>
<Text variant="secondary">{ dayjs(data.timestamp).format('llll') }</Text> <Text color="text.secondary">{ dayjs(data.timestamp).format('llll') }</Text>
</Td> </TableCell>
</> </>
); );
} }
...@@ -385,40 +384,40 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -385,40 +384,40 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
return ( return (
<> <>
<Td fontSize="sm"> <TableCell fontSize="sm">
<EnsEntity.Container> <EnsEntity.Container>
<EnsEntity.Icon protocol={ data.ens_info.protocol }/> <EnsEntity.Icon protocol={ data.ens_info.protocol }/>
<LinkInternal <Link
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } loading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
overflow="hidden" overflow="hidden"
> >
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
dangerouslySetInnerHTML={{ __html: highlightText(data.ens_info.name, searchTerm) }} dangerouslySetInnerHTML={{ __html: highlightText(data.ens_info.name, searchTerm) }}
whiteSpace="nowrap" whiteSpace="nowrap"
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
/> />
</LinkInternal> </Link>
</EnsEntity.Container> </EnsEntity.Container>
</Td> </TableCell>
<Td> <TableCell>
<Flex alignItems="center" overflow="hidden"> <Flex alignItems="center" overflow="hidden">
<Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }> <Box overflow="hidden" whiteSpace="nowrap" w={ data.is_smart_contract_verified ? 'calc(100%-28px)' : 'unset' }>
<HashStringShortenDynamic hash={ hash }/> <HashStringShortenDynamic hash={ hash }/>
</Box> </Box>
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> } { data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
</Flex> </Flex>
</Td> </TableCell>
<Td> <TableCell>
{ data.ens_info.names_count > 1 ? { data.ens_info.names_count > 1 ?
<chakra.span color="text_secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> : <chakra.span color="text.secondary"> ({ data.ens_info.names_count > 39 ? '40+' : `+${ data.ens_info.names_count - 1 }` })</chakra.span> :
<chakra.span color="text_secondary">{ expiresText }</chakra.span> } <chakra.span color="text.secondary">{ expiresText }</chakra.span> }
</Td> </TableCell>
</> </>
); );
} }
...@@ -428,14 +427,14 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P ...@@ -428,14 +427,14 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
const category = getItemCategory(data); const category = getItemCategory(data);
return ( return (
<Tr> <TableRow>
{ content } { content }
<Td fontSize="sm" textTransform="capitalize" verticalAlign="middle"> <TableCell fontSize="sm" textTransform="capitalize" verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block"> <Skeleton loading={ isLoading } color="text.secondary" display="inline-block">
<span>{ category ? searchItemTitles[category].itemTitle : '' }</span> <span>{ category ? searchItemTitles[category].itemTitle : '' }</span>
</Skeleton> </Skeleton>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
import { PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import { debounce } from 'es-toolkit'; import { debounce } from 'es-toolkit';
import type { FormEvent, FocusEvent } from 'react'; import type { FormEvent, FocusEvent } from 'react';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { getRecentSearchKeywords } from 'lib/recentSearchKeywords'; import { getRecentSearchKeywords } from 'lib/recentSearchKeywords';
import Popover from 'ui/shared/chakra/Popover'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import SearchBarBackdrop from 'ui/snippets/searchBar/SearchBarBackdrop'; import SearchBarBackdrop from 'ui/snippets/searchBar/SearchBarBackdrop';
import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput'; import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
import SearchBarRecentKeywords from 'ui/snippets/searchBar/SearchBarRecentKeywords'; import SearchBarRecentKeywords from 'ui/snippets/searchBar/SearchBarRecentKeywords';
...@@ -17,7 +17,7 @@ type Props = { ...@@ -17,7 +17,7 @@ type Props = {
}; };
const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }: Props) => { const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }: Props) => {
const { isOpen, onClose, onOpen } = useDisclosure(); const { open, onClose, onOpen } = useDisclosure();
const inputRef = React.useRef<HTMLFormElement>(null); const inputRef = React.useRef<HTMLFormElement>(null);
const menuRef = React.useRef<HTMLDivElement>(null); const menuRef = React.useRef<HTMLDivElement>(null);
const menuWidth = React.useRef<number>(0); const menuWidth = React.useRef<number>(0);
...@@ -25,6 +25,10 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange } ...@@ -25,6 +25,10 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }
const recentSearchKeywords = getRecentSearchKeywords(); const recentSearchKeywords = getRecentSearchKeywords();
const handleOpenChange = React.useCallback(({ open }: { open: boolean }) => {
open && onOpen();
}, [ onOpen ]);
const handleFocus = React.useCallback(() => { const handleFocus = React.useCallback(() => {
onOpen(); onOpen();
}, [ onOpen ]); }, [ onOpen ]);
...@@ -47,7 +51,7 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange } ...@@ -47,7 +51,7 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }
inputRef.current?.querySelector('input')?.focus(); inputRef.current?.querySelector('input')?.focus();
}, [ handleSearchTermChange ]); }, [ handleSearchTermChange ]);
const menuPaddingX = isMobile ? 32 : 0; const menuPaddingX = isMobile ? 24 : 0;
const calculateMenuWidth = React.useCallback(() => { const calculateMenuWidth = React.useCallback(() => {
menuWidth.current = (inputRef.current?.getBoundingClientRect().width || 0) - menuPaddingX; menuWidth.current = (inputRef.current?.getBoundingClientRect().width || 0) - menuPaddingX;
}, [ menuPaddingX ]); }, [ menuPaddingX ]);
...@@ -68,17 +72,15 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange } ...@@ -68,17 +72,15 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }
}; };
}, [ calculateMenuWidth ]); }, [ calculateMenuWidth ]);
const isSuggestOpen = isOpen && recentSearchKeywords.length > 0 && searchTerm.trim().length === 0; const isSuggestOpen = open && recentSearchKeywords.length > 0 && searchTerm.trim().length === 0;
return ( return (
<> <>
<Popover <PopoverRoot
isOpen={ isSuggestOpen } open={ isSuggestOpen }
autoFocus={ false } autoFocus={ false }
onClose={ onClose } onOpenChange={ handleOpenChange }
placement="bottom-start" positioning={{ offset: { mainAxis: isMobile ? 0 : 8, crossAxis: isMobile ? 12 : 0 } }}
offset={ isMobile ? [ 16, -12 ] : [ 0, 8 ] }
isLazy
> >
<PopoverTrigger> <PopoverTrigger>
<SearchBarInput <SearchBarInput
...@@ -98,7 +100,7 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange } ...@@ -98,7 +100,7 @@ const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/> <SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Popover> </PopoverRoot>
<SearchBarBackdrop isOpen={ isSuggestOpen }/> <SearchBarBackdrop isOpen={ isSuggestOpen }/>
</> </>
); );
......
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