Commit cec56f19 authored by Max Alekseenko's avatar Max Alekseenko Committed by GitHub

Merge pull request #2134 from blockscout/ens-in-tx-interpretation

Display ENS in tx interpretation
parents 5d1edc5a 9fb08da3
...@@ -23,12 +23,15 @@ import { extractVariables, getStringChunks, fillStringVariables, checkSummary, N ...@@ -23,12 +23,15 @@ import { extractVariables, getStringChunks, fillStringVariables, checkSummary, N
type Props = { type Props = {
summary?: TxInterpretationSummary; summary?: TxInterpretationSummary;
isLoading?: boolean; isLoading?: boolean;
ensDomainNames?: Record<string, string>;
className?: string; className?: string;
} }
type NonStringTxInterpretationVariable = Exclude<TxInterpretationVariable, TxInterpretationVariableString> type NonStringTxInterpretationVariable = Exclude<TxInterpretationVariable, TxInterpretationVariableString>
const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInterpretationVariable }) => { const TxInterpretationElementByType = (
{ variable, ensDomainNames }: { variable?: NonStringTxInterpretationVariable; ensDomainNames?: Record<string, string> },
) => {
const onAddressClick = React.useCallback(() => { const onAddressClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Address click' }); mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Address click' });
}, []); }, []);
...@@ -48,10 +51,14 @@ const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInt ...@@ -48,10 +51,14 @@ const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInt
const { type, value } = variable; const { type, value } = variable;
switch (type) { switch (type) {
case 'address': { case 'address': {
let address = value;
if (!address.ens_domain_name && ensDomainNames?.[address.hash]) {
address = { ...address, ens_domain_name: ensDomainNames[address.hash] };
}
return ( return (
<chakra.span display="inline-block" verticalAlign="top" _notFirst={{ marginLeft: 1 }}> <chakra.span display="inline-block" verticalAlign="top" _notFirst={{ marginLeft: 1 }}>
<AddressEntity <AddressEntity
address={ value } address={ address }
truncation="constant" truncation="constant"
onClick={ onAddressClick } onClick={ onAddressClick }
whiteSpace="initial" whiteSpace="initial"
...@@ -122,7 +129,7 @@ const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInt ...@@ -122,7 +129,7 @@ const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInt
} }
}; };
const TxInterpretation = ({ summary, isLoading, className }: Props) => { const TxInterpretation = ({ summary, isLoading, ensDomainNames, className }: Props) => {
if (!summary) { if (!summary) {
return null; return null;
} }
...@@ -151,7 +158,12 @@ const TxInterpretation = ({ summary, isLoading, className }: Props) => { ...@@ -151,7 +158,12 @@ const TxInterpretation = ({ summary, isLoading, className }: Props) => {
{ index < variablesNames.length && ( { index < variablesNames.length && (
variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ? variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ?
<chakra.span>{ currencyUnits.ether + ' ' }</chakra.span> : <chakra.span>{ currencyUnits.ether + ' ' }</chakra.span> :
<TxInterpretationElementByType variable={ variables[variablesNames[index]] as NonStringTxInterpretationVariable }/> (
<TxInterpretationElementByType
variable={ variables[variablesNames[index]] as NonStringTxInterpretationVariable }
ensDomainNames={ ensDomainNames }
/>
)
) } ) }
</chakra.span> </chakra.span>
); );
......
import React from 'react'; import React from 'react';
import type { AddressMetadataInfo, AddressMetadataTagApi } from 'types/api/addressMetadata'; import type { AddressMetadataInfo, AddressMetadataTagApi } from 'types/api/addressMetadata';
import type { AddressParam } from 'types/api/addressParams';
import config from 'configs/app'; import config from 'configs/app';
import { protocolTagWithMeta } from 'mocks/metadata/address'; import { protocolTagWithMeta } from 'mocks/metadata/address';
...@@ -64,6 +65,25 @@ test.describe('blockscout provider', () => { ...@@ -64,6 +65,25 @@ test.describe('blockscout provider', () => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('with interpretation and recipient ENS domain', async({ render, mockApiResponse }) => {
const txData = {
...txMock.base,
to: {
...txMock.base.to,
hash: (txInterpretation.data.summaries[0].summary_template_variables.to_address.value as AddressParam).hash,
ens_domain_name: 'duckduck.eth',
},
};
const txWithEnsQuery = {
data: txData,
isPlaceholderData: false,
isError: false,
} as TxQuery;
await mockApiResponse('tx_interpretation', txInterpretation, { pathParams: { hash } });
const component = await render(<TxSubHeading hash={ hash } hasTag={ false } txQuery={ txWithEnsQuery }/>);
await expect(component).toHaveScreenshot();
});
test('with interpretation and view all link +@mobile', async({ render, mockApiResponse }) => { test('with interpretation and view all link +@mobile', async({ render, mockApiResponse }) => {
await mockApiResponse( await mockApiResponse(
'tx_interpretation', 'tx_interpretation',
......
...@@ -59,14 +59,21 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => { ...@@ -59,14 +59,21 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => {
(hasNovesInterpretation && novesInterpretationQuery.data && !novesInterpretationQuery.isPlaceholderData) || (hasNovesInterpretation && novesInterpretationQuery.data && !novesInterpretationQuery.isPlaceholderData) ||
(hasInternalInterpretation && !txInterpretationQuery.isPlaceholderData); (hasInternalInterpretation && !txInterpretationQuery.isPlaceholderData);
const ensDomainNames: Record<string, string> = {};
[ txQuery.data?.from, txQuery.data?.to ].forEach(data => {
if (data?.hash && data?.ens_domain_name) {
ensDomainNames[data.hash] = data.ens_domain_name;
}
});
const content = (() => { const content = (() => {
if (hasNovesInterpretation && novesInterpretationQuery.data) { if (hasNovesInterpretation && novesInterpretationQuery.data) {
const novesSummary = createNovesSummaryObject(novesInterpretationQuery.data); const novesSummary = createNovesSummaryObject(novesInterpretationQuery.data);
return ( return (
<TxInterpretation <TxInterpretation
summary={ novesSummary } summary={ novesSummary }
isLoading={ novesInterpretationQuery.isPlaceholderData } isLoading={ novesInterpretationQuery.isPlaceholderData || txQuery.isPlaceholderData }
ensDomainNames={ ensDomainNames }
fontSize="lg" fontSize="lg"
mr={{ base: 0, lg: 6 }} mr={{ base: 0, lg: 6 }}
/> />
...@@ -76,7 +83,8 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => { ...@@ -76,7 +83,8 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => {
<Flex mr={{ base: 0, lg: 6 }} flexWrap="wrap" alignItems="center"> <Flex mr={{ base: 0, lg: 6 }} flexWrap="wrap" alignItems="center">
<TxInterpretation <TxInterpretation
summary={ txInterpretationQuery.data?.data.summaries[0] } summary={ txInterpretationQuery.data?.data.summaries[0] }
isLoading={ txInterpretationQuery.isPlaceholderData } isLoading={ txInterpretationQuery.isPlaceholderData || txQuery.isPlaceholderData }
ensDomainNames={ ensDomainNames }
fontSize="lg" fontSize="lg"
mr={ hasViewAllInterpretationsLink ? 3 : 0 } mr={ hasViewAllInterpretationsLink ? 3 : 0 }
/> />
...@@ -114,6 +122,11 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => { ...@@ -114,6 +122,11 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => {
} }
})(); })();
const isLoading =
txQuery.isPlaceholderData ||
(hasNovesInterpretation && novesInterpretationQuery.isPlaceholderData) ||
(hasInternalInterpretation && txInterpretationQuery.isPlaceholderData);
return ( return (
<Box display={{ base: 'block', lg: 'flex' }} alignItems="center" w="100%"> <Box display={{ base: 'block', lg: 'flex' }} alignItems="center" w="100%">
{ content } { content }
...@@ -124,7 +137,7 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => { ...@@ -124,7 +137,7 @@ const TxSubHeading = ({ hash, hasTag, txQuery }: Props) => {
gap={ 3 } gap={ 3 }
mt={{ base: 3, lg: 0 }} mt={{ base: 3, lg: 0 }}
> >
{ !hasTag && <AccountActionsMenu/> } { !hasTag && <AccountActionsMenu isLoading={ isLoading }/> }
{ (appActionData && hasAnyInterpretation) && ( { (appActionData && hasAnyInterpretation) && (
<AppActionButton data={ appActionData } txHash={ hash } source="Txn"/> <AppActionButton data={ appActionData } txHash={ hash } source="Txn"/>
) } ) }
......
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