Commit 4979acc7 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into contract-logs

parents 34f5d0ef b39137ac
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.224 8.076V6.048h9.557v2.028H13.22v9.608h-2.432V8.076H7.224Z" fill="currentColor"/>
</svg>
......@@ -129,6 +129,7 @@ function makePolicyMap() {
// ad
'servedbyadbutler.com',
'cdn.coinzilla.io',
],
'font-src': [
......
......@@ -17,7 +17,7 @@ const relativeTimeConfig = {
{ l: 'dd', r: 29, d: 'day' },
{ l: 'M', r: 1 },
{ l: 'MM', r: 11, d: 'month' },
{ l: 'y' },
{ l: 'y', r: 17 },
{ l: 'yy', d: 'year' },
],
};
......
export const duck = {
ad: {
name: 'Hello utia!',
description_short: 'Utia is the best! Go with utia! Utia is the best! Go with utia!',
thumbnail: 'https://utia.utia',
url: 'https://test.url',
cta_button: 'Click me!',
},
};
export default async function insertAdText() {
const ad = document.getElementsByClassName('coinzilla');
ad[0].textContent = 'coinzilla banner!';
}
import type { Page } from 'playwright-core';
export default async function insertAdPlaceholder(page: Page) {
await page.waitForSelector('#adBanner', { state: 'attached' });
await page.evaluate(() => {
const adContainer = document.getElementById('adBanner');
const adReplacer = document.createElement('div');
adReplacer.style.width = '200px';
adReplacer.style.height = '100px';
adReplacer.style.background = '#f00';
adContainer?.replaceChildren(adReplacer);
});
}
import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react';
import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize';
import NextLink from 'next/link';
......@@ -52,6 +52,8 @@ const BlockDetails = () => {
router.push(url, undefined);
}, [ router ]);
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
if (isLoading) {
return <BlockDetailsSkeleton/>;
}
......@@ -68,7 +70,15 @@ const BlockDetails = () => {
return <DataFetchAlert/>;
}
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
const sectionGap = (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor={ borderColor }
/>
);
const { totalReward, staticReward, burntFees, txFees } = getBlockReward(data);
const validatorTitle = getNetworkValidatorTitle();
......@@ -262,7 +272,7 @@ const BlockDetails = () => {
{ /* ADDITIONAL INFO */ }
{ isExpanded && (
<>
{ sectionGap }
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
<DetailsInfoItem
title="Difficulty"
......
import { Grid, GridItem, Skeleton } from '@chakra-ui/react';
import { Grid, GridItem, Skeleton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
const BlockDetailsSkeleton = () => {
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const sectionGap = (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor={ borderColor }
/>
);
return (
<Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
......
......@@ -26,7 +26,6 @@ test('daily txs chart +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, p
<ChainIndicators/>
</TestApp>,
);
await page.waitForResponse(STATS_API_URL),
await page.hover('.ChartOverlay', { position: { x: 100, y: 100 } });
await expect(component).toHaveScreenshot();
......
......@@ -13,6 +13,7 @@ import AddressInternalTxs from 'ui/address/AddressInternalTxs';
import AddressTokenTransfers from 'ui/address/AddressTokenTransfers';
import AddressTxs from 'ui/address/AddressTxs';
import AddressLogs from 'ui/address/logs/AddressLogs';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
......@@ -48,20 +49,19 @@ const AddressPageContent = () => {
].filter(notEmpty);
}, [ isContract ]);
const tagsNode = tags.length > 0 ? <Flex columnGap={ 2 }>{ tags }</Flex> : null;
return (
<Page>
<Flex alignItems="center" columnGap={ 3 }>
{ addressQuery.isLoading ? (
<Skeleton h={ 10 } w="260px" mb={ 6 }/>
) : (
<PageTitle text={ `${ addressQuery.data?.is_contract ? 'Contract' : 'Address' } details` }/>
) }
{ tags.length > 0 && (
<Flex mb={ 6 } columnGap={ 2 }>
{ tags }
</Flex>
) }
</Flex>
<TextAd mb={ 6 }/>
{ addressQuery.isLoading ? (
<Skeleton h={ 10 } w="260px" mb={ 6 }/>
) : (
<PageTitle
text={ `${ addressQuery.data?.is_contract ? 'Contract' : 'Address' } details` }
additionals={ tagsNode }
/>
) }
<AddressDetails addressQuery={ addressQuery }/>
{ addressQuery.isLoading ? <SkeletonTabs/> : <RoutedTabs tabs={ tabs } tabListProps={{ mt: 8 }}/> }
</Page>
......
import { Flex, Icon, Link, Tooltip } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import eastArrowIcon from 'icons/arrows/east.svg';
import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import isBrowser from 'lib/isBrowser';
import BlockDetails from 'ui/block/BlockDetails';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
......@@ -52,16 +51,12 @@ const BlockPageContent = () => {
return (
<Page>
<Flex alignItems="center" columnGap={ 3 }>
{ hasGoBackLink && (
<Tooltip label="Back to blocks list">
<Link mb={ 6 } display="inline-flex" href={ referrer }>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/>
</Link>
</Tooltip>
) }
<PageTitle text={ `Block #${ router.query.id }` }/>
</Flex>
<TextAd mb={ 6 }/>
<PageTitle
text={ `Block #${ router.query.id }` }
backLinkUrl={ hasGoBackLink ? referrer : undefined }
backLinkLabel="Back to blocks list"
/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as textAdMock from 'mocks/ad/textAd';
import * as blockMock from 'mocks/blocks/block';
import * as statsMock from 'mocks/stats/index';
import * as socketServer from 'playwright/fixtures/socketServer';
......@@ -26,6 +27,19 @@ const test = base.extend<socketServer.SocketServerFixture>({
// test cases which use socket cannot run in parallel since the socket server always run on the same port
test.describe.configure({ mode: 'serial' });
test.beforeEach(async({ page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: JSON.stringify(textAdMock.duck),
}));
await page.route(textAdMock.duck.ad.thumbnail, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
});
test('base view +@mobile +@dark-mode', async({ mount, page }) => {
await page.route(BLOCKS_API_URL, (route) => route.fulfill({
status: 200,
......
......@@ -42,7 +42,7 @@ const BlocksPageContent = () => {
return (
<Page>
<PageTitle text="Blocks"/>
<PageTitle text="Blocks" withTextAd/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
......@@ -5,9 +5,9 @@ import * as blockMock from 'mocks/blocks/block';
import * as dailyTxsMock from 'mocks/stats/daily_txs';
import * as statsMock from 'mocks/stats/index';
import * as txMock from 'mocks/txs/tx';
import insertAdText from 'playwright/scripts/insertAdText';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import insertAdPlaceholder from 'playwright/utils/insertAdPlaceholder';
import Home from './Home';
......@@ -42,7 +42,7 @@ test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount,
</TestApp>,
);
await page.evaluate(insertAdText);
await insertAdPlaceholder(page);
await expect(component.locator('main')).toHaveScreenshot();
});
import { Flex, Link, Icon, Tag, Tooltip } from '@chakra-ui/react';
import { Flex, Tag } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import eastArrowIcon from 'icons/arrows/east.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
import isBrowser from 'lib/isBrowser';
import networkExplorers from 'lib/networks/networkExplorers';
import AdBanner from 'ui/shared/ad/AdBanner';
import TextAd from 'ui/shared/ad/TextAd';
import ExternalLink from 'ui/shared/ExternalLink';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -52,36 +51,33 @@ const TransactionPageContent = () => {
return <ExternalLink key={ explorer.baseUrl } title={ `Open in ${ explorer.title }` } href={ url.toString() }/>;
});
const additionals = (
<Flex justifyContent="space-between" alignItems="center" flexGrow={ 1 }>
{ data?.tx_tag && <Tag my={ 2 }>{ data.tx_tag }</Tag> }
{ explorersLinks.length > 0 && (
<Flex
alignItems="center"
flexWrap="wrap"
columnGap={ 6 }
rowGap={ 3 }
ml={{ base: 'initial', lg: 'auto' }}
>
{ explorersLinks }
</Flex>
) }
</Flex>
);
return (
<Page>
<Flex alignItems="flex-start" flexDir={{ base: 'column', lg: 'row' }}>
<Flex alignItems="center" columnGap={ 3 }>
{ hasGoBackLink && (
<Tooltip label="Back to transactions list">
<Link display="inline-flex" href={ referrer } mb={ 6 }>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/>
</Link>
</Tooltip>
) }
<PageTitle text="Transaction details"/>
</Flex>
{ data?.tx_tag && <Tag my={ 2 } ml={ 3 }>{ data.tx_tag }</Tag> }
{ explorersLinks.length > 0 && (
<Flex
alignItems="center"
flexWrap="wrap"
columnGap={ 6 }
rowGap={ 3 }
ml={{ base: 'initial', lg: 'auto' }}
mb={{ base: 6, lg: 'initial' }}
py={ 2.5 }
>
{ explorersLinks }
</Flex>
) }
</Flex>
<TextAd mb={ 6 }/>
<PageTitle
text="Transaction details"
additionals={ additionals }
backLinkUrl={ hasGoBackLink ? referrer : undefined }
backLinkLabel="Back to transactions list"
/>
<RoutedTabs tabs={ TABS }/>
<AdBanner mt={ 6 } justifyContent={{ base: 'center', lg: 'start' }}/>
</Page>
);
};
......
......@@ -37,7 +37,7 @@ const Transactions = () => {
return (
<Page hideMobileHeaderOnScrollDown>
<Box h="100%">
<PageTitle text="Transactions"/>
<PageTitle text="Transactions" withTextAd/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
......@@ -30,7 +30,5 @@ test('with indexing alert +@mobile', async({ mount, page }) => {
</TestApp>,
);
await page.waitForResponse(API_URL),
await expect(component).toHaveScreenshot();
});
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as textAdMock from 'mocks/ad/textAd';
import TestApp from 'playwright/TestApp';
import PageTitle from './PageTitle';
test.beforeEach(async({ page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: JSON.stringify(textAdMock.duck),
}));
await page.route(textAdMock.duck.ad.thumbnail, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
});
test('default view +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="Title"
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with text ad, back link and addons +@mobile +@dark-mode', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="Title"
withTextAd
backLinkLabel="Back"
backLinkUrl="back"
additionals="Privet"
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('long title with text ad, back link and addons +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="This title is long, really long"
withTextAd
backLinkLabel="Back"
backLinkUrl="back"
additionals="Privet, kak dela?"
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import { Heading, chakra } from '@chakra-ui/react';
import { Heading, Flex, Tooltip, Link, Icon, chakra } from '@chakra-ui/react';
import React from 'react';
const PageTitle = ({ text, className }: {text: string; className?: string}) => {
import eastArrowIcon from 'icons/arrows/east.svg';
import TextAd from 'ui/shared/ad/TextAd';
type Props = {
text: string;
additionals?: React.ReactNode;
withTextAd?: boolean;
className?: string;
backLinkLabel?: string;
backLinkUrl?: string;
}
const PageTitle = ({ text, additionals, withTextAd, backLinkUrl, backLinkLabel, className }: Props) => {
const title = (
<Heading
as="h1"
size="lg"
flex="none"
overflow="hidden"
textOverflow="ellipsis"
whiteSpace="nowrap"
width={ backLinkUrl ? 'calc(100% - 36px)' : '100%' }
>
{ text }
</Heading>
);
return (
<Heading as="h1" size="lg" marginBottom={ 6 } className={ className }>{ text }</Heading>
<Flex
columnGap={ 3 }
rowGap={ 3 }
alignItems={{ base: 'start', lg: 'center' }}
flexDirection={{ base: 'column', lg: 'row' }}
mb={ 6 }
justifyContent="space-between"
className={ className }
>
<Flex flexWrap="wrap" columnGap={ 3 } alignItems="center" width={ withTextAd ? 'unset' : '100%' }>
<Flex
flexWrap="nowrap"
alignItems="center"
columnGap={ 3 }
overflow="hidden"
>
{ backLinkUrl && (
<Tooltip label={ backLinkLabel }>
<Link display="inline-flex" href={ backLinkUrl }>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/>
</Link>
</Tooltip>
) }
{ title }
</Flex>
{ additionals }
</Flex>
{ withTextAd && <TextAd flexShrink={ 100 }/> }
</Flex>
);
};
......
import { Image, Center, chakra, useColorModeValue } from '@chakra-ui/react';
import { Image, chakra, useColorModeValue, Icon } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
import tokenPlaceholderIcon from 'icons/token-placeholder.svg';
const EmptyElement = ({ className, letter }: { className?: string; letter: string }) => {
const EmptyElement = ({ className }: { className?: string }) => {
const bgColor = useColorModeValue('gray.200', 'gray.600');
const color = useColorModeValue('gray.400', 'gray.200');
return (
<Center
<Icon
className={ className }
fontWeight={ 600 }
bgColor={ bgColor }
color={ color }
borderRadius="base"
>
{ letter.toUpperCase() }
</Center>
as={ tokenPlaceholderIcon }
/>
);
};
......@@ -41,7 +41,7 @@ const TokenLogo = ({ hash, name, className }: Props) => {
className={ className }
src={ logoSrc }
alt={ `${ name || 'token' } logo` }
fallback={ <EmptyElement className={ className } letter={ name?.slice(0, 1) || 'U' }/> }
fallback={ <EmptyElement className={ className }/> }
/>
);
};
......
......@@ -9,12 +9,13 @@ interface Props {
hash: string;
name?: string | null;
className?: string;
logoSize?: number;
}
const TokenSnippet = ({ symbol, hash, name, className }: Props) => {
const TokenSnippet = ({ symbol, hash, name, className, logoSize = 6 }: Props) => {
return (
<Flex className={ className } alignItems="center" columnGap={ 1 } w="100%">
<TokenLogo boxSize={ 5 } hash={ hash } name={ name }/>
<Flex className={ className } alignItems="center" columnGap={ 2 } w="100%">
<TokenLogo boxSize={ logoSize } hash={ hash } name={ name }/>
<AddressLink hash={ hash } alias={ name } type="token"/>
{ symbol && <Text variant="secondary">({ symbol })</Text> }
</Flex>
......
/* eslint-disable max-len */
import { Flex, chakra } from '@chakra-ui/react';
import Script from 'next/script';
import React from 'react';
// eslint-disable-next-line @typescript-eslint/no-var-requires
// const adbutlerHTML = require('ui/shared/ad/adbutler.html');
// didn't find a way to raw-load html that works both for webpack (app build) and vite (playwright build)
const adbutlerHTML = `
<div id="ad-banner"></div>
<script type="text/javascript">if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}</script>
<script type="text/javascript">
var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || [];
const scriptText1 = `if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}`;
const scriptText2 = `
var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || [];
var abkw = window.abkw || '';
const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;
if (isMobile) {
......@@ -23,12 +18,15 @@ const adbutlerHTML = `
document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_523705_'+plc523705+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(182226, 523705, [728,90], 'placement_523705_'+opt.place, opt); }, opt: { place: plc523705++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
}
</script>
`;
const AdbutlerBanner = ({ className }: { className?: string }) => {
return (
<Flex className={ className } dangerouslySetInnerHTML={{ __html: adbutlerHTML }}/>
<Flex className={ className } id="adBanner">
<div id="ad-banner"></div>
<Script id="ad-butler-1">{ scriptText1 }</Script>
<Script id="ad-butler-2">{ scriptText2 }</Script>
</Flex>
);
};
......
import { Flex, chakra } from '@chakra-ui/react';
import Script from 'next/script';
import React from 'react';
import isBrowser from 'lib/isBrowser';
......@@ -18,18 +19,21 @@ type CPreferences = {
const CoinzillaBanner = ({ className }: { className?: string }) => {
const isInBrowser = isBrowser();
if (isInBrowser) {
window.coinzilla_display = window.coinzilla_display || [];
const cDisplayPreferences = {} as CPreferences;
cDisplayPreferences.zone = '26660bf627543e46851';
cDisplayPreferences.width = '728';
cDisplayPreferences.height = '90';
window.coinzilla_display.push(cDisplayPreferences);
}
React.useEffect(() => {
if (isInBrowser) {
window.coinzilla_display = window.coinzilla_display || [];
const cDisplayPreferences = {} as CPreferences;
cDisplayPreferences.zone = '26660bf627543e46851';
cDisplayPreferences.width = '728';
cDisplayPreferences.height = '90';
window.coinzilla_display.push(cDisplayPreferences);
}
}, [ isInBrowser ]);
return (
<Flex className={ className }>
<script async src="https://coinzillatag.com/lib/display.js"></script>
<Flex className={ className } id="adBanner">
<Script src="https://coinzillatag.com/lib/display.js"/>
<div className="coinzilla" data-zone="C-26660bf627543e46851"></div>
</Flex>
);
......
import { Box, Image, Link, Text, chakra } from '@chakra-ui/react';
import React, { useEffect } from 'react';
import { ndash } from 'lib/html-entities';
type AdData = {
ad: {
name: string;
description_short: string;
thumbnail: string;
url: string;
cta_button: string;
impressionUrl: string;
};
}
const CoinzillaTextAd = ({ className }: {className?: string}) => {
const [ adData, setAdData ] = React.useState<AdData | null>(null);
useEffect(() => {
fetch('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242')
.then(res => res.status === 200 ? res.json() : null)
.then((data) => {
setAdData(data);
if (data?.ad?.impressionUrl) {
fetch(data.ad.impressionUrl);
}
});
}, []);
if (!adData) {
return null;
}
const urlObject = new URL(adData.ad.url);
return (
<Box className={ className }>
<Text
as="span"
whiteSpace="pre-wrap"
fontWeight={ 700 }
mr={ 3 }
display={{ base: 'none', lg: 'inline' }}
>
ADs:
</Text>
{ urlObject.hostname === 'nifty.ink' ?
<Text as="span" mr={ 1 }>🎨</Text> :
<Image src={ adData.ad.thumbnail } width="20px" height="20px" mb="-4px" mr={ 1 } display="inline" alt=""/>
}
<Text as="span" whiteSpace="pre-wrap">{ `${ adData.ad.name } ${ ndash } ${ adData.ad.description_short } ` }</Text>
<Link href={ adData.ad.url }>{ adData.ad.cta_button }</Link>
</Box>
);
};
export default chakra(CoinzillaTextAd);
import { chakra } from '@chakra-ui/react';
import React from 'react';
import isSelfHosted from 'lib/isSelfHosted';
import CoinzillaTextAd from './CoinzillaTextAd';
const TextAd = ({ className }: {className?: string}) => {
if (!isSelfHosted()) {
return null;
}
return <CoinzillaTextAd className={ className }/>;
};
export default chakra(TextAd);
<div id="ad-banner"></div>
<script type="text/javascript">if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}</script>
<script type="text/javascript">
var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || [];
var abkw = window.abkw || '';
const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;
if (isMobile) {
var plc539876 = window.plc539876 || 0;
document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_539876_'+plc539876+'"></'+'div>';
document.getElementById("ad-banner").className = "ad-container mb-3";
AdButler.ads.push({handler: function(opt){ AdButler.register(182226, 539876, [320,100], 'placement_539876_'+opt.place, opt); }, opt: { place: plc539876++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
} else {
var plc523705 = window.plc523705 || 0;
document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_523705_'+plc523705+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(182226, 523705, [728,90], 'placement_523705_'+opt.place, opt); }, opt: { place: plc523705++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
}
</script>
\ No newline at end of file
......@@ -26,7 +26,7 @@ const NftTokenTransferSnippet = ({ value, name, hash, symbol, tokenId }: Props)
<Link href={ url } fontWeight={ 600 }>{ tokenId }</Link>
</Box>
{ name ? (
<TokenSnippet symbol={ symbol } hash={ hash } name={ name } w="auto"/>
<TokenSnippet symbol={ symbol } hash={ hash } name={ name } w="auto" logoSize={ 5 } columnGap={ 1 }/>
) : (
<AddressLink hash={ hash } truncation="constant" type="token"/>
) }
......
......@@ -4,6 +4,7 @@ import React from 'react';
import * as txMock from 'mocks/txs/tx';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import insertAdPlaceholder from 'playwright/utils/insertAdPlaceholder';
import TxDetails from './TxDetails';
......@@ -28,6 +29,7 @@ test('between addresses +@mobile +@dark-mode', async({ mount, page }) => {
);
await page.getByText('View details').click();
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
......@@ -45,6 +47,8 @@ test('creating contact', async({ mount, page }) => {
{ hooksConfig },
);
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
......@@ -61,6 +65,8 @@ test('with token transfer +@mobile', async({ mount, page }) => {
{ hooksConfig },
);
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
......@@ -77,6 +83,8 @@ test('with decoded revert reason', async({ mount, page }) => {
{ hooksConfig },
);
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
......@@ -93,6 +101,8 @@ test('with decoded raw reason', async({ mount, page }) => {
{ hooksConfig },
);
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
......@@ -110,6 +120,7 @@ test('pending', async({ mount, page }) => {
);
await page.getByText('View details').click();
await insertAdPlaceholder(page);
await expect(component).toHaveScreenshot();
});
import { Grid, GridItem, Text, Box, Icon, Link, Spinner, Tag, Flex, Tooltip, chakra } from '@chakra-ui/react';
import {
Grid,
GridItem,
Text,
Box,
Icon,
Link,
Spinner,
Tag,
Flex,
Tooltip,
chakra,
useColorModeValue,
} from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import { scroller, Element } from 'react-scroll';
......@@ -10,7 +23,9 @@ import errorIcon from 'icons/status/error.svg';
import successIcon from 'icons/status/success.svg';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
import dayjs from 'lib/date/dayjs';
import useIsMobile from 'lib/hooks/useIsMobile';
import getConfirmationDuration from 'lib/tx/getConfirmationDuration';
import AdBanner from 'ui/shared/ad/AdBanner';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -34,6 +49,10 @@ import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxDetails = () => {
const { data, isLoading, isError, socketStatus, error } = useFetchTxInfo();
const isMobile = useIsMobile();
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleCutClick = React.useCallback(() => {
......@@ -148,7 +167,29 @@ const TxDetails = () => {
<Text variant="secondary">{ getConfirmationDuration(data.confirmation_duration) }</Text>
</DetailsInfoItem>
) }
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/>
{ isMobile ?
(
<GridItem
colSpan={{ base: undefined, lg: 2 }}
>
<AdBanner justifyContent="center"/>
</GridItem>
) :
(
<DetailsInfoItem
title="Sponsored"
hint="Sponsored banner advertisement"
>
<AdBanner/>
</DetailsInfoItem>
) }
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor={ borderColor }
/>
<DetailsInfoItem
title="From"
hint="Address (external or contract) sending the transaction."
......@@ -199,7 +240,13 @@ const TxDetails = () => {
</DetailsInfoItem>
{ data.token_transfers && <TxDetailsTokenTransfers data={ data.token_transfers } txHash={ data.hash }/> }
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/>
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor={ borderColor }
/>
<DetailsInfoItem
title="Value"
hint="Value sent in the native token (and USD) if applicable."
......
import { Grid, GridItem, Skeleton } from '@chakra-ui/react';
import { Grid, GridItem, Skeleton, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
const TxDetailsSkeleton = () => {
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const sectionGap = (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor={ borderColor }
/>
);
return (
<Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
......
......@@ -25,7 +25,15 @@ const TxDetailsTokenTransfer = ({ token, total, to, from }: Props) => {
<Text fontWeight={ 500 } as="span">For:{ space }
<CurrencyValue value={ payload.value } exchangeRate={ token.exchange_rate } fontWeight={ 600 }/>
</Text>
<TokenSnippet symbol={ token.symbol } hash={ token.address } name={ token.name } w="auto" flexGrow="1"/>
<TokenSnippet
symbol={ token.symbol }
hash={ token.address }
name={ token.name }
w="auto"
flexGrow="1"
columnGap={ 1 }
logoSize={ 5 }
/>
</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