Public tags: abstract deposit name tags (#2972)
* Public tags: abstract deposit name tags Fixes #2964 * rename cex_deposit to cexDeposit
@@ -31,6 +31,7 @@ export default function parseMetaPayload(meta: AddressMetadataTag['meta']): Addr
|
||||
'alertBgColor',
|
||||
'alertTextColor',
|
||||
'alertStatus',
|
||||
'cexDeposit',
|
||||
];
|
||||
|
||||
for (const stringField of stringFields) {
|
||||
|
||||
@@ -9,6 +9,16 @@ export const nameTag: AddressMetadataTagApi = {
|
||||
meta: null,
|
||||
};
|
||||
|
||||
export const cexDepositTag: AddressMetadataTagApi = {
|
||||
name: 'Duck deposit address',
|
||||
tagType: 'name',
|
||||
ordinal: 42,
|
||||
slug: 'duck-deposit-address',
|
||||
meta: {
|
||||
cexDeposit: 'true',
|
||||
},
|
||||
};
|
||||
|
||||
export const customNameTag: AddressMetadataTagApi = {
|
||||
slug: 'unicorn-uproar',
|
||||
name: 'Unicorn Uproar',
|
||||
|
||||
@@ -169,7 +169,9 @@ export const metatag2: SearchResultMetadataTag = {
|
||||
metadata: {
|
||||
name: 'utko',
|
||||
slug: 'utko',
|
||||
meta: {},
|
||||
meta: {
|
||||
cexDeposit: 'true',
|
||||
},
|
||||
tagType: 'name',
|
||||
ordinal: 1,
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface AddressMetadataTagApi extends Omit<AddressMetadataTag, 'meta'>
|
||||
alertBgColor?: string;
|
||||
alertTextColor?: string;
|
||||
alertStatus?: string;
|
||||
cexDeposit?: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ const OpSuperchainAddress = () => {
|
||||
<EntityTags
|
||||
tags={ tags }
|
||||
isLoading={ isLoading }
|
||||
addressHash={ checkSummedHash }
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -380,6 +380,7 @@ const AddressPageContent = () => {
|
||||
const titleContentAfter = (
|
||||
<EntityTags
|
||||
tags={ tags }
|
||||
addressHash={ addressQuery.data?.hash }
|
||||
isLoading={
|
||||
isLoading ||
|
||||
(config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData) ||
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 41 KiB |
@@ -6,19 +6,23 @@ import highlightText from 'lib/highlightText';
|
||||
import type { TagProps } from 'toolkit/chakra/tag';
|
||||
import { Tag } from 'toolkit/chakra/tag';
|
||||
import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon';
|
||||
import { getTagName } from 'ui/shared/EntityTags/utils';
|
||||
|
||||
interface Props extends TagProps {
|
||||
metadata: AddressMetadataTagApi;
|
||||
searchTerm: string;
|
||||
addressHash?: string;
|
||||
}
|
||||
|
||||
const SearchResultEntityTag = ({ metadata, searchTerm, ...rest }: Props) => {
|
||||
const SearchResultEntityTag = ({ metadata, searchTerm, addressHash, ...rest }: Props) => {
|
||||
const name = getTagName(metadata, addressHash);
|
||||
|
||||
return (
|
||||
<Tag
|
||||
{ ...rest }
|
||||
startElement={ <EntityTagIcon data={ metadata } ignoreColor/> }
|
||||
>
|
||||
<span dangerouslySetInnerHTML={{ __html: highlightText(metadata.name, searchTerm) }}/>
|
||||
<span dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}/>
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -433,7 +433,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
|
||||
</Flex>
|
||||
) }
|
||||
{ data.type === 'metadata_tag' && (
|
||||
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm }/>
|
||||
<SearchResultEntityTag metadata={ data.metadata } addressHash={ data.address_hash } searchTerm={ searchTerm }/>
|
||||
) }
|
||||
</Flex>
|
||||
) :
|
||||
|
||||
@@ -170,7 +170,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
|
||||
) }
|
||||
{ data.type === 'metadata_tag' && (
|
||||
<TableCell colSpan={ addressName ? 1 : 2 } verticalAlign="middle" textAlign="right">
|
||||
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm }/>
|
||||
<SearchResultEntityTag metadata={ data.metadata } addressHash={ hash } searchTerm={ searchTerm }/>
|
||||
</TableCell>
|
||||
) }
|
||||
</>
|
||||
|
||||
@@ -13,6 +13,15 @@ test('custom name tag +@dark-mode', async({ render }) => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('cex deposit tag', async({ render }) => {
|
||||
const component = await render(
|
||||
<Box w="200px">
|
||||
<EntityTag data={ addressMetadataMock.cexDepositTag } addressHash="0x1234567890123456789012345678901234567890"/>
|
||||
</Box>,
|
||||
);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('warpcast tag', async({ render }) => {
|
||||
const component = await render(<Box w="200px"><EntityTag data={ addressMetadataMock.warpcastTag }/></Box>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
|
||||
@@ -11,15 +11,16 @@ import { Tag } from 'toolkit/chakra/tag';
|
||||
|
||||
import EntityTagIcon from './EntityTagIcon';
|
||||
import EntityTagTooltip from './EntityTagTooltip';
|
||||
import { getTagLinkParams } from './utils';
|
||||
import { getTagName, getTagLinkParams } from './utils';
|
||||
|
||||
interface Props extends HTMLChakraProps<'span'> {
|
||||
data: TEntityTag;
|
||||
addressHash?: string;
|
||||
isLoading?: boolean;
|
||||
noLink?: boolean;
|
||||
}
|
||||
|
||||
const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => {
|
||||
const EntityTag = ({ data, addressHash, isLoading, noLink, ...rest }: Props) => {
|
||||
const multichainContext = useMultichainContext();
|
||||
|
||||
const linkParams = !noLink ? getTagLinkParams(data, multichainContext) : undefined;
|
||||
@@ -47,7 +48,7 @@ const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => {
|
||||
return `@${ data.meta.warpcastHandle }`;
|
||||
}
|
||||
|
||||
return data.name;
|
||||
return getTagName(data, addressHash);
|
||||
})();
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,10 +13,11 @@ import EntityTag from './EntityTag';
|
||||
interface Props {
|
||||
className?: string;
|
||||
tags: Array<TEntityTag>;
|
||||
addressHash?: string;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const EntityTags = ({ tags, className, isLoading }: Props) => {
|
||||
const EntityTags = ({ tags, addressHash, className, isLoading }: Props) => {
|
||||
const isMobile = useIsMobile();
|
||||
const visibleNum = isMobile ? 2 : 3;
|
||||
|
||||
@@ -43,7 +44,15 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
|
||||
if (tags.length > visibleNum) {
|
||||
return (
|
||||
<>
|
||||
{ tags.slice(0, visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag } isLoading={ isLoading } maxW={ tagMaxW }/>) }
|
||||
{ tags.slice(0, visibleNum).map((tag) => (
|
||||
<EntityTag
|
||||
key={ tag.slug }
|
||||
data={ tag }
|
||||
addressHash={ addressHash }
|
||||
isLoading={ isLoading }
|
||||
maxW={ tagMaxW }
|
||||
/>
|
||||
)) }
|
||||
{ metaSuitesPlaceholder }
|
||||
<PopoverRoot>
|
||||
<PopoverTrigger>
|
||||
@@ -54,7 +63,7 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
|
||||
<PopoverContent maxW="300px" w="fit-content">
|
||||
<PopoverBody>
|
||||
<Flex columnGap={ 2 } rowGap={ 2 } flexWrap="wrap">
|
||||
{ tags.slice(visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag }/>) }
|
||||
{ tags.slice(visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag } addressHash={ addressHash }/>) }
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
@@ -65,7 +74,7 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{ tags.map((tag) => <EntityTag key={ tag.slug } data={ tag } isLoading={ isLoading } maxW={ tagMaxW }/>) }
|
||||
{ tags.map((tag) => <EntityTag key={ tag.slug } data={ tag } addressHash={ addressHash } isLoading={ isLoading } maxW={ tagMaxW }/>) }
|
||||
{ metaSuitesPlaceholder }
|
||||
</>
|
||||
);
|
||||
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -26,3 +26,11 @@ export function getTagLinkParams(data: EntityTag, multichainContext?: TMultichai
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getTagName(data: EntityTag, addressHash?: string) {
|
||||
if (data.tagType === 'name' && data.meta?.cexDeposit === 'true' && addressHash) {
|
||||
return `${ data.name } (${ addressHash.slice(0, 2 + 5) })`;
|
||||
}
|
||||
|
||||
return data.name;
|
||||
}
|
||||
|
||||
@@ -219,6 +219,22 @@ test('with name tag', async({ render }) => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.describe('with cex deposit tag', () => {
|
||||
test.use({ viewport: { width: 500, height: 140 } });
|
||||
|
||||
test('default', async({ render }) => {
|
||||
const address = { ...addressMock.withNameTag, metadata: { reputation: 1, tags: [ metadataMock.cexDepositTag ] } };
|
||||
|
||||
const component = await render(
|
||||
<AddressEntity
|
||||
address={ address }
|
||||
/>,
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('external link', async({ render }) => {
|
||||
const component = await render(
|
||||
<AddressEntity
|
||||
|
||||
@@ -12,6 +12,7 @@ import getIconUrl from 'lib/multichain/getIconUrl';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import * as EntityBase from 'ui/shared/entities/base/components';
|
||||
import { getTagName } from 'ui/shared/EntityTags/utils';
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
|
||||
import { distributeEntityProps, getContentProps, getIconProps } from '../base/utils';
|
||||
@@ -119,7 +120,14 @@ export type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<Enti
|
||||
|
||||
const Content = chakra((props: ContentProps) => {
|
||||
const displayedAddress = getDisplayedAddress(props.address, props.altHash);
|
||||
const nameTag = props.address.metadata?.tags.find(tag => tag.tagType === 'name')?.name;
|
||||
const nameTag = (() => {
|
||||
const tagData = props.address.metadata?.tags.find(tag => tag.tagType === 'name');
|
||||
if (!tagData || !tagData.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
return getTagName(tagData, props.address.hash);
|
||||
})();
|
||||
const nameText = nameTag || props.address.ens_domain_name || props.address.name;
|
||||
|
||||
const isProxy = props.address.implementations && props.address.implementations.length > 0 && props.address.proxy_type !== 'eip7702';
|
||||
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -53,7 +53,7 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm, addressFormat }:
|
||||
</Flex>
|
||||
);
|
||||
const tagEl = data.type === 'metadata_tag' ? (
|
||||
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm } ml={{ base: 0, lg: 'auto' }}/>
|
||||
<SearchResultEntityTag metadata={ data.metadata } addressHash={ hash } searchTerm={ searchTerm } ml={{ base: 0, lg: 'auto' }}/>
|
||||
) : null;
|
||||
const addressEl = <HashStringShortenDynamic hash={ hash } noTooltip/>;
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 42 KiB |
@@ -100,6 +100,7 @@ const TokenPageTitle = ({ tokenQuery, addressQuery, hash }: Props) => {
|
||||
<EntityTags
|
||||
isLoading={ isLoading || (config.features.addressMetadata.isEnabled && addressMetadataQuery.isPending) }
|
||||
tags={ tags }
|
||||
addressHash={ addressQuery.data?.hash }
|
||||
flexGrow={ 1 }
|
||||
/>
|
||||
</>
|
||||
|
||||