Commit f8d8b7f8 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into tom2drum/issue-1414

parents 754e3197 ea9e0cd5
...@@ -31,6 +31,7 @@ const moduleExports = { ...@@ -31,6 +31,7 @@ const moduleExports = {
}, },
); );
config.resolve.fallback = { fs: false, net: false, tls: false }; config.resolve.fallback = { fs: false, net: false, tls: false };
config.externals.push('pino-pretty', 'lokijs', 'encoding');
return config; return config;
}, },
......
...@@ -2,6 +2,8 @@ import type CspDev from 'csp-dev'; ...@@ -2,6 +2,8 @@ import type CspDev from 'csp-dev';
import config from 'configs/app'; import config from 'configs/app';
import { KEY_WORDS } from '../utils';
export function walletConnect(): CspDev.DirectiveDescriptor { export function walletConnect(): CspDev.DirectiveDescriptor {
if (!config.features.blockchainInteraction.isEnabled) { if (!config.features.blockchainInteraction.isEnabled) {
return {}; return {};
...@@ -9,11 +11,13 @@ export function walletConnect(): CspDev.DirectiveDescriptor { ...@@ -9,11 +11,13 @@ export function walletConnect(): CspDev.DirectiveDescriptor {
return { return {
'connect-src': [ 'connect-src': [
'*.web3modal.com',
'*.walletconnect.com', '*.walletconnect.com',
'wss://relay.walletconnect.com', 'wss://relay.walletconnect.com',
'wss://www.walletlink.org', 'wss://www.walletlink.org',
], ],
'img-src': [ 'img-src': [
KEY_WORDS.BLOB,
'*.walletconnect.com', '*.walletconnect.com',
], ],
}; };
......
import { ChakraProvider } from '@chakra-ui/react'; import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { w3mProvider } from '@web3modal/ethereum'; import { createWeb3Modal, defaultWagmiConfig } from '@web3modal/wagmi/react';
import React from 'react'; import React from 'react';
import { configureChains, createConfig, WagmiConfig } from 'wagmi'; import { WagmiConfig } from 'wagmi';
import { mainnet } from 'wagmi/chains'; import { mainnet } from 'wagmi/chains';
import type { Props as PageProps } from 'nextjs/getServerSideProps'; import type { Props as PageProps } from 'nextjs/getServerSideProps';
...@@ -33,17 +33,18 @@ const defaultAppContext = { ...@@ -33,17 +33,18 @@ const defaultAppContext = {
}; };
// >>> Web3 stuff // >>> Web3 stuff
const { publicClient } = configureChains( const chains = [ mainnet ];
[ mainnet ], const WALLET_CONNECT_PROJECT_ID = 'PROJECT_ID';
[
w3mProvider({ projectId: '' }),
],
);
const wagmiConfig = createConfig({ const wagmiConfig = defaultWagmiConfig({
autoConnect: false, chains,
connectors: [ ], projectId: WALLET_CONNECT_PROJECT_ID,
publicClient, });
createWeb3Modal({
wagmiConfig,
projectId: WALLET_CONNECT_PROJECT_ID,
chains,
}); });
// <<<< // <<<<
......
import { Alert, Button, Flex } from '@chakra-ui/react'; import { Alert, Button, Flex } from '@chakra-ui/react';
import { useWeb3Modal } from '@web3modal/react'; import { useWeb3Modal, useWeb3ModalState } from '@web3modal/wagmi/react';
import React from 'react'; import React from 'react';
import { useAccount, useDisconnect } from 'wagmi'; import { useAccount, useDisconnect } from 'wagmi';
...@@ -8,7 +8,8 @@ import * as mixpanel from 'lib/mixpanel/index'; ...@@ -8,7 +8,8 @@ import * as mixpanel from 'lib/mixpanel/index';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
const ContractConnectWallet = () => { const ContractConnectWallet = () => {
const { open, isOpen } = useWeb3Modal(); const { open } = useWeb3Modal();
const { open: isOpen } = useWeb3ModalState();
const { disconnect } = useDisconnect(); const { disconnect } = useDisconnect();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [ isModalOpening, setIsModalOpening ] = React.useState(false); const [ isModalOpening, setIsModalOpening ] = React.useState(false);
......
import { Alert, Box, Button, chakra, Flex, Link, Radio, RadioGroup } from '@chakra-ui/react'; import { Alert, Box, Button, chakra, Flex, Link, Radio, RadioGroup } from '@chakra-ui/react';
import { useWeb3Modal } from '@web3modal/react'; import { useWeb3Modal } from '@web3modal/wagmi/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
......
...@@ -8,8 +8,8 @@ import config from 'configs/app'; ...@@ -8,8 +8,8 @@ import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import GasInfoTooltipContent from 'ui/shared/GasInfoTooltipContent/GasInfoTooltipContent';
import StatsGasPrices from './StatsGasPrices';
import StatsItem from './StatsItem'; import StatsItem from './StatsItem';
const hasGasTracker = config.UI.homepage.showGasTracker; const hasGasTracker = config.UI.homepage.showGasTracker;
...@@ -45,7 +45,7 @@ const Stats = () => { ...@@ -45,7 +45,7 @@ const Stats = () => {
!data.gas_prices && itemsCount--; !data.gas_prices && itemsCount--;
data.rootstock_locked_btc && itemsCount++; data.rootstock_locked_btc && itemsCount++;
const isOdd = Boolean(itemsCount % 2); const isOdd = Boolean(itemsCount % 2);
const gasLabel = hasGasTracker && data.gas_prices ? <StatsGasPrices gasPrices={ data.gas_prices }/> : null; const gasLabel = hasGasTracker && data.gas_prices ? <GasInfoTooltipContent gasPrices={ data.gas_prices }/> : null;
content = ( content = (
<> <>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { GasPrices } from 'types/api/stats'; import type { GasPrices } from 'types/api/stats';
const StatsGasPrices = ({ gasPrices }: {gasPrices: GasPrices}) => { const GasInfoTooltipContent = ({ gasPrices }: {gasPrices: GasPrices}) => {
const nameStyleProps = { const nameStyleProps = {
color: useColorModeValue('blue.100', 'blue.600'), color: useColorModeValue('blue.100', 'blue.600'),
}; };
...@@ -20,4 +20,4 @@ const StatsGasPrices = ({ gasPrices }: {gasPrices: GasPrices}) => { ...@@ -20,4 +20,4 @@ const StatsGasPrices = ({ gasPrices }: {gasPrices: GasPrices}) => {
); );
}; };
export default StatsGasPrices; export default React.memo(GasInfoTooltipContent);
import { useColorModeValue, useToken } from '@chakra-ui/react'; import { useColorMode } from '@chakra-ui/react';
import { jsonRpcProvider } from '@wagmi/core/providers/jsonRpc'; import { jsonRpcProvider } from '@wagmi/core/providers/jsonRpc';
import { EthereumClient, w3mConnectors } from '@web3modal/ethereum'; import { createWeb3Modal, useWeb3ModalTheme, defaultWagmiConfig } from '@web3modal/wagmi/react';
import { Web3Modal } from '@web3modal/react';
import React from 'react'; import React from 'react';
import type { Chain } from 'wagmi'; import type { Chain } from 'wagmi';
import { configureChains, createConfig, WagmiConfig } from 'wagmi'; import { configureChains, WagmiConfig } from 'wagmi';
import config from 'configs/app'; import config from 'configs/app';
import colors from 'theme/foundations/colors';
import { BODY_TYPEFACE } from 'theme/foundations/typography';
import zIndices from 'theme/foundations/zIndices';
const feature = config.features.blockchainInteraction; const feature = config.features.blockchainInteraction;
...@@ -41,58 +43,71 @@ const getConfig = () => { ...@@ -41,58 +43,71 @@ const getConfig = () => {
}, },
}; };
const chains = [ currentChain ]; const { chains } = configureChains(
[ currentChain ],
const { publicClient } = configureChains(chains, [ [
jsonRpcProvider({ jsonRpcProvider({
rpc: () => ({ rpc: () => ({
http: config.chain.rpcUrl || '', http: config.chain.rpcUrl || '',
}),
}), }),
}), ],
]); );
const wagmiConfig = createConfig({
autoConnect: true, const wagmiConfig = defaultWagmiConfig({
connectors: w3mConnectors({ projectId: feature.walletConnect.projectId, chains }), chains,
publicClient, projectId: feature.walletConnect.projectId,
});
createWeb3Modal({
wagmiConfig,
projectId: feature.walletConnect.projectId,
chains,
themeVariables: {
'--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`,
'--w3m-accent': colors.blue[600],
'--w3m-border-radius-master': '2px',
'--w3m-z-index': zIndices.modal,
},
}); });
const ethereumClient = new EthereumClient(wagmiConfig, chains);
return { wagmiConfig, ethereumClient }; return { wagmiConfig };
} catch (error) { } catch (error) {
return { wagmiConfig: undefined, ethereumClient: undefined }; return { };
} }
}; };
const { wagmiConfig, ethereumClient } = getConfig(); const { wagmiConfig } = getConfig();
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
fallback?: JSX.Element | (() => JSX.Element); fallback?: JSX.Element | (() => JSX.Element);
} }
const Web3ModalProvider = ({ children, fallback }: Props) => { const Fallback = ({ children, fallback }: Props) => {
const modalZIndex = useToken<string>('zIndices', 'modal'); return typeof fallback === 'function' ? fallback() : (fallback || <>{ children }</>); // eslint-disable-line react/jsx-no-useless-fragment
const web3ModalTheme = useColorModeValue('light', 'dark'); };
const Provider = ({ children, fallback }: Props) => {
const { colorMode } = useColorMode();
const { setThemeMode } = useWeb3ModalTheme();
if (!wagmiConfig || !ethereumClient || !feature.isEnabled) { React.useEffect(() => {
return typeof fallback === 'function' ? fallback() : (fallback || <>{ children }</>); // eslint-disable-line react/jsx-no-useless-fragment setThemeMode(colorMode);
}, [ colorMode, setThemeMode ]);
// not really necessary, but we have to make typescript happy
if (!wagmiConfig || !feature.isEnabled) {
return <Fallback fallback={ fallback }>{ children }</Fallback>;
} }
return ( return (
<> <WagmiConfig config={ wagmiConfig }>
<WagmiConfig config={ wagmiConfig }> { children }
{ children } </WagmiConfig>
</WagmiConfig>
<Web3Modal
projectId={ feature.walletConnect.projectId }
ethereumClient={ ethereumClient }
themeMode={ web3ModalTheme }
themeVariables={{
'--w3m-z-index': modalZIndex,
}}
/>
</>
); );
}; };
const Web3ModalProvider = wagmiConfig && feature.isEnabled ? Provider : Fallback;
export default Web3ModalProvider; export default Web3ModalProvider;
...@@ -19,7 +19,9 @@ test('default view +@dark-mode +@mobile', async({ mount, page }) => { ...@@ -19,7 +19,9 @@ test('default view +@dark-mode +@mobile', async({ mount, page }) => {
</TestApp>, </TestApp>,
); );
await component.getByLabel('color mode switch').click(); await component.getByText(/gwei/i).hover();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } });
await component.getByLabel('color mode switch').click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } }); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } });
}); });
import { Flex, Skeleton } from '@chakra-ui/react'; import { Flex, LightMode, Link, Skeleton, Tooltip, chakra, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import GasInfoTooltipContent from 'ui/shared/GasInfoTooltipContent/GasInfoTooltipContent';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
const TopBarStats = () => { const TopBarStats = () => {
// have to implement controlled tooltip because of the issue - https://github.com/chakra-ui/chakra-ui/issues/7107
const { isOpen, onOpen, onToggle, onClose } = useDisclosure();
const handleClick = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
onToggle();
}, [ onToggle ]);
const { data, isPlaceholderData, isError } = useApiQuery('homepage_stats', { const { data, isPlaceholderData, isError } = useApiQuery('homepage_stats', {
queryOptions: { queryOptions: {
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
...@@ -26,14 +35,34 @@ const TopBarStats = () => { ...@@ -26,14 +35,34 @@ const TopBarStats = () => {
> >
{ data?.coin_price && ( { data?.coin_price && (
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
<span>{ config.chain.governanceToken.symbol || config.chain.currency.symbol }: </span> <chakra.span color="text_secondary">{ config.chain.governanceToken.symbol || config.chain.currency.symbol } </chakra.span>
<span>${ Number(data.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }) }</span> <span>${ Number(data.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }) }</span>
</Skeleton> </Skeleton>
) } ) }
{ data?.coin_price && config.UI.homepage.showGasTracker && <TextSeparator color="divider"/> } { data?.coin_price && config.UI.homepage.showGasTracker && <TextSeparator color="divider"/> }
{ data?.gas_prices && config.UI.homepage.showGasTracker && ( { data?.gas_prices && config.UI.homepage.showGasTracker && (
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton isLoaded={ !isPlaceholderData }>
<span>Gas: { data.gas_prices.average } Gwei</span> <chakra.span color="text_secondary">Gas </chakra.span>
<LightMode>
<Tooltip
label={ <GasInfoTooltipContent gasPrices={ data.gas_prices }/> }
hasArrow={ false }
borderRadius="md"
offset={ [ 0, 16 ] }
bgColor="blackAlpha.900"
p={ 0 }
isOpen={ isOpen }
>
<Link
_hover={{ textDecoration: 'none', color: 'link_hovered' }}
onClick={ handleClick }
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
>
{ data.gas_prices.average } Gwei
</Link>
</Tooltip>
</LightMode>
</Skeleton> </Skeleton>
) } ) }
</Flex> </Flex>
......
import { useWeb3Modal } from '@web3modal/react'; import { useWeb3Modal, useWeb3ModalState } from '@web3modal/wagmi/react';
import React from 'react'; import React from 'react';
import { useAccount, useDisconnect } from 'wagmi'; import { useAccount, useDisconnect } from 'wagmi';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
export default function useWallet() { export default function useWallet() {
const { open, isOpen } = useWeb3Modal(); const { open } = useWeb3Modal();
const { open: isOpen } = useWeb3ModalState();
const { disconnect } = useDisconnect(); const { disconnect } = useDisconnect();
const [ isModalOpening, setIsModalOpening ] = React.useState(false); const [ isModalOpening, setIsModalOpening ] = React.useState(false);
const [ isClientLoaded, setIsClientLoaded ] = React.useState(false); const [ isClientLoaded, setIsClientLoaded ] = React.useState(false);
......
This diff is collapsed.
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