Commit ca3d5305 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #2054 from blockscout/release/v1_32_0-fixes

Fixes for release v.1.32.0
parents 110076d4 b8fedb9a
......@@ -14,6 +14,7 @@ const defaultOptions: UseToastOptions & { toastComponent?: React.FC<ToastProps>
containerStyle: {
margin: 8,
},
variant: 'subtle',
};
export default function useToastModified() {
......
import type { TestFixture, Page } from '@playwright/test';
import _isEqual from 'lodash/isEqual';
import type { PublicRpcSchema } from 'viem';
import { getEnvValue } from 'configs/app/utils';
type Params = PublicRpcSchema[number];
export type MockRpcResponseFixture = (params: Params) => Promise<void>;
// WIP
const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ page }, use) => {
await use(async({ Method, ReturnType }) => {
const rpcUrl = getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL');
if (!rpcUrl) {
return;
}
await page.route(rpcUrl, (route) => {
const method = route.request().method();
if (method !== 'POST') {
route.continue();
return;
}
const json = route.request().postDataJSON();
const id = json?.id;
const payload = {
id,
jsonrpc: '2.0',
method: Method,
// TODO: add params to match actual payload
};
if (_isEqual(json, payload) && id !== undefined) {
return route.fulfill({
status: 200,
body: JSON.stringify({
id,
jsonrpc: '2.0',
result: ReturnType,
}),
});
}
});
});
};
export default fixture;
......@@ -8,6 +8,7 @@ import * as mockConfigResponse from './fixtures/mockConfigResponse';
import * as mockContractReadResponse from './fixtures/mockContractReadResponse';
import * as mockEnvs from './fixtures/mockEnvs';
import * as mockFeatures from './fixtures/mockFeatures';
import * as mockRpcResponse from './fixtures/mockRpcResponse';
import * as mockTextAd from './fixtures/mockTextAd';
import * as render from './fixtures/render';
import * as socketServer from './fixtures/socketServer';
......@@ -20,6 +21,7 @@ interface Fixtures {
mockContractReadResponse: mockContractReadResponse.MockContractReadResponseFixture;
mockEnvs: mockEnvs.MockEnvsFixture;
mockFeatures: mockFeatures.MockFeaturesFixture;
mockRpcResponse: mockRpcResponse.MockRpcResponseFixture;
createSocket: socketServer.CreateSocketFixture;
injectMetaMaskProvider: injectMetaMaskProvider.InjectMetaMaskProvider;
mockTextAd: mockTextAd.MockTextAdFixture;
......@@ -33,6 +35,7 @@ const test = base.extend<Fixtures>({
mockContractReadResponse: mockContractReadResponse.default,
mockEnvs: mockEnvs.default,
mockFeatures: mockFeatures.default,
mockRpcResponse: mockRpcResponse.default,
// FIXME: for some reason Playwright does not intercept requests to text ad provider when running multiple tests in parallel
// even if we have a global request interceptor (maybe it is related to service worker issue, maybe not)
// so we have to inject mockTextAd fixture in each test and mock the response where it is needed
......
......@@ -86,33 +86,33 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</Skeleton>
) : data.tx_count }
</Td>
<Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Flex mt={ 2 }>
<Tooltip label={ isLoading ? undefined : 'Gas Used %' }>
<Box>
<Utilization
colorScheme="gray"
value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() }
isLoading={ isLoading }
/>
</Box>
</Tooltip>
{ data.gas_target_percentage && (
<>
<TextSeparator color={ separatorColor } mx={ 1 }/>
<GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isLoading }/>
</>
) }
</Flex>
</Td>
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && (
<Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Flex mt={ 2 }>
<Tooltip label={ isLoading ? undefined : 'Gas Used %' }>
<Box>
<Utilization
colorScheme="gray"
value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() }
isLoading={ isLoading }
/>
</Box>
</Tooltip>
{ data.gas_target_percentage && (
<>
<TextSeparator color={ separatorColor } mx={ 1 }/>
<GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isLoading }/>
</>
) }
</Flex>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ totalReward.toFixed(8) }
</Skeleton>
</Td>
) }
<Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ totalReward.toFixed(8) }
</Skeleton>
</Td>
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && (
<Td fontSize="sm">
<Flex alignItems="center" columnGap={ 2 }>
......
......@@ -179,7 +179,7 @@ const MarketplaceAppCard = ({
showContractList={ showContractList }
isLoading={ isLoading }
source="Discovery view"
popoverPlacement={ isMobile ? 'bottom-end' : 'bottom-start' }
popoverPlacement={ isMobile ? 'bottom-end' : 'left' }
position="absolute"
right={{ base: 3, md: 5 }}
top={{ base: '10px', md: 5 }}
......
......@@ -53,7 +53,7 @@ const NameDomainsActionBar = ({
minW={{ base: 'auto', lg: '250px' }}
size="xs"
onChange={ onSearchChange }
placeholder="Search by name"
placeholder="Search by name or address"
initialValue={ searchTerm }
isLoading={ isInitialLoading }
/>
......
......@@ -64,21 +64,12 @@ const GasTracker = () => {
</Flex>
);
const content = (() => {
const snippets = (() => {
if (!isPlaceholderData && data?.gas_prices?.slow === null && data?.gas_prices.average === null && data.gas_prices.fast === null) {
return <Alert status="warning">No data available yet</Alert>;
return <Alert status="warning">No recent data available</Alert>;
}
return (
<>
{ data?.gas_prices && <GasTrackerPrices prices={ data.gas_prices } isLoading={ isLoading }/> }
{ config.features.stats.isEnabled && (
<Box mt={ 12 }>
<GasTrackerChart/>
</Box>
) }
</>
);
return data?.gas_prices ? <GasTrackerPrices prices={ data.gas_prices } isLoading={ isLoading }/> : null;
})();
return (
......@@ -88,7 +79,12 @@ const GasTracker = () => {
secondRow={ titleSecondRow }
withTextAd
/>
{ content }
{ snippets }
{ config.features.stats.isEnabled && (
<Box mt={ 12 }>
<GasTrackerChart/>
</Box>
) }
</>
);
};
......
......@@ -33,9 +33,9 @@ const testFn: Parameters<typeof test>[1] = async({ render, mockConfigResponse, m
await expect(component).toHaveScreenshot();
};
test('base view +@dark-mode', testFn);
test.fixme('base view +@dark-mode', testFn);
test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', testFn);
test.fixme('base view', testFn);
});
......@@ -20,7 +20,7 @@ function getBgColor(status?: AlertStatus) {
}
}
const Toast = ({ onClose, title, description, id, isClosable, status }: ToastProps) => {
const Toast = ({ onClose, title, description, id, isClosable, status, icon }: ToastProps) => {
const ids = id ?
{
......@@ -48,7 +48,7 @@ const Toast = ({ onClose, title, description, id, isClosable, status }: ToastPro
maxWidth="400px"
>
<chakra.div flex="1" maxWidth="100%">
{ title && <AlertTitle id={ ids?.title }>{ title }</AlertTitle> }
{ title && <AlertTitle id={ ids?.title } display="flex" alignItems="center">{ icon }{ title }</AlertTitle> }
{ description && (
<AlertDescription id={ ids?.description } display="block">
{ description }
......
import { chakra, Alert, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react';
import type { ToastId } from '@chakra-ui/react';
import { chakra, Alert, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Spinner } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import ReCaptcha from 'react-google-recaptcha';
......@@ -9,7 +10,7 @@ import type { TokenInstance } from 'types/api/token';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery';
import { MINUTE } from 'lib/consts';
import { MINUTE, SECOND } from 'lib/consts';
import useToast from 'lib/hooks/useToast';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
......@@ -23,6 +24,7 @@ interface Props {
const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
const timeoutId = React.useRef<number>();
const toastId = React.useRef<ToastId>();
const { status, setStatus } = useMetadataUpdateContext() || {};
const apiFetch = useApiFetch();
......@@ -31,12 +33,12 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
const handleRefreshError = React.useCallback(() => {
setStatus?.('ERROR');
toast.closeAll();
toast({
toastId.current && toast.update(toastId.current, {
title: 'Error',
description: 'The refreshing process has failed. Please try again.',
status: 'warning',
variant: 'subtle',
duration: 5 * SECOND,
isClosable: true,
});
}, [ setStatus, toast ]);
......@@ -49,13 +51,15 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
},
})
.then(() => {
toast({
setStatus?.('WAITING_FOR_RESPONSE');
toastId.current = toast({
title: 'Please wait',
description: 'Refetching metadata request sent',
icon: <Spinner size="sm" mr={ 2 }/>,
status: 'warning',
variant: 'subtle',
duration: null,
isClosable: false,
});
setStatus?.('WAITING_FOR_RESPONSE');
timeoutId.current = window.setTimeout(handleRefreshError, 2 * MINUTE);
})
.catch(() => {
......@@ -63,7 +67,6 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
title: 'Error',
description: 'Unable to initialize metadata update',
status: 'warning',
variant: 'subtle',
});
setStatus?.('ERROR');
});
......@@ -112,12 +115,12 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
};
});
toast.closeAll();
toast({
toastId.current && toast.update(toastId.current, {
title: 'Success!',
description: 'Metadata has been refreshed',
status: 'success',
variant: 'subtle',
duration: 5 * SECOND,
isClosable: true,
});
setStatus?.('SUCCESS');
......@@ -138,6 +141,15 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
handler: handleSocketMessage,
});
React.useEffect(() => {
return () => {
timeoutId.current && window.clearTimeout(timeoutId.current);
toastId.current && toast.close(toastId.current);
};
// run only on mount/unmount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<Modal isOpen={ status === 'MODAL_OPENED' } onClose={ handleModalClose } size={{ base: 'full', lg: 'sm' }}>
<ModalOverlay/>
......
......@@ -8605,10 +8605,10 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
dappscout-iframe@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/dappscout-iframe/-/dappscout-iframe-0.2.1.tgz#b4718515ee4f00022af3912fac6ca1a321c156f9"
integrity sha512-EsiAAEk2I6hN+/E8o45WUn4BFd7aN8UvBwsIcOH79WOly0GOOHkPEO/puPkBCV0EcdxBsZIfssx3X0fSWVz5Bw==
dappscout-iframe@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dappscout-iframe/-/dappscout-iframe-0.2.2.tgz#de3df6abccad68a27c9304300b92d86ec0ab1c59"
integrity sha512-ASOimgBRG61pSYQLdYGWePdiO3IsfTEgWZ6CHpZ4XQjJRmj1+WiWF56vFTeLIo5aucp+2+6oRCJ8KgKHGVDj0A==
dependencies:
react "^18.2.0"
react-dom "^18.2.0"
......
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