Commit d58af66d authored by tom's avatar tom

support ENS in search

parent 831240f1
......@@ -55,6 +55,20 @@ export const address1: SearchResultAddressOrContract = {
url: '/address/0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
};
export const address2: SearchResultAddressOrContract = {
address: '0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131b',
name: null,
type: 'address' as const,
is_smart_contract_verified: false,
url: '/address/0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131b',
ens_info: {
address_hash: '0x1234567890123456789012345678901234567890',
expiry_date: '2022-12-11T17:55:20Z',
name: 'utko.eth',
names_count: 1,
},
};
export const contract1: SearchResultAddressOrContract = {
address: '0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
name: 'Unknown contract in this network',
......
......@@ -23,6 +23,12 @@ export interface SearchResultAddressOrContract {
address: string;
is_smart_contract_verified: boolean;
url?: string; // not used by the frontend, we build the url ourselves
ens_info?: {
address_hash: string;
expiry_date: string;
name: string;
names_count: number;
};
}
export interface SearchResultLabel {
......
......@@ -32,6 +32,7 @@ test.describe('search by name ', () => {
searchMock.token1,
searchMock.token2,
searchMock.contract1,
searchMock.address2,
searchMock.label1,
],
}),
......
import { Flex, Grid, Icon, Image, Box, Text, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import { chakra, Flex, Grid, Icon, Image, Box, Text, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';
......@@ -13,6 +13,7 @@ import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
......@@ -75,7 +76,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
case 'contract':
case 'address': {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const shouldHighlightHash = ADDRESS_REGEXP.test(searchTerm);
const address = {
hash: data.address,
is_contract: data.type === 'contract',
......@@ -267,8 +268,21 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
}
case 'contract':
case 'address': {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
return data.name ? <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/> : null;
const shouldHighlightHash = ADDRESS_REGEXP.test(searchTerm);
const addressName = data.name || data.ens_info?.name;
return addressName ? (
<>
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/>
{ data.ens_info &&
(
data.ens_info.names_count > 1 ?
<chakra.span color="text_secondary"> (+{ data.ens_info.names_count - 1 })</chakra.span> :
<chakra.span color="text_secondary"> (expires { dayjs(data.ens_info.expiry_date).fromNow() })</chakra.span>
)
}
</>
) :
null;
}
default:
......
import { Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import { chakra, Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode, Tag } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';
......@@ -13,6 +13,7 @@ import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
......@@ -22,7 +23,6 @@ import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
interface Props {
data: SearchResultItem | SearchResultAppItem;
searchTerm: string;
......@@ -93,8 +93,8 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
case 'contract':
case 'address': {
if (data.name) {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const shouldHighlightHash = ADDRESS_REGEXP.test(searchTerm);
const addressName = data.name || data.ens_info?.name;
const address = {
hash: data.address,
is_contract: data.type === 'contract',
......@@ -106,7 +106,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
return (
<>
<Td fontSize="sm">
<Td fontSize="sm" colSpan={ addressName ? 1 : 3 }>
<AddressEntity.Container>
<AddressEntity.Icon address={ address }/>
<AddressEntity.Link
......@@ -124,41 +124,19 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td>
{ addressName && (
<Td colSpan={ 2 } fontSize="sm" verticalAlign="middle">
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/>
</Td>
</>
);
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(addressName) : highlightText(addressName, searchTerm) }}/>
{ data.ens_info &&
(
data.ens_info.names_count > 1 ?
<chakra.span color="text_secondary"> (+{ data.ens_info.names_count - 1 })</chakra.span> :
<chakra.span color="text_secondary"> (expires { dayjs(data.ens_info.expiry_date).fromNow() })</chakra.span>
)
}
const address = {
hash: data.address,
is_contract: data.type === 'contract',
is_verified: data.is_smart_contract_verified,
name: null,
implementation_name: null,
ens_domain_name: null,
};
return (
<Td colSpan={ 3 } fontSize="sm">
<AddressEntity.Container>
<AddressEntity.Icon address={ address }/>
<AddressEntity.Link
address={ address }
onClick={ handleLinkClick }
>
<AddressEntity.Content
asProp="mark"
address={ address }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td>
) }
</>
);
}
......
......@@ -65,6 +65,7 @@ test('search by contract name +@mobile +@dark-mode', async({ mount, page }) =>
status: 200,
body: JSON.stringify([
searchMock.contract1,
searchMock.address2,
]),
}));
......
import { Box, Text, Flex } from '@chakra-ui/react';
import { chakra, Box, Text, Flex } from '@chakra-ui/react';
import React from 'react';
import type { SearchResultAddressOrContract } from 'types/api/search';
import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
......@@ -14,7 +16,8 @@ interface Props {
}
const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const shouldHighlightHash = ADDRESS_REGEXP.test(searchTerm);
const icon = (
<AddressEntity.Icon
address={{
......@@ -27,17 +30,26 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
}}
/>
);
const name = data.name && (
const addressName = data.name || data.ens_info?.name;
const nameEl = addressName && (
<Text
variant="secondary"
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
>
<span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/>
<chakra.span fontWeight={ 500 } dangerouslySetInnerHTML={{ __html: highlightText(addressName, searchTerm) }}/>
{ data.ens_info &&
(
data.ens_info.names_count > 1 ?
<span> (+{ data.ens_info.names_count - 1 })</span> :
<span> (expires { dayjs(data.ens_info.expiry_date).fromNow() })</span>
)
}
</Text>
);
const address = <HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>;
const addressEl = <HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>;
if (isMobile) {
return (
......@@ -51,10 +63,10 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
whiteSpace="nowrap"
fontWeight={ 700 }
>
{ address }
{ addressEl }
</Box>
</Flex>
{ name }
{ nameEl }
</>
);
}
......@@ -70,10 +82,10 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
whiteSpace="nowrap"
fontWeight={ 700 }
>
{ address }
{ addressEl }
</Box>
</Flex>
{ name }
{ nameEl }
</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