Commit 504de39d authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Merge pull request #1553 from blockscout/interpretation-domains

add domain type to tx interpretation
parents 9802a721 6c0b765f
...@@ -96,7 +96,7 @@ Type extends EventTypes.PAGE_WIDGET ? ( ...@@ -96,7 +96,7 @@ Type extends EventTypes.PAGE_WIDGET ? (
} }
) : ) :
Type extends EventTypes.TX_INTERPRETATION_INTERACTION ? { Type extends EventTypes.TX_INTERPRETATION_INTERACTION ? {
'Type': 'Address click' | 'Token click'; 'Type': 'Address click' | 'Token click' | 'Domain click';
} : } :
Type extends EventTypes.EXPERIMENT_STARTED ? { Type extends EventTypes.EXPERIMENT_STARTED ? {
'Experiment name': string; 'Experiment name': string;
......
...@@ -17,9 +17,10 @@ export type TxInterpretationVariable = ...@@ -17,9 +17,10 @@ export type TxInterpretationVariable =
TxInterpretationVariableCurrency | TxInterpretationVariableCurrency |
TxInterpretationVariableTimestamp | TxInterpretationVariableTimestamp |
TxInterpretationVariableToken | TxInterpretationVariableToken |
TxInterpretationVariableAddress; TxInterpretationVariableAddress |
TxInterpretationVariableDomain;
export type TxInterpretationVariableType = 'string' | 'currency' | 'timestamp' | 'token' | 'address'; export type TxInterpretationVariableType = 'string' | 'currency' | 'timestamp' | 'token' | 'address' | 'domain';
export type TxInterpretationVariableString = { export type TxInterpretationVariableString = {
type: 'string'; type: 'string';
...@@ -45,3 +46,8 @@ export type TxInterpretationVariableAddress = { ...@@ -45,3 +46,8 @@ export type TxInterpretationVariableAddress = {
type: 'address'; type: 'address';
value: AddressParam; value: AddressParam;
} }
export type TxInterpretationVariableDomain = {
type: 'domain';
value: string;
}
import { Skeleton, Flex, Text, chakra } from '@chakra-ui/react'; import { Skeleton, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { TxInterpretationSummary, TxInterpretationVariable } from 'types/api/txInterpretation'; import type {
TxInterpretationSummary,
TxInterpretationVariable,
TxInterpretationVariableString,
} from 'types/api/txInterpretation';
import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { extractVariables, getStringChunks, NATIVE_COIN_SYMBOL_VAR_NAME } from './utils'; import { extractVariables, getStringChunks, fillStringVariables, NATIVE_COIN_SYMBOL_VAR_NAME } from './utils';
type Props = { type Props = {
summary?: TxInterpretationSummary; summary?: TxInterpretationSummary;
...@@ -19,7 +25,9 @@ type Props = { ...@@ -19,7 +25,9 @@ type Props = {
className?: string; className?: string;
} }
const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretationVariable }) => { type NonStringTxInterpretationVariable = Exclude<TxInterpretationVariable, TxInterpretationVariableString>
const TxInterpretationElementByType = ({ variable }: { variable?: NonStringTxInterpretationVariable }) => {
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' });
}, []); }, []);
...@@ -28,6 +36,10 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati ...@@ -28,6 +36,10 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati
mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Token click' }); mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Token click' });
}, []); }, []);
const onDomainClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Domain click' });
}, []);
if (!variable) { if (!variable) {
return null; return null;
} }
...@@ -36,28 +48,47 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati ...@@ -36,28 +48,47 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati
switch (type) { switch (type) {
case 'address': { case 'address': {
return ( return (
<AddressEntity <chakra.span display="inline-block" verticalAlign="top" _notFirst={{ marginLeft: 1 }}>
address={ value } <AddressEntity
truncation="constant" address={ value }
sx={{ ':not(:first-child)': { marginLeft: 1 } }} truncation="constant"
whiteSpace="initial" onClick={ onAddressClick }
onClick={ onAddressClick } whiteSpace="initial"
/> />
</chakra.span>
); );
} }
case 'token': case 'token':
return ( return (
<TokenEntity <chakra.span display="inline-block" verticalAlign="top" _notFirst={{ marginLeft: 1 }}>
token={ value } <TokenEntity
onlySymbol token={ value }
noCopy onlySymbol
width="fit-content" noCopy
sx={{ ':not(:first-child)': { marginLeft: 1 } }} width="fit-content"
mr={ 2 } _notFirst={{ marginLeft: 1 }}
whiteSpace="initial" mr={ 2 }
onClick={ onTokenClick } whiteSpace="initial"
/> onClick={ onTokenClick }
/>
</chakra.span>
); );
case 'domain': {
if (config.features.nameService.isEnabled) {
return (
<chakra.span display="inline-block" verticalAlign="top" _notFirst={{ marginLeft: 1 }}>
<EnsEntity
name={ value }
width="fit-content"
_notFirst={{ marginLeft: 1 }}
whiteSpace="initial"
onClick={ onDomainClick }
/>
</chakra.span>
);
}
return <chakra.span color="text_secondary" whiteSpace="pre">{ value + ' ' }</chakra.span>;
}
case 'currency': { case 'currency': {
let numberString = ''; let numberString = '';
if (BigNumber(value).isLessThan(0.1)) { if (BigNumber(value).isLessThan(0.1)) {
...@@ -69,14 +100,10 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati ...@@ -69,14 +100,10 @@ const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretati
} else { } else {
numberString = BigNumber(value).dividedBy(1000000).toFormat(2) + 'M'; numberString = BigNumber(value).dividedBy(1000000).toFormat(2) + 'M';
} }
return <Text>{ numberString + ' ' }</Text>; return <chakra.span>{ numberString + ' ' }</chakra.span>;
} }
case 'timestamp': case 'timestamp': {
// timestamp is in unix format return <chakra.span color="text_secondary" whiteSpace="pre">{ dayjs(Number(value) * 1000).format('MMM DD YYYY') }</chakra.span>;
return <Text color="text_secondary">{ dayjs(Number(value) * 1000).format('llll') + ' ' }</Text>;
case 'string':
default: {
return <Text color="text_secondary">{ value.toString() + ' ' }</Text>;
} }
} }
}; };
...@@ -89,23 +116,24 @@ const TxInterpretation = ({ summary, isLoading, className }: Props) => { ...@@ -89,23 +116,24 @@ const TxInterpretation = ({ summary, isLoading, className }: Props) => {
const template = summary.summary_template; const template = summary.summary_template;
const variables = summary.summary_template_variables; const variables = summary.summary_template_variables;
const variablesNames = extractVariables(template); const intermediateResult = fillStringVariables(template, variables);
const chunks = getStringChunks(template); const variablesNames = extractVariables(intermediateResult);
const chunks = getStringChunks(intermediateResult);
return ( return (
<Skeleton display="flex" flexWrap="wrap" alignItems="center" isLoaded={ !isLoading } className={ className }> <Skeleton isLoaded={ !isLoading } className={ className } fontWeight={ 500 } whiteSpace="pre-wrap" >
<IconSvg name="lightning" boxSize={ 5 } color="text_secondary" mr={ 2 }/> <IconSvg name="lightning" boxSize={ 5 } color="text_secondary" mr={ 2 } verticalAlign="text-top"/>
{ chunks.map((chunk, index) => { { chunks.map((chunk, index) => {
return ( return (
<Flex whiteSpace="pre" key={ chunk + index } fontWeight={ 500 }> <chakra.span key={ chunk + index }>
<Text color="text_secondary">{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</Text> <chakra.span color="text_secondary">{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</chakra.span>
{ index < variablesNames.length && ( { index < variablesNames.length && (
variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ? variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ?
<Text>{ currencyUnits.ether + ' ' }</Text> : <chakra.span>{ currencyUnits.ether + ' ' }</chakra.span> :
<TxInterpretationElementByType variable={ variables[variablesNames[index]] }/> <TxInterpretationElementByType variable={ variables[variablesNames[index]] as NonStringTxInterpretationVariable }/>
) } ) }
</Flex> </chakra.span>
); );
}) } }) }
</Skeleton> </Skeleton>
......
// we use that regex as a separator when splitting template and dont want to capture variables // we use that regex as a separator when splitting template and dont want to capture variables
import type { TxInterpretationVariable } from 'types/api/txInterpretation';
// eslint-disable-next-line regexp/no-useless-non-capturing-group // eslint-disable-next-line regexp/no-useless-non-capturing-group
export const VAR_REGEXP = /\{(?:[^}]+)\}/g; export const VAR_REGEXP = /\{(?:[^}]+)\}/g;
...@@ -16,3 +19,16 @@ export function extractVariables(templateString: string) { ...@@ -16,3 +19,16 @@ export function extractVariables(templateString: string) {
export function getStringChunks(template: string) { export function getStringChunks(template: string) {
return template.split(VAR_REGEXP); return template.split(VAR_REGEXP);
} }
export function fillStringVariables(template: string, variables: Record<string, TxInterpretationVariable>) {
const variablesNames = extractVariables(template);
let result = template;
variablesNames.forEach(name => {
if (variables[name] && variables[name].type === 'string') {
result = result.replace(`{${ name }}`, variables[name].value as string);
}
});
return result;
}
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