diff --git a/lib/api/resources.ts b/lib/api/resources.ts index e2770a12b5..ee25a7ef09 100644 --- a/lib/api/resources.ts +++ b/lib/api/resources.ts @@ -1,4 +1,6 @@ import type * as bens from '@blockscout/bens-types'; +import type * as stats from '@blockscout/stats-types'; +import type * as visualizer from '@blockscout/visualizer-types'; import { getFeaturePayload } from 'configs/app/features/types'; import type { UserInfo, @@ -71,7 +73,7 @@ import type { import type { RawTracesResponse } from 'types/api/rawTrace'; import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search'; import type { ShibariumWithdrawalsResponse, ShibariumDepositsResponse } from 'types/api/shibarium'; -import type { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats'; +import type { HomeStats } from 'types/api/stats'; import type { TokenCounters, TokenInfo, @@ -99,7 +101,6 @@ import type { TxStateChanges } from 'types/api/txStateChanges'; import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'types/api/userOps'; import type { ValidatorsCountersResponse, ValidatorsFilters, ValidatorsResponse, ValidatorsSorting } from 'types/api/validators'; import type { VerifiedContractsSorting } from 'types/api/verifiedContracts'; -import type { VisualizedContract } from 'types/api/visualization'; import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals'; import type { ZkEvmL2DepositsResponse, @@ -931,9 +932,9 @@ Q extends 'homepage_zkevm_l2_batches' ? { items: Array } Q extends 'homepage_indexing_status' ? IndexingStatus : Q extends 'homepage_zkevm_latest_batch' ? number : Q extends 'homepage_zksync_latest_batch' ? number : -Q extends 'stats_counters' ? Counters : -Q extends 'stats_lines' ? StatsCharts : -Q extends 'stats_line' ? StatsChart : +Q extends 'stats_counters' ? stats.Counters : +Q extends 'stats_lines' ? stats.LineCharts : +Q extends 'stats_line' ? stats.LineChart : Q extends 'blocks' ? BlocksResponse : Q extends 'block' ? Block : Q extends 'block_txs' ? BlockTransactionsResponse : @@ -986,7 +987,7 @@ Q extends 'contract' ? SmartContract : Q extends 'contract_solidityscan_report' ? SolidityscanReport : Q extends 'verified_contracts' ? VerifiedContractsResponse : Q extends 'verified_contracts_counters' ? VerifiedContractsCounters : -Q extends 'visualize_sol2uml' ? VisualizedContract : +Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse : Q extends 'contract_verification_config' ? SmartContractVerificationConfig : Q extends 'withdrawals' ? WithdrawalsResponse : Q extends 'withdrawals_counters' ? WithdrawalsCounters : diff --git a/lib/hooks/useToast.tsx b/lib/hooks/useToast.tsx index 4110c513c1..599ac0e3e8 100644 --- a/lib/hooks/useToast.tsx +++ b/lib/hooks/useToast.tsx @@ -14,6 +14,7 @@ const defaultOptions: UseToastOptions & { toastComponent?: React.FC containerStyle: { margin: 8, }, + variant: 'subtle', }; export default function useToastModified() { diff --git a/mocks/stats/line.ts b/mocks/stats/line.ts index 47de79b184..799cff4c45 100644 --- a/mocks/stats/line.ts +++ b/mocks/stats/line.ts @@ -1,128 +1,161 @@ -export const averageGasPrice = { +import type * as stats from '@blockscout/stats-types'; + +export const averageGasPrice: stats.LineChart = { chart: [ { date: '2023-12-22', value: '37.7804422597599', + is_approximate: false, }, { date: '2023-12-23', value: '25.84889883009387', + is_approximate: false, }, { date: '2023-12-24', value: '25.818463227198574', + is_approximate: false, }, { date: '2023-12-25', value: '26.045513050051298', + is_approximate: false, }, { date: '2023-12-26', value: '21.42600692652399', + is_approximate: false, }, { date: '2023-12-27', value: '31.066730409846656', + is_approximate: false, }, { date: '2023-12-28', value: '33.63955781902089', + is_approximate: false, }, { date: '2023-12-29', value: '28.064736756058384', + is_approximate: false, }, { date: '2023-12-30', value: '23.074500869678175', + is_approximate: false, }, { date: '2023-12-31', value: '17.651005734615133', + is_approximate: false, }, { date: '2024-01-01', value: '14.906085174476441', + is_approximate: false, }, { date: '2024-01-02', value: '22.28459059038656', + is_approximate: false, }, { date: '2024-01-03', value: '39.8311646806592', + is_approximate: false, }, { date: '2024-01-04', value: '26.09989322256083', + is_approximate: false, }, { date: '2024-01-05', value: '22.821996688111998', + is_approximate: false, }, { date: '2024-01-06', value: '20.32680041262083', + is_approximate: false, }, { date: '2024-01-07', value: '32.535045831809704', + is_approximate: false, }, { date: '2024-01-08', value: '27.443477102139482', + is_approximate: false, }, { date: '2024-01-09', value: '20.7911332558055', + is_approximate: false, }, { date: '2024-01-10', value: '42.10740192523919', + is_approximate: false, }, { date: '2024-01-11', value: '35.75215680343582', + is_approximate: false, }, { date: '2024-01-12', value: '27.430414798093253', + is_approximate: false, }, { date: '2024-01-13', value: '20.170934096589875', + is_approximate: false, }, { date: '2024-01-14', value: '38.79660984371034', + is_approximate: false, }, { date: '2024-01-15', value: '26.140740484554204', + is_approximate: false, }, { date: '2024-01-16', value: '36.708543184194156', + is_approximate: false, }, { date: '2024-01-17', value: '40.325438794298876', + is_approximate: false, }, { date: '2024-01-18', value: '37.55145309930694', + is_approximate: false, }, { date: '2024-01-19', value: '33.271450114434664', + is_approximate: false, }, { date: '2024-01-20', value: '19.303304377685638', + is_approximate: false, }, { date: '2024-01-21', value: '14.375908594704976', + is_approximate: false, }, ], }; diff --git a/mocks/stats/lines.ts b/mocks/stats/lines.ts index 51aca3c97d..9f8870249f 100644 --- a/mocks/stats/lines.ts +++ b/mocks/stats/lines.ts @@ -1,4 +1,6 @@ -export const base = { +import type * as stats from '@blockscout/stats-types'; + +export const base: stats.LineCharts = { sections: [ { id: 'accounts', @@ -8,19 +10,19 @@ export const base = { id: 'accountsGrowth', title: 'Accounts growth', description: 'Cumulative accounts number per period', - units: null, + units: undefined, }, { id: 'activeAccounts', title: 'Active accounts', description: 'Active accounts number per period', - units: null, + units: undefined, }, { id: 'newAccounts', title: 'New accounts', description: 'New accounts number per day', - units: null, + units: undefined, }, ], }, @@ -38,7 +40,7 @@ export const base = { id: 'newTxns', title: 'New transactions', description: 'New transactions number', - units: null, + units: undefined, }, { id: 'txnsFee', @@ -50,13 +52,13 @@ export const base = { id: 'txnsGrowth', title: 'Transactions growth', description: 'Cumulative transactions number', - units: null, + units: undefined, }, { id: 'txnsSuccessRate', title: 'Transactions success rate', description: 'Successful transactions rate per day', - units: null, + units: undefined, }, ], }, @@ -80,7 +82,7 @@ export const base = { id: 'newBlocks', title: 'New blocks', description: 'New blocks number', - units: null, + units: undefined, }, ], }, @@ -92,7 +94,7 @@ export const base = { id: 'newNativeCoinTransfers', title: 'New ETH transfers', description: 'New token transfers number for the period', - units: null, + units: undefined, }, ], }, @@ -104,7 +106,7 @@ export const base = { id: 'averageGasLimit', title: 'Average gas limit', description: 'Average gas limit per block for the period', - units: null, + units: undefined, }, { id: 'averageGasPrice', @@ -116,7 +118,7 @@ export const base = { id: 'gasUsedGrowth', title: 'Gas used growth', description: 'Cumulative gas used for the period', - units: null, + units: undefined, }, ], }, @@ -128,13 +130,13 @@ export const base = { id: 'newVerifiedContracts', title: 'New verified contracts', description: 'New verified contracts number for the period', - units: null, + units: undefined, }, { id: 'verifiedContractsGrowth', title: 'Verified contracts growth', description: 'Cumulative number verified contracts for the period', - units: null, + units: undefined, }, ], }, diff --git a/package.json b/package.json index 2e6d782c4e..6a7f5be039 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "monitoring:grafana:local": "docker run -d -p 4000:3000 --name=blockscout_grafana --user $(id -u) --volume $(pwd)/grafana:/var/lib/grafana grafana/grafana-enterprise" }, "dependencies": { - "@blockscout/bens-types": "v1.3.0-beta", + "@blockscout/bens-types": "1.3.4", + "@blockscout/stats-types": "1.6.0", + "@blockscout/visualizer-types": "0.2.0", "@chakra-ui/react": "2.7.1", "@chakra-ui/theme-tools": "^2.0.18", "@emotion/react": "^11.10.4", @@ -67,7 +69,7 @@ "chakra-react-select": "^4.4.3", "crypto-js": "^4.2.0", "d3": "^7.6.1", - "dappscout-iframe": "0.2.1", + "dappscout-iframe": "0.2.2", "dayjs": "^1.11.5", "dom-to-image": "^2.6.0", "focus-visible": "^5.2.0", diff --git a/playwright/fixtures/mockRpcResponse.ts b/playwright/fixtures/mockRpcResponse.ts new file mode 100644 index 0000000000..f65bf81974 --- /dev/null +++ b/playwright/fixtures/mockRpcResponse.ts @@ -0,0 +1,52 @@ +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; + +// WIP +const fixture: TestFixture = 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; diff --git a/playwright/lib.tsx b/playwright/lib.tsx index dffe7f3715..586f980bf0 100644 --- a/playwright/lib.tsx +++ b/playwright/lib.tsx @@ -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({ 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 diff --git a/stubs/stats.ts b/stubs/stats.ts index d21ba588fd..e1e70724fb 100644 --- a/stubs/stats.ts +++ b/stubs/stats.ts @@ -1,4 +1,5 @@ -import type { Counter, HomeStats, StatsChartsSection } from 'types/api/stats'; +import type * as stats from '@blockscout/stats-types'; +import type { HomeStats } from 'types/api/stats'; export const HOMEPAGE_STATS: HomeStats = { average_block_time: 14346, @@ -41,7 +42,7 @@ export const HOMEPAGE_STATS: HomeStats = { tvl: '1767425.102766552', }; -export const STATS_CHARTS_SECTION: StatsChartsSection = { +export const STATS_CHARTS_SECTION: stats.LineChartSection = { id: 'placeholder', title: 'Placeholder', charts: [ @@ -61,13 +62,13 @@ export const STATS_CHARTS_SECTION: StatsChartsSection = { id: 'chart_2', title: 'New transactions', description: 'New transactions number', - units: null, + units: undefined, }, { id: 'chart_3', title: 'Transactions growth', description: 'Cumulative transactions number', - units: null, + units: undefined, }, ], }; @@ -76,7 +77,7 @@ export const STATS_CHARTS = { sections: [ STATS_CHARTS_SECTION ], }; -export const STATS_COUNTER: Counter = { +export const STATS_COUNTER: stats.Counter = { id: 'stub', value: '9074405', title: 'Placeholder Counter', diff --git a/types/api/stats.ts b/types/api/stats.ts index c7585f3797..3d75a5f3e9 100644 --- a/types/api/stats.ts +++ b/types/api/stats.ts @@ -34,39 +34,3 @@ export interface GasPriceInfo { base_fee: number | null; priority_fee: number | null; } - -export type Counters = { - counters: Array; -} - -export type Counter = { - id: string; - value: string; - title: string; - description?: string; - units: string; -} - -export type StatsCharts = { - sections: Array; -} - -export type StatsChartsSection = { - id: string; - title: string; - charts: Array; -} - -export type StatsChartInfo = { - id: string; - title: string; - description: string; - units: string | null; -} - -export type StatsChart = { chart: Array }; - -export type StatsChartItem = { - date: string; - value: string; -} diff --git a/types/api/visualization.ts b/types/api/visualization.ts deleted file mode 100644 index f80dc493b1..0000000000 --- a/types/api/visualization.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface VisualizedContract { - png: string | null; - svg: string | null; -} diff --git a/ui/blocks/BlocksTableItem.tsx b/ui/blocks/BlocksTableItem.tsx index 1560bfa1f3..1e230e4788 100644 --- a/ui/blocks/BlocksTableItem.tsx +++ b/ui/blocks/BlocksTableItem.tsx @@ -86,33 +86,33 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ) : data.tx_count } + + { BigNumber(data.gas_used || 0).toFormat() } + + + + + + + { data.gas_target_percentage && ( + <> + + + + ) } + + { !isRollup && !config.UI.views.block.hiddenFields?.total_reward && ( - { BigNumber(data.gas_used || 0).toFormat() } - - - - - - - { data.gas_target_percentage && ( - <> - - - - ) } - + + { totalReward.toFixed(8) } + ) } - - - { totalReward.toFixed(8) } - - { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && ( diff --git a/ui/marketplace/MarketplaceAppCard.tsx b/ui/marketplace/MarketplaceAppCard.tsx index d35533d2d3..7db60d3ec4 100644 --- a/ui/marketplace/MarketplaceAppCard.tsx +++ b/ui/marketplace/MarketplaceAppCard.tsx @@ -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 }} diff --git a/ui/nameDomains/NameDomainsActionBar.tsx b/ui/nameDomains/NameDomainsActionBar.tsx index a4230f9086..8518abceb2 100644 --- a/ui/nameDomains/NameDomainsActionBar.tsx +++ b/ui/nameDomains/NameDomainsActionBar.tsx @@ -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 } /> diff --git a/ui/pages/GasTracker.tsx b/ui/pages/GasTracker.tsx index 84d8b581a5..69efae2104 100644 --- a/ui/pages/GasTracker.tsx +++ b/ui/pages/GasTracker.tsx @@ -64,21 +64,12 @@ const GasTracker = () => { ); - const content = (() => { + const snippets = (() => { if (!isPlaceholderData && data?.gas_prices?.slow === null && data?.gas_prices.average === null && data.gas_prices.fast === null) { - return No data available yet; + return No recent data available; } - return ( - <> - { data?.gas_prices && } - { config.features.stats.isEnabled && ( - - - - ) } - - ); + return data?.gas_prices ? : null; })(); return ( @@ -88,7 +79,12 @@ const GasTracker = () => { secondRow={ titleSecondRow } withTextAd /> - { content } + { snippets } + { config.features.stats.isEnabled && ( + + + + ) } ); }; diff --git a/ui/pages/MarketplaceApp.pw.tsx b/ui/pages/MarketplaceApp.pw.tsx index 08d1be9b6d..7f35c17f7b 100644 --- a/ui/pages/MarketplaceApp.pw.tsx +++ b/ui/pages/MarketplaceApp.pw.tsx @@ -33,9 +33,9 @@ const testFn: Parameters[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); }); diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png index fa0ba6445e..96d1c2e66a 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png differ diff --git a/ui/pages/__screenshots__/NameDomains.pw.tsx_default_default-view-mobile-1.png b/ui/pages/__screenshots__/NameDomains.pw.tsx_default_default-view-mobile-1.png index 49dedc7bc7..4aa9eaba13 100644 Binary files a/ui/pages/__screenshots__/NameDomains.pw.tsx_default_default-view-mobile-1.png and b/ui/pages/__screenshots__/NameDomains.pw.tsx_default_default-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/NameDomains.pw.tsx_mobile_default-view-mobile-1.png b/ui/pages/__screenshots__/NameDomains.pw.tsx_mobile_default-view-mobile-1.png index 2af4ecf5ae..90abdecd57 100644 Binary files a/ui/pages/__screenshots__/NameDomains.pw.tsx_mobile_default-view-mobile-1.png and b/ui/pages/__screenshots__/NameDomains.pw.tsx_mobile_default-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-2.png b/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-2.png index 3958a1ced0..857a2d1515 100644 Binary files a/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-2.png and b/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-2.png differ diff --git a/ui/shared/Toast.tsx b/ui/shared/Toast.tsx index 67451e02f6..c886a67438 100644 --- a/ui/shared/Toast.tsx +++ b/ui/shared/Toast.tsx @@ -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" > - { title && { title } } + { title && { icon }{ title } } { description && ( { description } diff --git a/ui/sol2uml/Sol2UmlDiagram.tsx b/ui/sol2uml/Sol2UmlDiagram.tsx index eaebf9c3c1..840b05fb9e 100644 --- a/ui/sol2uml/Sol2UmlDiagram.tsx +++ b/ui/sol2uml/Sol2UmlDiagram.tsx @@ -1,6 +1,7 @@ import { chakra, Tooltip, useColorModeValue } from '@chakra-ui/react'; import React from 'react'; +import type * as visualizer from '@blockscout/visualizer-types'; import type { SmartContract } from 'types/api/contract'; import type { ResourceError } from 'lib/api/resources'; @@ -13,7 +14,7 @@ interface Props { addressHash: string; } -function composeSources(contract: SmartContract | undefined) { +function composeSources(contract: SmartContract | undefined): visualizer.VisualizeStorageRequest['sources'] { if (!contract) { return {}; } @@ -23,7 +24,7 @@ function composeSources(contract: SmartContract | undefined) { }, {}); return { - [contract.file_path || 'index.sol']: contract.source_code, + [contract.file_path || 'index.sol']: contract.source_code || '', ...additionalSources, }; } @@ -76,7 +77,7 @@ const Sol2UmlDiagram = ({ addressHash }: Props) => { return ( ; + charts?: Array; interval: StatsIntervalIds; } diff --git a/ui/stats/NumberWidgetsList.tsx b/ui/stats/NumberWidgetsList.tsx index 758d9810c5..ad222401b5 100644 --- a/ui/stats/NumberWidgetsList.tsx +++ b/ui/stats/NumberWidgetsList.tsx @@ -29,7 +29,7 @@ const NumberWidgetsList = () => { data?.counters?.map(({ id, title, value, units, description }, index) => { let unitsStr = ''; - if (UNITS_WITHOUT_SPACE.includes(units)) { + if (units && UNITS_WITHOUT_SPACE.includes(units)) { unitsStr = units; } else if (units) { unitsStr = ' ' + units; diff --git a/ui/stats/StatsFilters.tsx b/ui/stats/StatsFilters.tsx index df96c3ac35..09891555e4 100644 --- a/ui/stats/StatsFilters.tsx +++ b/ui/stats/StatsFilters.tsx @@ -1,7 +1,7 @@ import { Grid, GridItem, Skeleton } from '@chakra-ui/react'; import React from 'react'; -import type { StatsChartsSection } from 'types/api/stats'; +import type * as stats from '@blockscout/stats-types'; import type { StatsInterval, StatsIntervalIds } from 'types/client/stats'; import FilterInput from 'ui/shared/filters/FilterInput'; @@ -15,7 +15,7 @@ const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({ })) as Array; type Props = { - sections?: Array; + sections?: Array; currentSection: string; onSectionChange: (newSection: string) => void; interval: StatsIntervalIds; diff --git a/ui/stats/useStats.tsx b/ui/stats/useStats.tsx index 96117b9622..f4a15dc110 100644 --- a/ui/stats/useStats.tsx +++ b/ui/stats/useStats.tsx @@ -1,18 +1,18 @@ import { useRouter } from 'next/router'; import React, { useCallback, useMemo, useState } from 'react'; -import type { StatsChartInfo, StatsChartsSection } from 'types/api/stats'; +import type * as stats from '@blockscout/stats-types'; import type { StatsIntervalIds } from 'types/client/stats'; import useApiQuery from 'lib/api/useApiQuery'; import getQueryParamString from 'lib/router/getQueryParamString'; import { STATS_CHARTS } from 'stubs/stats'; -function isSectionMatches(section: StatsChartsSection, currentSection: string): boolean { +function isSectionMatches(section: stats.LineChartSection, currentSection: string): boolean { return currentSection === 'all' || section.id === currentSection; } -function isChartNameMatches(q: string, chart: StatsChartInfo) { +function isChartNameMatches(q: string, chart: stats.LineChartInfo) { return chart.title.toLowerCase().includes(q.toLowerCase()); } diff --git a/ui/tokenInstance/TokenInstanceMetadataFetcher.tsx b/ui/tokenInstance/TokenInstanceMetadataFetcher.tsx index 5a8af64044..255ed645c4 100644 --- a/ui/tokenInstance/TokenInstanceMetadataFetcher.tsx +++ b/ui/tokenInstance/TokenInstanceMetadataFetcher.tsx @@ -1,4 +1,5 @@ -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(); + const toastId = React.useRef(); 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: , 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 ( diff --git a/yarn.lock b/yarn.lock index 2e79f65273..fe7b49bf07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1327,10 +1327,20 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@blockscout/bens-types@v1.3.0-beta": - version "1.3.0-beta" - resolved "https://registry.yarnpkg.com/@blockscout/bens-types/-/bens-types-1.3.0-beta.tgz#791fa102a33b1add14188beffe7c879219825424" - integrity sha512-Gh4qYrj7bNo6fzPGsdtPDPqwboxv/4OWx9QyrblkKt4YSNAlyElIS9rcpGGJ9rVP3YyhflaCh52YOvtDcRXRLw== +"@blockscout/bens-types@1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@blockscout/bens-types/-/bens-types-1.3.4.tgz#e75b863c6d065e7d6d5d01e1a1d64da8df261640" + integrity sha512-kKRa8jKu/CBLR3QbWpRXmtwIXiIwIPDrFeEPIYUQp5bg9uY+ActOyQERixo/9FE+BHZShWUDm+75FoaAmIGIOw== + +"@blockscout/stats-types@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@blockscout/stats-types/-/stats-types-1.6.0.tgz#cdb27ab3d3cb1eef7b8b069c39d4e09afda1aec9" + integrity sha512-MzItYOsLa3zgoFzRgFAgg7gynSXG0w/GqHzg5BGHcBPbPSp/g7A6mMtyIchI6TnZxxnCwziHHvzmJFXz11emUg== + +"@blockscout/visualizer-types@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@blockscout/visualizer-types/-/visualizer-types-0.2.0.tgz#a8ef326d69e29417db124c78f868d68aca959ad1" + integrity sha512-gasqbEL89iH8YnH/TIEk0MBSG9SwhEJegY9tnQ1c/jFZOCYjiVkgNwm4oH0ncwCKoNX1GoKAregbkEUwDDw7FQ== "@braintree/sanitize-url@=6.0.4": version "6.0.4" @@ -8595,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" @@ -14846,16 +14856,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14983,14 +14984,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16150,7 +16144,7 @@ word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -16168,15 +16162,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"