Skip to content

Commit

Permalink
Merge branch 'main' of github.com:blockscout/frontend into tom2drum/i…
Browse files Browse the repository at this point in the history
…ssue-1414
  • Loading branch information
tom2drum committed Dec 26, 2023
2 parents f8d8b7f + 95243dc commit 1484bb3
Show file tree
Hide file tree
Showing 17 changed files with 221 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ jobs:
cleanup_docker_image:
uses: blockscout/blockscout-ci-cd/.github/workflows/cleanup_docker.yaml@master
with:
dockerImage: prerelease-$GITHUB_REF_NAME_SLUG
dockerImage: review-$GITHUB_REF_NAME_SLUG
secrets: inherit
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
"main.L2",
"poa_core",
"eth_goerli",
"sepolia",
"eth",
"rootstock",
"polygon",
Expand Down
60 changes: 60 additions & 0 deletions configs/envs/.env.sepolia
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Set of ENVs for Sepolia testnet network explorer
# https://eth-sepolia.blockscout.com/

# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000

# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Sepolia
NEXT_PUBLIC_NETWORK_SHORT_NAME=Sepolia
NEXT_PUBLIC_NETWORK_ID=11155111
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth-sepolia.public.blastapi.io
NEXT_PUBLIC_IS_TESTNET=true

# api configuration
NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/

# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)'
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
## footer
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
##views
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'LooksRare','collection_url':'https://sepolia.looksrare.org/collections/{hash}','instance_url':'https://sepolia.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://sepolia.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}},{'title':'Tenderly','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/sepolia'}}]

# app features
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_HAS_BEACON_CHAIN=true

#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
13 changes: 12 additions & 1 deletion pages/apps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@ import React from 'react';

import PageNextJs from 'nextjs/PageNextJs';

import config from 'configs/app';
import LinkExternal from 'ui/shared/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle';

const feature = config.features.marketplace;

const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false });

const Page: NextPage = () => {
return (
<PageNextJs pathname="/apps">
<>
<PageTitle title="DAppscout"/>
<PageTitle
title="DAppscout"
contentAfter={ feature.isEnabled && (
<LinkExternal href={ feature.submitFormUrl } variant="subtle" fontSize="sm" lineHeight={ 5 } ml="auto">
Submit app
</LinkExternal>
) }
/>
<Marketplace/>
</>
</PageNextJs>
Expand Down
25 changes: 25 additions & 0 deletions ui/address/contract/ContractCode.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ test('verified with changed byte code socket', async({ mount, page, createSocket
await expect(component).toHaveScreenshot();
});

test('verified via lookup in eth_bytecode_db', async({ mount, page, createSocket }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.nonVerified),
}));
await page.route('https://cdn.jsdelivr.net/npm/[email protected]/**', (route) => route.abort());

await mount(
<TestApp withSocket>
<ContractCode addressHash={ addressHash }/>
</TestApp>,
{ hooksConfig },
);

const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:' + addressHash.toLowerCase());

await page.waitForResponse(CONTRACT_API_URL);
socketServer.sendMessage(socket, channel, 'smart_contract_was_verified', {});

const request = await page.waitForRequest(CONTRACT_API_URL);

expect(request).toBeTruthy();
});

test('verified with multiple sources', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
Expand Down
7 changes: 3 additions & 4 deletions ui/address/contract/ContractCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>();

const queryClient = useQueryClient();
const refetchQueries = queryClient.refetchQueries;
const addressInfo = queryClient.getQueryData<AddressInfo>(getResourceKey('address', { pathParams: { hash: addressHash } }));

const { data, isPlaceholderData, isError } = useApiQuery('contract', {
Expand All @@ -55,13 +54,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
}, [ ]);

const handleContractWasVerifiedMessage: SocketMessage.SmartContractWasVerified['handler'] = React.useCallback(() => {
refetchQueries({
queryClient.refetchQueries({
queryKey: getResourceKey('address', { pathParams: { hash: addressHash } }),
});
refetchQueries({
queryClient.refetchQueries({
queryKey: getResourceKey('contract', { pathParams: { hash: addressHash } }),
});
}, [ addressHash, refetchQueries ]);
}, [ addressHash, queryClient ]);

const enableQuery = React.useCallback(() => setIsQueryEnabled(true), []);

Expand Down
10 changes: 8 additions & 2 deletions ui/address/tokens/ERC20TokensTableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ const ERC20TokensTableItem = ({
} = getCurrencyValue({ value: value, exchangeRate: token.exchange_rate, decimals: token.decimals, accuracy: 8, accuracyUsd: 2 });

return (
<Tr>
<Tr
sx={{
'&:hover [aria-label="Add token to wallet"]': {
opacity: 1,
},
}}
>
<Td verticalAlign="middle">
<TokenEntity
token={ token }
Expand All @@ -39,7 +45,7 @@ const ERC20TokensTableItem = ({
isLoading={ isLoading }
noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading } opacity="0"/>
</Flex>
</Td>
<Td isNumeric verticalAlign="middle">
Expand Down
26 changes: 1 addition & 25 deletions ui/pages/Marketplace.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Link, Skeleton } from '@chakra-ui/react';
import { Box } from '@chakra-ui/react';
import React from 'react';

import config from 'configs/app';
Expand All @@ -7,7 +7,6 @@ import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu'
import MarketplaceDisclaimerModal from 'ui/marketplace/MarketplaceDisclaimerModal';
import MarketplaceList from 'ui/marketplace/MarketplaceList';
import FilterInput from 'ui/shared/filters/FilterInput';
import IconSvg from 'ui/shared/IconSvg';

import useMarketplace from '../marketplace/useMarketplace';
const feature = config.features.marketplace;
Expand Down Expand Up @@ -91,29 +90,6 @@ const Marketplace = () => {
appId={ selectedApp.id }
/>
) }

<Skeleton
isLoaded={ !isPlaceholderData }
marginTop={{ base: 8, sm: 16 }}
display="inline-block"
>
<Link
fontWeight="bold"
display="inline-flex"
alignItems="baseline"
href={ feature.submitFormUrl }
isExternal
>
<IconSvg
name="plus"
w={ 3 }
h={ 3 }
mr={ 2 }
/>

Submit an app
</Link>
</Skeleton>
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion ui/shared/address/AddressAddToWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico

return (
<Tooltip label={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick } flexShrink={ 0 }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick } flexShrink={ 0 } aria-label="Add token to wallet">
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ iconSize }/>
</Box>
</Tooltip>
Expand Down
3 changes: 2 additions & 1 deletion ui/shared/layout/LayoutApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import * as Layout from './components';
const LayoutDefault = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.TopRow/>
<HeaderMobile/>
<Layout.MainArea>
<Layout.MainColumn
paddingTop={{ base: '138px', lg: 6 }}
paddingTop={{ base: 16, lg: 6 }}
paddingX={{ base: 4, lg: 6 }}
>
<HeaderAlert/>
Expand Down
2 changes: 1 addition & 1 deletion ui/snippets/profileMenu/ProfileMenuDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => {
textAlign="center"
padding={ 2 }
isDisabled={ hasMenu }
openDelay={ 300 }
openDelay={ 500 }
>
<Box>
<PopoverTrigger>
Expand Down
93 changes: 48 additions & 45 deletions ui/snippets/searchBar/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter } from '@chakra-ui/react';
import { Box, Portal, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter, useOutsideClick } from '@chakra-ui/react';
import _debounce from 'lodash/debounce';
import { useRouter } from 'next/router';
import type { FormEvent, FocusEvent } from 'react';
import type { FormEvent } from 'react';
import React from 'react';
import { Element } from 'react-scroll';

Expand Down Expand Up @@ -59,13 +59,15 @@ const SearchBar = ({ isHomepage }: Props) => {
inputRef.current?.querySelector('input')?.blur();
}, [ onClose ]);

const handleBlur = React.useCallback((event: FocusEvent<HTMLFormElement>) => {
const isFocusInMenu = menuRef.current?.contains(event.relatedTarget);
const isFocusInInput = inputRef.current?.contains(event.relatedTarget);
if (!isFocusInMenu && !isFocusInInput) {
onClose();
const handleOutsideClick = React.useCallback((event: Event) => {
const isFocusInInput = inputRef.current?.contains(event.target as Node);

if (!isFocusInInput) {
handelHide();
}
}, [ onClose ]);
}, [ handelHide ]);

useOutsideClick({ ref: menuRef, handler: handleOutsideClick });

const handleClear = React.useCallback(() => {
handleSearchTermChange('');
Expand Down Expand Up @@ -118,53 +120,54 @@ const SearchBar = ({ isHomepage }: Props) => {
onChange={ handleSearchTermChange }
onSubmit={ handleSubmit }
onFocus={ handleFocus }
onBlur={ handleBlur }
onHide={ handelHide }
onClear={ handleClear }
isHomepage={ isHomepage }
value={ searchTerm }
/>
</PopoverTrigger>
<PopoverContent
w={ `${ menuWidth.current }px` }
ref={ menuRef }
>
<PopoverBody
p={ 0 }
color="chakra-body-text"
<Portal>
<PopoverContent
w={ `${ menuWidth.current }px` }
ref={ menuRef }
>
<Box
maxH="50vh"
overflowY="auto"
id={ SCROLL_CONTAINER_ID }
ref={ scrollRef }
as={ Element }
px={ 4 }
<PopoverBody
p={ 0 }
color="chakra-body-text"
>
{ searchTerm.trim().length === 0 && recentSearchKeywords.length > 0 && (
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
) }
{ searchTerm.trim().length > 0 && (
<SearchBarSuggest
query={ query }
searchTerm={ debouncedSearchTerm }
onItemClick={ handleItemClick }
containerId={ SCROLL_CONTAINER_ID }
/>
) }
</Box>
</PopoverBody>
{ searchTerm.trim().length > 0 && query.data && query.data.length >= 50 && (
<PopoverFooter>
<LinkInternal
href={ route({ pathname: '/search-results', query: { q: searchTerm } }) }
fontSize="sm"
<Box
maxH="50vh"
overflowY="auto"
id={ SCROLL_CONTAINER_ID }
ref={ scrollRef }
as={ Element }
px={ 4 }
>
{ searchTerm.trim().length === 0 && recentSearchKeywords.length > 0 && (
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
) }
{ searchTerm.trim().length > 0 && (
<SearchBarSuggest
query={ query }
searchTerm={ debouncedSearchTerm }
onItemClick={ handleItemClick }
containerId={ SCROLL_CONTAINER_ID }
/>
) }
</Box>
</PopoverBody>
{ searchTerm.trim().length > 0 && query.data && query.data.length >= 50 && (
<PopoverFooter>
<LinkInternal
href={ route({ pathname: '/search-results', query: { q: searchTerm } }) }
fontSize="sm"
>
View all results
</LinkInternal>
</PopoverFooter>
) }
</PopoverContent>
</LinkInternal>
</PopoverFooter>
) }
</PopoverContent>
</Portal>
</Popover>
);
};
Expand Down
Loading

0 comments on commit 1484bb3

Please sign in to comment.