From 674dc73e423fba04ad168c2b19247e6cc5a330f0 Mon Sep 17 00:00:00 2001 From: tom Date: Tue, 19 May 2026 20:04:08 +0200 Subject: [PATCH 01/85] =?UTF-8?q?track:=206-10=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3462?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- client/MIGRATION_TASKS.md | 14 +++++++------- docs/GLOSSARY.md | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index a093ef3899..d3b731d71a 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -190,13 +190,13 @@ One PR per chain variant. Each goes under `client/features/chain-variants/ ### 5-4 · [x] Feature: `chain-variants/suave` · [#3443](https://github.com/blockscout/frontend/issues/3443) **Scope:** SUAVE Kettle UI → `client/features/chain-variants/suave/`. See `configs/app/features/suave.ts`. -### 5-5 · [~] Feature: `chain-variants/mega-eth` · [#3449](https://github.com/blockscout/frontend/issues/3449) +### 5-5 · [x] Feature: `chain-variants/mega-eth` · [#3449](https://github.com/blockscout/frontend/issues/3449) **Scope:** MegaETH Flashblocks UI → `client/features/chain-variants/mega-eth/`. See `configs/app/features/megaEth.ts` and `flashblocks.ts`. ### 5-6 · [x] Feature: `chain-variants/beacon-chain` · [#3442](https://github.com/blockscout/frontend/issues/3442) **Scope:** Beacon chain deposits/withdrawals UI → `client/features/chain-variants/beacon-chain/`. See `configs/app/features/beaconChain.ts`. -### 5-7 · [~] Feature: `chain-variants/mud` · [#3450](https://github.com/blockscout/frontend/issues/3450) +### 5-7 · [x] Feature: `chain-variants/mud` · [#3450](https://github.com/blockscout/frontend/issues/3450) --- @@ -205,16 +205,16 @@ One PR per chain variant. Each goes under `client/features/chain-variants/ One PR per feature. Features that are pure infrastructure (analytics, monitoring, A/B flags) were migrated to `client/shared/` in 1-2 and do not appear here. -### 6-1 · [~] Feature: `user-ops` · [#3452](https://github.com/blockscout/frontend/issues/3452) +### 6-1 · [x] Feature: `user-ops` · [#3452](https://github.com/blockscout/frontend/issues/3452) -### 6-2 · [~] Feature: `data-availability` · [#3453](https://github.com/blockscout/frontend/issues/3453) +### 6-2 · [x] Feature: `data-availability` · [#3453](https://github.com/blockscout/frontend/issues/3453) ### 6-3 · [ ] Feature: `multichain` **Scope:** Includes `lib/multichain/`, `lib/contexts/multichain.tsx`, `ui/snippets/networkMenu/` → `client/features/multichain/` -### 6-4 · [~] Feature: `name-services` (domains and clusters) · [#3458](https://github.com/blockscout/frontend/issues/3458) +### 6-4 · [x] Feature: `name-services` (domains and clusters) · [#3458](https://github.com/blockscout/frontend/issues/3458) -### 6-5 · [~] Feature: `account` · [#3456](https://github.com/blockscout/frontend/issues/3456) +### 6-5 · [x] Feature: `account` · [#3456](https://github.com/blockscout/frontend/issues/3456) **Scope:** Includes `lib/hooks/useGetCsrfToken`, `ui/snippets/auth/`, `ui/snippets/user/` → `client/features/account/` ### 6-6 · [ ] Feature: `stats` @@ -230,7 +230,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-9 · [ ] Feature: `marketplace` **Scope:** Includes `lib/contexts/marketplace.tsx`, `lib/hooks/useGraphLinks` → `client/features/marketplace/` -### 6-10 · [ ] Feature: `rewards` +### 6-10 · [~] Feature: `rewards` · [#3462](https://github.com/blockscout/frontend/issues/3462) **Scope:** Includes `lib/contexts/rewards.tsx`, `lib/hooks/useRewardsActivity` → `client/features/rewards/` ### 6-11 · [ ] Feature: `advanced-filter` diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index eaaa2ccafa..067f2bb054 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -25,6 +25,7 @@ Blockchain objects that appear as first-class UI elements — detail pages, list | Term | Definition | |------|------------| | **Blob** | An individual EIP-4844 data blob attached to a transaction. A single transaction can carry multiple blobs. Each blob has its own detail page (`/blob/[hash]`) and is a first-class entity in the UI (with its own components, API resource, and stub). The term "blob" remains correct for the entity — only the *feature folder* uses `data-availability` (see **Data Availability**). | +| **Block Reward** | A blockchain-level payout issued to a block producer (miner, validator, etc.). Typed as `Reward` in `client/slices/block/types/api.ts` with subtypes: `Miner Reward`, `Validator Reward`, `Emission Reward`, `Chore Reward`, `Uncle Reward`, `POA Mania Reward`. Entirely distinct from the **Rewards** (Merits) program — no shared code, API, or folder. | | **CCTX (Cross-Chain Transaction)** | A ZetaChain-specific transaction type that spans multiple chains. Displayed as a separate tab on the transactions list page. Unrelated to the general cross-chain transactions feature (`interchainIndexer`). | | **Dispute Games** | Part of the Optimism **Fault Proof System**. On-chain games used to challenge and resolve disputed L2 output roots. Displayed in the rollup navigation when `faultProofSystem` is enabled. | | **Epoch** | A consensus time period specific to **Celo**. Has its own index and detail pages in the UI. Not a generic blockchain concept in this codebase — always refers to a Celo epoch. Lives under `features/chain-variants/celo/`. | @@ -58,7 +59,7 @@ Optional product areas and their backing services. Each corresponds to a folder | **Multichain** | The feature that aggregates data across multiple Blockscout-indexed chains into a single explorer view. Introduces an "Ecosystems" page and a multichain balance button. Config-gated via `NEXT_PUBLIC_MULTICHAIN_ENABLED`. | | **Pools** | DEX liquidity pool positions tracked on-chain. Exposed as a "DEX tracker" section under Tokens. Config-gated via `NEXT_PUBLIC_DEX_POOLS_ENABLED`. | | **Public Tags** | Community-submitted labels for addresses, visible on address pages. Submitted via a dedicated form. Config-gated via `NEXT_PUBLIC_METADATA_SERVICE_API_HOST`. | -| **Rewards** | A token rewards and incentives program operated by Blockscout. Has its own API, context provider, and UI modals. Config-gated via `NEXT_PUBLIC_REWARDS_SERVICE_API_HOST`. | +| **Rewards** | The Blockscout Merits program — a token rewards and incentives system operated by Blockscout. Has its own external API (`@blockscout/points-types`), context provider, and UI modals. Config-gated via `NEXT_PUBLIC_REWARDS_SERVICE_API_HOST`. Lives under `client/features/rewards/`. Entirely distinct from **Block Reward** (on-chain block-producer payouts), which belongs to `client/slices/block/`. | | **SolidityScan** | A third-party smart contract security vulnerability scanner integrated into contract detail pages. Displays a security score and known vulnerability findings. | | **SUAVE** | A MEV-focused chain developed by Flashbots, built around a trusted execution environment (TEE) architecture. Introduces the **Kettle** entity. | | **TAC (Ton Application Chain)** | A chain that bridges the TON blockchain and EVM ecosystems. Introduces the **Operation** entity. Its data comes from a dedicated `tac-operation-lifecycle` microservice. Config-gated via `NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST`. | From f922e9fd21ea282c313035774fab8d3098cad573 Mon Sep 17 00:00:00 2001 From: tom goriunov Date: Tue, 19 May 2026 20:23:42 +0200 Subject: [PATCH 02/85] Average block time: live refresh via stats API refetch interval ENV (#3461) * Average block time: new ENV to update in realtime Resolves #3407 * [skip ci] fixes --- .../pages/index/ChainStatsCounters.tsx | 12 +++++- client/features/chain-stats/types/config.ts | 10 +++++ client/slices/home/hooks/useChartDataQuery.ts | 18 ++++++++- .../pages/index/charts/ChainIndicators.tsx | 7 ++++ .../slices/home/pages/index/stats/Stats.tsx | 9 ++++- configs/app/apis.ts | 18 ++++++++- deploy/tools/envs-validator/schemas/apis.ts | 39 ++++++++++++++++++- deploy/tools/envs-validator/test/.env.base | 1 + docs/ENVS.md | 1 + 9 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 client/features/chain-stats/types/config.ts diff --git a/client/features/chain-stats/pages/index/ChainStatsCounters.tsx b/client/features/chain-stats/pages/index/ChainStatsCounters.tsx index cb019b6d49..3464d51cd6 100644 --- a/client/features/chain-stats/pages/index/ChainStatsCounters.tsx +++ b/client/features/chain-stats/pages/index/ChainStatsCounters.tsx @@ -5,6 +5,7 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import config from 'configs/app'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import StatsWidget from 'ui/shared/stats/StatsWidget'; @@ -13,13 +14,20 @@ import { CHAIN_STATS_COUNTER } from '../../stubs/counters'; const UNITS_WITHOUT_SPACE = [ 's' ]; const ChainStatsCounters = () => { - const { data, isPlaceholderData, isError } = useApiQuery('stats:counters', { + const { data, isPlaceholderData, isError, isRefetchError } = useApiQuery('stats:counters', { queryOptions: { placeholderData: { counters: Array(10).fill(CHAIN_STATS_COUNTER) }, + refetchInterval: (query) => { + if (query.state.status === 'error') { + return false; + } + + return config.apis.stats?.refetchInterval?.[ 'stats:counters' ]; + }, }, }); - if (isError) { + if (isError && !isRefetchError) { return ; } diff --git a/client/features/chain-stats/types/config.ts b/client/features/chain-stats/types/config.ts new file mode 100644 index 0000000000..5c6fffe5b6 --- /dev/null +++ b/client/features/chain-stats/types/config.ts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { StatsApiResourceName } from 'client/api/services/stats'; + +export const STATS_API_RESOURCES_REFETCH_INTERVAL = [ + 'stats:counters', + 'stats:pages_main', +] satisfies Array; + +export type StatsApiResourceNameRefetchInterval = typeof STATS_API_RESOURCES_REFETCH_INTERVAL[number]; diff --git a/client/slices/home/hooks/useChartDataQuery.ts b/client/slices/home/hooks/useChartDataQuery.ts index 6f790e9ff8..3e29ef4b1c 100644 --- a/client/slices/home/hooks/useChartDataQuery.ts +++ b/client/slices/home/hooks/useChartDataQuery.ts @@ -27,6 +27,13 @@ export default function useChartDataQuery(indicatorId: ChainIndicatorId): UseFet refetchOnMount: false, enabled: isStatsFeatureEnabled && indicatorId === 'daily_txs', select: (data) => data.daily_new_transactions?.chart.map((item) => ({ date: new Date(item.date), value: Number(item.value) })) || [], + refetchInterval: (query) => { + if (query.state.status === 'error') { + return false; + } + + return config.apis.stats?.refetchInterval?.[ 'stats:pages_main' ]; + }, }, }); @@ -42,6 +49,13 @@ export default function useChartDataQuery(indicatorId: ChainIndicatorId): UseFet } return []; }, + refetchInterval: (query) => { + if (query.state.status === 'error') { + return false; + } + + return config.apis.stats?.refetchInterval?.[ 'stats:pages_main' ]; + }, }, }); @@ -108,14 +122,14 @@ export default function useChartDataQuery(indicatorId: ChainIndicatorId): UseFet const query = isStatsFeatureEnabled ? statsDailyTxsQuery : apiDailyTxsQuery; return { data: getChartData(indicatorId, query.data || []), - isError: query.isError, + isError: query.isError && !query.isRefetchError, isPending: query.isPending, }; } case 'daily_operational_txs': { return { data: getChartData(indicatorId, statsDailyOperationalTxsQuery.data || []), - isError: statsDailyOperationalTxsQuery.isError, + isError: statsDailyOperationalTxsQuery.isError && !statsDailyOperationalTxsQuery.isRefetchError, isPending: statsDailyOperationalTxsQuery.isPending, }; } diff --git a/client/slices/home/pages/index/charts/ChainIndicators.tsx b/client/slices/home/pages/index/charts/ChainIndicators.tsx index 645e975ebf..b639ed27fa 100644 --- a/client/slices/home/pages/index/charts/ChainIndicators.tsx +++ b/client/slices/home/pages/index/charts/ChainIndicators.tsx @@ -31,6 +31,13 @@ const ChainIndicators = () => { refetchOnMount: false, enabled: isStatsFeatureEnabled, placeholderData: HOMEPAGE_STATS_MICROSERVICE, + refetchInterval: (query) => { + if (query.state.status === 'error') { + return false; + } + + return config.apis.stats?.refetchInterval?.[ 'stats:pages_main' ]; + }, }, }); diff --git a/client/slices/home/pages/index/stats/Stats.tsx b/client/slices/home/pages/index/stats/Stats.tsx index 7e864be72f..0b11fbdffb 100644 --- a/client/slices/home/pages/index/stats/Stats.tsx +++ b/client/slices/home/pages/index/stats/Stats.tsx @@ -40,6 +40,13 @@ const Stats = () => { refetchOnMount: false, placeholderData: isStatsFeatureEnabled ? HOMEPAGE_STATS_MICROSERVICE : undefined, enabled: isStatsFeatureEnabled, + refetchInterval: (query) => { + if (query.state.status === 'error') { + return false; + } + + return config.apis.stats?.refetchInterval?.[ 'stats:pages_main' ]; + }, }, }); @@ -60,7 +67,7 @@ const Stats = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isPlaceholderData ]); - const hasStatsError = apiQuery.isError || statsQuery.isError || blocksQuery?.isError || latestBatchQuery?.isError; + const hasStatsError = apiQuery.isError || (statsQuery.isError && !statsQuery.isRefetchError) || blocksQuery?.isError || latestBatchQuery?.isError; if (hasStatsError) { return ; diff --git a/configs/app/apis.ts b/configs/app/apis.ts index 33e052f39f..c702a102c1 100644 --- a/configs/app/apis.ts +++ b/configs/app/apis.ts @@ -1,16 +1,23 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import { pickBy } from 'es-toolkit'; + import type { ApiName } from 'client/api/types'; +import { STATS_API_RESOURCES_REFETCH_INTERVAL } from 'client/features/chain-stats/types/config'; +import type { StatsApiResourceNameRefetchInterval } from 'client/features/chain-stats/types/config'; + +import type { ResourceName } from 'client/api/resources'; import { stripTrailingSlash } from 'toolkit/utils/url'; -import { getEnvValue } from './utils'; +import { getEnvValue, parseEnvJson } from './utils'; export interface ApiPropsBase { endpoint: string; basePath?: string; socketEndpoint?: string; instanceId?: string; + refetchInterval?: Partial>; } export interface ApiPropsFull extends ApiPropsBase { @@ -158,9 +165,18 @@ const statsApi = (() => { return; } + const refetchInterval = (() => { + const refetchInterval = parseEnvJson>(getEnvValue('NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL')); + + if (refetchInterval) { + return pickBy(refetchInterval, (value, key) => STATS_API_RESOURCES_REFETCH_INTERVAL.includes(key) && typeof value === 'number'); + } + })(); + return Object.freeze({ endpoint: apiHost, basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_STATS_API_BASE_PATH') || ''), + refetchInterval, }); })(); diff --git a/deploy/tools/envs-validator/schemas/apis.ts b/deploy/tools/envs-validator/schemas/apis.ts index bf1106c137..dafa947037 100644 --- a/deploy/tools/envs-validator/schemas/apis.ts +++ b/deploy/tools/envs-validator/schemas/apis.ts @@ -1,5 +1,16 @@ import * as yup from 'yup'; -import { protocols, urlTest } from '../utils'; +import { getYupValidationErrorMessage, protocols, urlTest } from '../utils'; +import { replaceQuotes } from 'configs/app/utils'; +import { StatsApiResourceNameRefetchInterval } from 'client/features/chain-stats/types/config'; + +const statsApiRefetchIntervalSchema = yup.object>() + .transform(replaceQuotes) + .json() + .shape({ + 'stats:counters': yup.number().integer().positive(), + 'stats:pages_main': yup.number().integer().positive(), + }) + .exact(); export default yup.object({ NEXT_PUBLIC_API_PROTOCOL: yup.string().oneOf(protocols), @@ -10,6 +21,32 @@ export default yup.object({ NEXT_PUBLIC_STATS_API_HOST: yup.string().test(urlTest), NEXT_PUBLIC_STATS_API_BASE_PATH: yup.string(), + NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL: yup + .mixed() + .test( + 'shape', + (ctx) => { + try { + statsApiRefetchIntervalSchema.validateSync(ctx.originalValue); + throw new Error('Unknown validation error'); + } catch (error: unknown) { + const message = getYupValidationErrorMessage(error); + return 'Invalid schema was provided for NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL' + (message ? `: ${ message }` : ''); + } + }, + (data) => { + const isUndefined = data === undefined; + return isUndefined || statsApiRefetchIntervalSchema.isValidSync(data); + }) + .when('NEXT_PUBLIC_STATS_API_HOST', { + is: (value: string) => Boolean(value), + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL can only be used with NEXT_PUBLIC_STATS_API_HOST', + value => value === undefined, + ), + }), NEXT_PUBLIC_VISUALIZE_API_HOST: yup.string().test(urlTest), NEXT_PUBLIC_VISUALIZE_API_BASE_PATH: yup.string(), diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index 061b02cfe7..b1c103ee95 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -69,6 +69,7 @@ NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE=true NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global NEXT_PUBLIC_STATS_API_HOST=https://example.com NEXT_PUBLIC_STATS_API_BASE_PATH=/ +NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL={'stats:counters': 10000, 'stats:pages_main': 10000} NEXT_PUBLIC_USE_NEXT_JS_PROXY=false NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16'] diff --git a/docs/ENVS.md b/docs/ENVS.md index 8cbaa180c4..e7a9fd5b7b 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -684,6 +684,7 @@ Essential dapps are built-in dapps that are displayed on the Marketplace page in | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_STATS_API_HOST | `string` | Stats API endpoint url | Required | - | `https://stats.services.blockscout.com` | v1.0.x+ | | NEXT_PUBLIC_STATS_API_BASE_PATH | `string` | Base path for Stats API endpoint url | - | - | `/poa/core` | v1.29.0+ | +| NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL | `Record` | Map that holds information about time in milliseconds that the resources should be continuously refetched; possible values for `StatsApiResourceName` are: `stats:counters`, `stats:pages_main` | - | - | `{'stats:pages_main': 10000}` | |   From 71a0cb7a48084c4205c025b1b19783ac5a9cd416 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 20:24:04 +0200 Subject: [PATCH 03/85] [Migration 6-4] Feature: name-services (domains and clusters) (#3460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: migrate name-services files to client/features/name-services/ Move domains and clusters files from legacy paths into client/features/name-services/: - types/api/ens.ts → domains/types/api.ts (merged) - types/api/clusters.ts → clusters/types/api.ts (merged) - stubs/ENS.ts → domains/stubs.ts - stubs/clusters.ts → clusters/stubs.ts - mocks/ens/ → domains/mocks/ - mocks/clusters/ → clusters/mocks/ - ui/shared/entities/ens/ → domains/components/ - ui/shared/entities/clusters/ → clusters/components/ - lib/clusters/ → clusters/hooks/ and clusters/utils/ - ui/nameServices/domains/ → domains/pages/index/ - ui/nameServices/directories/ → clusters/pages/index/ - ui/nameDomain/ → domains/pages/details/ - ui/cluster/ → clusters/pages/details/ - ui/pages/NameServices.tsx → common/pages/ - ui/pages/NameDomain.tsx → domains/pages/details/ - ui/pages/Cluster.tsx → clusters/pages/details/ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * fixes --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> Co-authored-by: tom --- client/api/services/bens.ts | 2 +- client/api/services/clusters.ts | 2 +- .../items/SearchResultItemDomain.tsx | 2 +- .../components}/ClustersEntity.pw.tsx | 3 +- .../clusters/components}/ClustersEntity.tsx | 3 +- ..._dark-color-mode_dark-mode-dark-mode-1.png | Bin ...t_basic-display-basic-cluster-entity-1.png | Bin ...basic-display-cluster-with-subdomain-1.png | Bin ...pw.tsx_default_customization-no-icon-1.png | Bin ...pw.tsx_default_customization-no-link-1.png | Bin ...y.pw.tsx_default_dark-mode-dark-mode-1.png | Bin ...ity.pw.tsx_default_hover-interaction-1.png | Bin ...default_long-cluster-name-truncation-1.png | Bin ...tsx_default_variants-heading-variant-1.png | Bin ..._default_variants-subheading-variant-1.png | Bin .../hooks}/useAddressClusters.spec.ts | 0 .../clusters/hooks}/useAddressClusters.ts | 0 .../hooks}/useClusterPagination.spec.ts | 2 +- .../clusters/hooks}/useClusterPagination.ts | 0 .../clusters/hooks}/useClusterSearch.spec.ts | 0 .../clusters/hooks}/useClusterSearch.ts | 0 .../clusters/hooks}/useClustersData.spec.ts | 5 +- .../clusters/hooks}/useClustersData.ts | 8 +- ...spec.tsx => useSearchWithClusters.spec.ts} | 0 ...hClusters.tsx => useSearchWithClusters.ts} | 0 .../name-services/clusters/mocks}/cluster.ts | 2 +- .../clusters/mocks}/directory.ts | 2 +- .../clusters/mocks}/leaderboard.ts | 2 +- .../pages/address/AddressClusters.tsx | 7 +- .../clusters/pages/details}/Cluster.pw.tsx | 3 +- .../clusters/pages/details}/Cluster.tsx | 3 +- .../pages/details}/ClusterDetails.tsx | 5 +- ...-Page-mainnet-cluster-details-mobile-1.png | Bin ...-Page-testnet-cluster-details-mobile-1.png | Bin ...-Page-mainnet-cluster-details-mobile-1.png | Bin ...-Page-testnet-cluster-details-mobile-1.png | Bin .../clusters/pages/index}/Clusters.tsx | 19 +-- .../pages/index}/ClustersActionBar.tsx | 7 +- .../index}/ClustersDirectoryListItem.tsx | 5 +- .../pages/index}/ClustersDirectoryTable.tsx | 2 +- .../index}/ClustersDirectoryTableItem.tsx | 5 +- .../index}/ClustersLeaderboardListItem.tsx | 5 +- .../pages/index}/ClustersLeaderboardTable.tsx | 2 +- .../index}/ClustersLeaderboardTableItem.tsx | 5 +- .../features/name-services/clusters/stubs.ts | 2 +- .../name-services/clusters/types/api.ts | 119 +++++++++++++++++ .../clusters/utils/action-bar-utils.spec.ts | 2 +- .../clusters/utils/action-bar-utils.ts | 0 .../clusters/utils/clusters-utils.spec.ts | 2 +- .../clusters/utils/clusters-utils.ts | 2 +- .../clusters/utils/detect-input-type.spec.ts | 2 +- .../clusters/utils/detect-input-type.ts | 0 .../clusters/utils/page-utils.spec.ts | 6 +- .../clusters/utils/page-utils.ts | 4 +- .../common}/pages/NameServices.pw.tsx | 7 +- .../common}/pages/NameServices.tsx | 5 +- ...x_default_directories-directory-view-1.png | Bin ...default_directories-leaderboard-view-1.png | Bin ...lt_directories-mobile-directory-view-1.png | Bin ..._directories-mobile-leaderboard-view-1.png | Bin ....pw.tsx_default_domains-default-view-1.png | Bin ...vices.pw.tsx_default_domains-filters-1.png | Bin ..._default_domains-mobile-default-view-1.png | Bin .../domains/components}/EnsEntity.pw.tsx | 3 +- .../domains/components}/EnsEntity.tsx | 3 +- .../components}/NameDomainExpiryStatus.tsx | 0 .../components/SearchBarSuggestDomain.tsx | 3 +- ...sEntity.pw.tsx_default_customization-1.png | Bin .../EnsEntity.pw.tsx_default_loading-1.png | Bin ...ault_tooltip-test-with-protocol-info-1.png | Bin ...ntity.pw.tsx_default_variant-content-1.png | Bin ...ty.pw.tsx_default_variant-subheading-1.png | Bin ...Entity.pw.tsx_default_with-long-name-1.png | Bin .../name-services/domains/mocks}/domain.ts | 0 .../name-services/domains/mocks}/events.ts | 0 .../domains/{stubs => mocks}/search.ts | 0 .../pages/address/AddressEnsDomains.pw.tsx | 3 +- .../pages/address/AddressEnsDomains.tsx | 3 +- .../domains/pages/details}/NameDomain.pw.tsx | 5 +- .../domains/pages/details}/NameDomain.tsx | 9 +- ...ameDomain.pw.tsx_default_details-tab-1.png | Bin ...in.pw.tsx_default_history-tab-mobile-1.png | Bin ...ain.pw.tsx_mobile_history-tab-mobile-1.png | Bin .../details/history}/NameDomainHistory.tsx | 9 +- .../history/NameDomainHistoryListItem.tsx | 0 .../history/NameDomainHistoryTable.tsx | 0 .../history/NameDomainHistoryTableItem.tsx | 0 .../domains/pages/details}/history/utils.ts | 0 .../pages/details/info}/NameDomainDetails.tsx | 5 +- .../details/info}/NameDomainDetailsAlert.tsx | 0 .../domains/pages/index}/NameDomains.tsx | 5 +- .../pages/index}/NameDomainsActionBar.tsx | 2 +- .../pages/index}/NameDomainsListItem.tsx | 5 +- .../domains/pages/index}/NameDomainsTable.tsx | 0 .../pages/index}/NameDomainsTableItem.tsx | 3 +- .../domains/pages/index}/utils.ts | 2 +- .../features/name-services/domains/stubs.ts | 0 .../name-services/domains/types/api.ts | 14 ++ .../common/components/TxInterpretation.tsx | 3 +- .../slices/address/pages/details/Address.tsx | 4 +- .../search-bar/SearchBarDesktop.pw.tsx | 2 +- .../search-bar/SearchBarMobile.pw.tsx | 2 +- client/slices/search/mocks.ts | 2 +- .../search-results/SearchResultListItem.tsx | 2 +- .../search-results/SearchResultTableItem.tsx | 2 +- .../pages/search-results/SearchResults.pw.tsx | 2 +- pages/name-services/clusters/[name].tsx | 2 +- pages/name-services/domains/[name].tsx | 2 +- pages/name-services/index.tsx | 2 +- types/api/clusters.ts | 121 ------------------ types/api/ens.ts | 18 --- ui/multichain/address/MultichainAddress.tsx | 3 +- .../header/MultichainAddressEnsDomains.tsx | 3 +- ui/showcases/Link.tsx | 4 +- 114 files changed, 260 insertions(+), 240 deletions(-) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/ClustersEntity.pw.tsx (97%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/ClustersEntity.tsx (98%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_dark-color-mode_dark-mode-dark-mode-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-basic-cluster-entity-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-cluster-with-subdomain-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-icon-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-link-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_dark-mode-dark-mode-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_hover-interaction-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_long-cluster-name-truncation-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_variants-heading-variant-1.png (100%) rename {ui/shared/entities/clusters => client/features/name-services/clusters/components}/__screenshots__/ClustersEntity.pw.tsx_default_variants-subheading-variant-1.png (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useAddressClusters.spec.ts (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useAddressClusters.ts (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClusterPagination.spec.ts (98%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClusterPagination.ts (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClusterSearch.spec.ts (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClusterSearch.ts (100%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClustersData.spec.ts (98%) rename {lib/clusters => client/features/name-services/clusters/hooks}/useClustersData.ts (90%) rename client/features/name-services/clusters/hooks/{useSearchWithClusters.spec.tsx => useSearchWithClusters.spec.ts} (100%) rename client/features/name-services/clusters/hooks/{useSearchWithClusters.tsx => useSearchWithClusters.ts} (100%) rename {mocks/clusters => client/features/name-services/clusters/mocks}/cluster.ts (97%) rename {mocks/clusters => client/features/name-services/clusters/mocks}/directory.ts (97%) rename {mocks/clusters => client/features/name-services/clusters/mocks}/leaderboard.ts (96%) rename {ui/pages => client/features/name-services/clusters/pages/details}/Cluster.pw.tsx (97%) rename {ui/pages => client/features/name-services/clusters/pages/details}/Cluster.tsx (91%) rename {ui/cluster => client/features/name-services/clusters/pages/details}/ClusterDetails.tsx (94%) rename {ui/pages => client/features/name-services/clusters/pages/details}/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png (100%) rename {ui/pages => client/features/name-services/clusters/pages/details}/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png (100%) rename {ui/pages => client/features/name-services/clusters/pages/details}/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png (100%) rename {ui/pages => client/features/name-services/clusters/pages/details}/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png (100%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/Clusters.tsx (90%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersActionBar.tsx (97%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersDirectoryListItem.tsx (92%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersDirectoryTable.tsx (94%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersDirectoryTableItem.tsx (90%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersLeaderboardListItem.tsx (90%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersLeaderboardTable.tsx (92%) rename {ui/nameServices/directories => client/features/name-services/clusters/pages/index}/ClustersLeaderboardTableItem.tsx (86%) rename stubs/clusters.ts => client/features/name-services/clusters/stubs.ts (77%) rename lib/clusters/actionBarUtils.spec.ts => client/features/name-services/clusters/utils/action-bar-utils.spec.ts (98%) rename lib/clusters/actionBarUtils.ts => client/features/name-services/clusters/utils/action-bar-utils.ts (100%) rename lib/clusters/clustersUtils.spec.ts => client/features/name-services/clusters/utils/clusters-utils.spec.ts (99%) rename lib/clusters/clustersUtils.ts => client/features/name-services/clusters/utils/clusters-utils.ts (90%) rename lib/clusters/detectInputType.spec.ts => client/features/name-services/clusters/utils/detect-input-type.spec.ts (84%) rename lib/clusters/detectInputType.ts => client/features/name-services/clusters/utils/detect-input-type.ts (100%) rename lib/clusters/pageUtils.spec.ts => client/features/name-services/clusters/utils/page-utils.spec.ts (97%) rename lib/clusters/pageUtils.ts => client/features/name-services/clusters/utils/page-utils.ts (96%) rename {ui => client/features/name-services/common}/pages/NameServices.pw.tsx (94%) rename {ui => client/features/name-services/common}/pages/NameServices.tsx (84%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png (100%) rename {ui => client/features/name-services/common}/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/EnsEntity.pw.tsx (95%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/EnsEntity.tsx (98%) rename {ui/nameDomain => client/features/name-services/domains/components}/NameDomainExpiryStatus.tsx (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_customization-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_tooltip-test-with-protocol-info-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_variant-content-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_variant-subheading-1.png (100%) rename {ui/shared/entities/ens => client/features/name-services/domains/components}/__screenshots__/EnsEntity.pw.tsx_default_with-long-name-1.png (100%) rename {mocks/ens => client/features/name-services/domains/mocks}/domain.ts (100%) rename {mocks/ens => client/features/name-services/domains/mocks}/events.ts (100%) rename client/features/name-services/domains/{stubs => mocks}/search.ts (100%) rename {ui/pages => client/features/name-services/domains/pages/details}/NameDomain.pw.tsx (90%) rename {ui/pages => client/features/name-services/domains/pages/details}/NameDomain.tsx (89%) rename {ui/pages => client/features/name-services/domains/pages/details}/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png (100%) rename {ui/pages => client/features/name-services/domains/pages/details}/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png (100%) rename {ui/pages => client/features/name-services/domains/pages/details}/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png (100%) rename {ui/nameDomain => client/features/name-services/domains/pages/details/history}/NameDomainHistory.tsx (88%) rename {ui/nameDomain => client/features/name-services/domains/pages/details}/history/NameDomainHistoryListItem.tsx (100%) rename {ui/nameDomain => client/features/name-services/domains/pages/details}/history/NameDomainHistoryTable.tsx (100%) rename {ui/nameDomain => client/features/name-services/domains/pages/details}/history/NameDomainHistoryTableItem.tsx (100%) rename {ui/nameDomain => client/features/name-services/domains/pages/details}/history/utils.ts (100%) rename {ui/nameDomain => client/features/name-services/domains/pages/details/info}/NameDomainDetails.tsx (98%) rename {ui/nameDomain/details => client/features/name-services/domains/pages/details/info}/NameDomainDetailsAlert.tsx (100%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/NameDomains.tsx (98%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/NameDomainsActionBar.tsx (98%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/NameDomainsListItem.tsx (92%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/NameDomainsTable.tsx (100%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/NameDomainsTableItem.tsx (94%) rename {ui/nameServices/domains => client/features/name-services/domains/pages/index}/utils.ts (90%) rename stubs/ENS.ts => client/features/name-services/domains/stubs.ts (100%) delete mode 100644 types/api/clusters.ts delete mode 100644 types/api/ens.ts diff --git a/client/api/services/bens.ts b/client/api/services/bens.ts index ea2618c3e1..045b687097 100644 --- a/client/api/services/bens.ts +++ b/client/api/services/bens.ts @@ -2,7 +2,7 @@ import type { ApiResource } from '../types'; import type * as bens from '@blockscout/bens-types'; -import type { EnsAddressLookupFilters, EnsDomainLookupFilters, EnsLookupSorting } from 'types/api/ens'; +import type { EnsAddressLookupFilters, EnsDomainLookupFilters, EnsLookupSorting } from 'client/features/name-services/domains/types/api'; export const BENS_API_RESOURCES = { addresses_lookup: { diff --git a/client/api/services/clusters.ts b/client/api/services/clusters.ts index 7746a2f198..96126a1e9a 100644 --- a/client/api/services/clusters.ts +++ b/client/api/services/clusters.ts @@ -12,7 +12,7 @@ import type { ClustersDirectoryQueryParams, ClusterByIdQueryParams, ClusterByIdResponse, -} from 'types/api/clusters'; +} from 'client/features/name-services/clusters/types/api'; export const CLUSTERS_API_RESOURCES = { get_clusters_by_address: { diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemDomain.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemDomain.tsx index b56efa324a..2eeec3c12b 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemDomain.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemDomain.tsx @@ -7,7 +7,7 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import { route } from 'nextjs/routes'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/ui/shared/entities/clusters/ClustersEntity.pw.tsx b/client/features/name-services/clusters/components/ClustersEntity.pw.tsx similarity index 97% rename from ui/shared/entities/clusters/ClustersEntity.pw.tsx rename to client/features/name-services/clusters/components/ClustersEntity.pw.tsx index f036d68b41..7bbcc90a89 100644 --- a/ui/shared/entities/clusters/ClustersEntity.pw.tsx +++ b/client/features/name-services/clusters/components/ClustersEntity.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { longNameCluster } from 'mocks/clusters/directory'; +import { longNameCluster } from 'client/features/name-services/clusters/mocks/directory'; + import { test, expect } from 'playwright/lib'; import ClustersEntity from './ClustersEntity'; diff --git a/ui/shared/entities/clusters/ClustersEntity.tsx b/client/features/name-services/clusters/components/ClustersEntity.tsx similarity index 98% rename from ui/shared/entities/clusters/ClustersEntity.tsx rename to client/features/name-services/clusters/components/ClustersEntity.tsx index a4d1d7e25f..ff21ce4595 100644 --- a/ui/shared/entities/clusters/ClustersEntity.tsx +++ b/client/features/name-services/clusters/components/ClustersEntity.tsx @@ -11,10 +11,9 @@ import { Link as LinkToolkit } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps, getIconProps } from 'ui/shared/entities/base/utils'; import IconSvg from 'ui/shared/IconSvg'; -import { distributeEntityProps, getIconProps } from '../base/utils'; - const nameServicesFeature = config.features.nameServices; const clustersFeature = nameServicesFeature.isEnabled && nameServicesFeature.clusters.isEnabled ? nameServicesFeature.clusters : undefined; diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_dark-color-mode_dark-mode-dark-mode-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_dark-color-mode_dark-mode-dark-mode-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_dark-color-mode_dark-mode-dark-mode-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_dark-color-mode_dark-mode-dark-mode-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-basic-cluster-entity-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-basic-cluster-entity-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-basic-cluster-entity-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-basic-cluster-entity-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-cluster-with-subdomain-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-cluster-with-subdomain-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-cluster-with-subdomain-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_basic-display-cluster-with-subdomain-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-icon-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-icon-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-icon-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-icon-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-link-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-link-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-link-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_customization-no-link-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_dark-mode-dark-mode-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_dark-mode-dark-mode-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_dark-mode-dark-mode-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_dark-mode-dark-mode-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_hover-interaction-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_hover-interaction-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_hover-interaction-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_hover-interaction-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_long-cluster-name-truncation-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_long-cluster-name-truncation-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_long-cluster-name-truncation-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_long-cluster-name-truncation-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_variants-heading-variant-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_variants-heading-variant-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_variants-heading-variant-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_variants-heading-variant-1.png diff --git a/ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_variants-subheading-variant-1.png b/client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_variants-subheading-variant-1.png similarity index 100% rename from ui/shared/entities/clusters/__screenshots__/ClustersEntity.pw.tsx_default_variants-subheading-variant-1.png rename to client/features/name-services/clusters/components/__screenshots__/ClustersEntity.pw.tsx_default_variants-subheading-variant-1.png diff --git a/lib/clusters/useAddressClusters.spec.ts b/client/features/name-services/clusters/hooks/useAddressClusters.spec.ts similarity index 100% rename from lib/clusters/useAddressClusters.spec.ts rename to client/features/name-services/clusters/hooks/useAddressClusters.spec.ts diff --git a/lib/clusters/useAddressClusters.ts b/client/features/name-services/clusters/hooks/useAddressClusters.ts similarity index 100% rename from lib/clusters/useAddressClusters.ts rename to client/features/name-services/clusters/hooks/useAddressClusters.ts diff --git a/lib/clusters/useClusterPagination.spec.ts b/client/features/name-services/clusters/hooks/useClusterPagination.spec.ts similarity index 98% rename from lib/clusters/useClusterPagination.spec.ts rename to client/features/name-services/clusters/hooks/useClusterPagination.spec.ts index 3db1df3189..be1612c8fa 100644 --- a/lib/clusters/useClusterPagination.spec.ts +++ b/client/features/name-services/clusters/hooks/useClusterPagination.spec.ts @@ -9,7 +9,7 @@ import type { Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { renderHook, act } from 'vitest/lib'; -import { useClusterPagination } from '../clusters/useClusterPagination'; +import { useClusterPagination } from './useClusterPagination'; vi.mock('next/router', () => ({ useRouter: vi.fn(), diff --git a/lib/clusters/useClusterPagination.ts b/client/features/name-services/clusters/hooks/useClusterPagination.ts similarity index 100% rename from lib/clusters/useClusterPagination.ts rename to client/features/name-services/clusters/hooks/useClusterPagination.ts diff --git a/lib/clusters/useClusterSearch.spec.ts b/client/features/name-services/clusters/hooks/useClusterSearch.spec.ts similarity index 100% rename from lib/clusters/useClusterSearch.spec.ts rename to client/features/name-services/clusters/hooks/useClusterSearch.spec.ts diff --git a/lib/clusters/useClusterSearch.ts b/client/features/name-services/clusters/hooks/useClusterSearch.ts similarity index 100% rename from lib/clusters/useClusterSearch.ts rename to client/features/name-services/clusters/hooks/useClusterSearch.ts diff --git a/lib/clusters/useClustersData.spec.ts b/client/features/name-services/clusters/hooks/useClustersData.spec.ts similarity index 98% rename from lib/clusters/useClustersData.spec.ts rename to client/features/name-services/clusters/hooks/useClustersData.spec.ts index e790c72a7e..193367aa85 100644 --- a/lib/clusters/useClustersData.spec.ts +++ b/client/features/name-services/clusters/hooks/useClustersData.spec.ts @@ -4,7 +4,8 @@ import { renderHook } from '@testing-library/react'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { detectInputType } from 'lib/clusters/detectInputType'; +import { detectInputType } from 'client/features/name-services/clusters/utils/detect-input-type'; + import type { Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest'; @@ -15,7 +16,7 @@ const mockUseApiQuery = useApiQuery as Mock; type MockQueryResult = ReturnType; -vi.mock('lib/clusters/detectInputType', () => ({ +vi.mock('client/features/name-services/clusters/utils/detect-input-type', () => ({ detectInputType: vi.fn(), })); diff --git a/lib/clusters/useClustersData.ts b/client/features/name-services/clusters/hooks/useClustersData.ts similarity index 90% rename from lib/clusters/useClustersData.ts rename to client/features/name-services/clusters/hooks/useClustersData.ts index aac604ebcb..937a74ad57 100644 --- a/lib/clusters/useClustersData.ts +++ b/client/features/name-services/clusters/hooks/useClustersData.ts @@ -2,13 +2,13 @@ import React from 'react'; -import type { ClustersByAddressObject } from 'types/api/clusters'; -import { ClustersOrderBy } from 'types/api/clusters'; +import type { ClustersByAddressObject } from 'client/features/name-services/clusters/types/api'; +import { ClustersOrderBy } from 'client/features/name-services/clusters/types/api'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { detectInputType } from 'lib/clusters/detectInputType'; -import { CLUSTER_ITEM } from 'stubs/clusters'; +import { CLUSTER_ITEM } from 'client/features/name-services/clusters/stubs'; +import { detectInputType } from 'client/features/name-services/clusters/utils/detect-input-type'; export function useClustersData(debouncedSearchTerm: string, viewMode: string, page: number) { const ITEMS_PER_PAGE = 50; diff --git a/client/features/name-services/clusters/hooks/useSearchWithClusters.spec.tsx b/client/features/name-services/clusters/hooks/useSearchWithClusters.spec.ts similarity index 100% rename from client/features/name-services/clusters/hooks/useSearchWithClusters.spec.tsx rename to client/features/name-services/clusters/hooks/useSearchWithClusters.spec.ts diff --git a/client/features/name-services/clusters/hooks/useSearchWithClusters.tsx b/client/features/name-services/clusters/hooks/useSearchWithClusters.ts similarity index 100% rename from client/features/name-services/clusters/hooks/useSearchWithClusters.tsx rename to client/features/name-services/clusters/hooks/useSearchWithClusters.ts diff --git a/mocks/clusters/cluster.ts b/client/features/name-services/clusters/mocks/cluster.ts similarity index 97% rename from mocks/clusters/cluster.ts rename to client/features/name-services/clusters/mocks/cluster.ts index 0979b85023..287f9a4c20 100644 --- a/mocks/clusters/cluster.ts +++ b/client/features/name-services/clusters/mocks/cluster.ts @@ -1,4 +1,4 @@ -import type { ClusterByNameResponse, ClusterByIdResponse } from 'types/api/clusters'; +import type { ClusterByNameResponse, ClusterByIdResponse } from 'client/features/name-services/clusters/types/api'; export const campNetworkClusterByName: ClusterByNameResponse = { result: { diff --git a/mocks/clusters/directory.ts b/client/features/name-services/clusters/mocks/directory.ts similarity index 97% rename from mocks/clusters/directory.ts rename to client/features/name-services/clusters/mocks/directory.ts index 5e39b6dd36..f1ca7b0156 100644 --- a/mocks/clusters/directory.ts +++ b/client/features/name-services/clusters/mocks/directory.ts @@ -1,4 +1,4 @@ -import type { ClustersDirectoryResponse, ClustersDirectoryObject } from 'types/api/clusters'; +import type { ClustersDirectoryResponse, ClustersDirectoryObject } from 'client/features/name-services/clusters/types/api'; export const campNetworkCluster: ClustersDirectoryObject = { name: 'campnetwork/lol', diff --git a/mocks/clusters/leaderboard.ts b/client/features/name-services/clusters/mocks/leaderboard.ts similarity index 96% rename from mocks/clusters/leaderboard.ts rename to client/features/name-services/clusters/mocks/leaderboard.ts index 437b9ce499..0c4ca7262d 100644 --- a/mocks/clusters/leaderboard.ts +++ b/client/features/name-services/clusters/mocks/leaderboard.ts @@ -1,4 +1,4 @@ -import type { ClustersLeaderboardResponse, ClustersLeaderboardObject } from 'types/api/clusters'; +import type { ClustersLeaderboardResponse, ClustersLeaderboardObject } from 'client/features/name-services/clusters/types/api'; export const leaderboardFirst: ClustersLeaderboardObject = { name: 'ethereum/foundation', diff --git a/client/features/name-services/clusters/pages/address/AddressClusters.tsx b/client/features/name-services/clusters/pages/address/AddressClusters.tsx index 871bb9cc81..d0361a57be 100644 --- a/client/features/name-services/clusters/pages/address/AddressClusters.tsx +++ b/client/features/name-services/clusters/pages/address/AddressClusters.tsx @@ -4,12 +4,13 @@ import { Grid, chakra } from '@chakra-ui/react'; import type { UseQueryResult } from '@tanstack/react-query'; import React from 'react'; -import type { ClustersByAddressResponse } from 'types/api/clusters'; +import type { ClustersByAddressResponse } from 'client/features/name-services/clusters/types/api'; import { route } from 'nextjs-routes'; import type { ResourceError } from 'client/api/resources'; +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; import { filterOwnedClusters, getTotalRecordsDisplay, @@ -17,14 +18,14 @@ import { getClustersToShow, getGridRows, hasMoreClusters, -} from 'lib/clusters/clustersUtils'; +} from 'client/features/name-services/clusters/utils/clusters-utils'; + import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import IconSvg from 'ui/shared/IconSvg'; interface Props { diff --git a/ui/pages/Cluster.pw.tsx b/client/features/name-services/clusters/pages/details/Cluster.pw.tsx similarity index 97% rename from ui/pages/Cluster.pw.tsx rename to client/features/name-services/clusters/pages/details/Cluster.pw.tsx index 3d5fdf55f6..8afd0d1242 100644 --- a/ui/pages/Cluster.pw.tsx +++ b/client/features/name-services/clusters/pages/details/Cluster.pw.tsx @@ -1,7 +1,8 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import { campNetworkClusterByName, testnetClusterByName } from 'mocks/clusters/cluster'; +import { campNetworkClusterByName, testnetClusterByName } from 'client/features/name-services/clusters/mocks/cluster'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/ui/pages/Cluster.tsx b/client/features/name-services/clusters/pages/details/Cluster.tsx similarity index 91% rename from ui/pages/Cluster.tsx rename to client/features/name-services/clusters/pages/details/Cluster.tsx index 7131cf8ca0..52436dabdc 100644 --- a/ui/pages/Cluster.tsx +++ b/client/features/name-services/clusters/pages/details/Cluster.tsx @@ -5,9 +5,10 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import ClusterDetails from 'client/features/name-services/clusters/pages/details/ClusterDetails'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; -import ClusterDetails from 'ui/cluster/ClusterDetails'; import TextAd from 'ui/shared/ad/TextAd'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/cluster/ClusterDetails.tsx b/client/features/name-services/clusters/pages/details/ClusterDetails.tsx similarity index 94% rename from ui/cluster/ClusterDetails.tsx rename to client/features/name-services/clusters/pages/details/ClusterDetails.tsx index 897e328f1f..1b9984bd87 100644 --- a/ui/cluster/ClusterDetails.tsx +++ b/client/features/name-services/clusters/pages/details/ClusterDetails.tsx @@ -2,17 +2,18 @@ import React from 'react'; -import type { ClusterByNameResponse } from 'types/api/clusters'; +import type { ClusterByNameResponse } from 'client/features/name-services/clusters/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; + import { currencyUnits } from 'client/shared/chain/units'; import { Skeleton } from 'toolkit/chakra/skeleton'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; interface Props { diff --git a/ui/pages/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png b/client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png rename to client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png diff --git a/ui/pages/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png b/client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png rename to client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png diff --git a/ui/pages/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png b/client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png rename to client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png diff --git a/ui/pages/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png b/client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png rename to client/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png diff --git a/ui/nameServices/directories/Clusters.tsx b/client/features/name-services/clusters/pages/index/Clusters.tsx similarity index 90% rename from ui/nameServices/directories/Clusters.tsx rename to client/features/name-services/clusters/pages/index/Clusters.tsx index 2a4fd8b1a8..5b994a4d25 100644 --- a/ui/nameServices/directories/Clusters.tsx +++ b/client/features/name-services/clusters/pages/index/Clusters.tsx @@ -4,10 +4,10 @@ import { Box, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React, { useCallback } from 'react'; -import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { useQueryParams } from 'client/shared/router/useQueryParams'; - -import { detectInputType } from 'lib/clusters/detectInputType'; +import { useClusterPagination } from 'client/features/name-services/clusters/hooks/useClusterPagination'; +import { useClustersData } from 'client/features/name-services/clusters/hooks/useClustersData'; +import { useClusterSearch } from 'client/features/name-services/clusters/hooks/useClusterSearch'; +import { detectInputType } from 'client/features/name-services/clusters/utils/detect-input-type'; import { shouldShowDirectoryView, transformLeaderboardData, @@ -15,11 +15,12 @@ import { applyDirectoryPagination, calculateHasNextPage, getCurrentDataLength, -} from 'lib/clusters/pageUtils'; -import type { ViewMode } from 'lib/clusters/pageUtils'; -import { useClusterPagination } from 'lib/clusters/useClusterPagination'; -import { useClustersData } from 'lib/clusters/useClustersData'; -import { useClusterSearch } from 'lib/clusters/useClusterSearch'; +} from 'client/features/name-services/clusters/utils/page-utils'; +import type { ViewMode } from 'client/features/name-services/clusters/utils/page-utils'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; + import { Link } from 'toolkit/chakra/link'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/nameServices/directories/ClustersActionBar.tsx b/client/features/name-services/clusters/pages/index/ClustersActionBar.tsx similarity index 97% rename from ui/nameServices/directories/ClustersActionBar.tsx rename to client/features/name-services/clusters/pages/index/ClustersActionBar.tsx index 0c0945940f..c76a50e1d1 100644 --- a/ui/nameServices/directories/ClustersActionBar.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersActionBar.tsx @@ -5,12 +5,13 @@ import React from 'react'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; - import { getSearchPlaceholder, shouldShowActionBar, -} from 'lib/clusters/actionBarUtils'; +} from 'client/features/name-services/clusters/utils/action-bar-utils'; + +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import { Button, ButtonGroupRadio } from 'toolkit/chakra/button'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/ui/nameServices/directories/ClustersDirectoryListItem.tsx b/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx similarity index 92% rename from ui/nameServices/directories/ClustersDirectoryListItem.tsx rename to client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx index c020ab16e4..3855abd48c 100644 --- a/ui/nameServices/directories/ClustersDirectoryListItem.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx @@ -2,14 +2,15 @@ import React from 'react'; -import type { ClustersDirectoryObject } from 'types/api/clusters'; +import type { ClustersDirectoryObject } from 'client/features/name-services/clusters/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; + import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/ui/nameServices/directories/ClustersDirectoryTable.tsx b/client/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx similarity index 94% rename from ui/nameServices/directories/ClustersDirectoryTable.tsx rename to client/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx index 3c006a9582..f03351549b 100644 --- a/ui/nameServices/directories/ClustersDirectoryTable.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { ClustersDirectoryObject } from 'types/api/clusters'; +import type { ClustersDirectoryObject } from 'client/features/name-services/clusters/types/api'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; diff --git a/ui/nameServices/directories/ClustersDirectoryTableItem.tsx b/client/features/name-services/clusters/pages/index/ClustersDirectoryTableItem.tsx similarity index 90% rename from ui/nameServices/directories/ClustersDirectoryTableItem.tsx rename to client/features/name-services/clusters/pages/index/ClustersDirectoryTableItem.tsx index 4e40b3e707..c2e0e057a1 100644 --- a/ui/nameServices/directories/ClustersDirectoryTableItem.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersDirectoryTableItem.tsx @@ -2,14 +2,15 @@ import React from 'react'; -import type { ClustersDirectoryObject } from 'types/api/clusters'; +import type { ClustersDirectoryObject } from 'client/features/name-services/clusters/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; interface Props { diff --git a/ui/nameServices/directories/ClustersLeaderboardListItem.tsx b/client/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx similarity index 90% rename from ui/nameServices/directories/ClustersLeaderboardListItem.tsx rename to client/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx index aea7affbe3..4496681faa 100644 --- a/ui/nameServices/directories/ClustersLeaderboardListItem.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx @@ -2,10 +2,11 @@ import React from 'react'; -import type { ClustersLeaderboardObject } from 'types/api/clusters'; +import type { ClustersLeaderboardObject } from 'client/features/name-services/clusters/types/api'; + +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; interface Props { diff --git a/ui/nameServices/directories/ClustersLeaderboardTable.tsx b/client/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx similarity index 92% rename from ui/nameServices/directories/ClustersLeaderboardTable.tsx rename to client/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx index d4757b4941..0dc25c0967 100644 --- a/ui/nameServices/directories/ClustersLeaderboardTable.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { ClustersLeaderboardObject } from 'types/api/clusters'; +import type { ClustersLeaderboardObject } from 'client/features/name-services/clusters/types/api'; import { TableBody, TableHeaderSticky, TableRow, TableColumnHeader, TableRoot } from 'toolkit/chakra/table'; diff --git a/ui/nameServices/directories/ClustersLeaderboardTableItem.tsx b/client/features/name-services/clusters/pages/index/ClustersLeaderboardTableItem.tsx similarity index 86% rename from ui/nameServices/directories/ClustersLeaderboardTableItem.tsx rename to client/features/name-services/clusters/pages/index/ClustersLeaderboardTableItem.tsx index 6ec02d4290..552956dfe8 100644 --- a/ui/nameServices/directories/ClustersLeaderboardTableItem.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersLeaderboardTableItem.tsx @@ -2,11 +2,12 @@ import React from 'react'; -import type { ClustersLeaderboardObject } from 'types/api/clusters'; +import type { ClustersLeaderboardObject } from 'client/features/name-services/clusters/types/api'; + +import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; interface Props { item: ClustersLeaderboardObject; diff --git a/stubs/clusters.ts b/client/features/name-services/clusters/stubs.ts similarity index 77% rename from stubs/clusters.ts rename to client/features/name-services/clusters/stubs.ts index 99a896ba06..dcce6a4881 100644 --- a/stubs/clusters.ts +++ b/client/features/name-services/clusters/stubs.ts @@ -1,4 +1,4 @@ -import type { ClustersLeaderboardObject } from 'types/api/clusters'; +import type { ClustersLeaderboardObject } from 'client/features/name-services/clusters/types/api'; export const CLUSTER_ITEM: ClustersLeaderboardObject = { name: 'example.cluster', diff --git a/client/features/name-services/clusters/types/api.ts b/client/features/name-services/clusters/types/api.ts index 44193d5572..905c58d87c 100644 --- a/client/features/name-services/clusters/types/api.ts +++ b/client/features/name-services/clusters/types/api.ts @@ -19,3 +19,122 @@ export interface SearchResultCluster { is_testnet?: boolean; }; } +export interface ClustersByAddressObject { + name: string; + owner: string; + totalWeiAmount: string; + createdAt: string; + updatedAt: string; + updatedBy: string; + isTestnet: boolean; + clusterId: string; + expiresAt: string | null; +} + +export interface ClustersByAddressResponse { + result: { + data: Array; + }; +} + +export interface ClusterByNameResponse { + result: { + data: { + name: string; + owner: string; + clusterId: string; + backingWei: string; + expiresAt: string | null; + createdAt: string; + updatedAt: string; + updatedBy: string; + isTestnet: boolean; + }; + }; +} + +export interface ClusterByIdQueryParams { + id: string; +} + +export interface ClusterByIdResponse { + result: { + data: { + id: string; + createdBy: string; + createdAt: string; + wallets: Array<{ + address: string; + name: string; + chainIds: Array; + }>; + isTestnet: boolean; + }; + }; +} + +export interface ClustersLeaderboardObject { + name: string; + clusterId: string; + isTestnet: boolean; + totalWeiAmount: string; + totalReferralAmount: string; + chainIds: Array; + nameCount: string; + rank: number; +} + +export interface ClustersLeaderboardResponse { + result: { + data: Array; + }; +} + +export interface ClustersDirectoryObject { + name: string; + isTestnet: boolean; + createdAt: string; + owner: string; + totalWeiAmount: string; + updatedAt: string; + updatedBy: string; + chainIds: Array; +} + +export interface ClustersDirectoryResponse { + result: { + data: { + total: number; + items: Array; + }; + }; +} + +export interface ClustersByAddressQueryParams { + address: string; +} + +export interface ClusterByNameQueryParams { + name: string; +} + +export enum ClustersOrderBy { + RANK_ASC = 'rank-asc', + CREATED_AT_DESC = 'createdAt-desc', + NAME_ASC = 'name-asc', +} + +export interface ClustersLeaderboardQueryParams { + offset?: number; + limit?: number; + orderBy?: ClustersOrderBy | string; + query?: string | null; + isExact?: boolean; +} + +export interface ClustersDirectoryQueryParams { + offset?: number; + limit?: number; + orderBy?: ClustersOrderBy | string; + query?: string | null; +} diff --git a/lib/clusters/actionBarUtils.spec.ts b/client/features/name-services/clusters/utils/action-bar-utils.spec.ts similarity index 98% rename from lib/clusters/actionBarUtils.spec.ts rename to client/features/name-services/clusters/utils/action-bar-utils.spec.ts index ada1b3e666..1f2af37074 100644 --- a/lib/clusters/actionBarUtils.spec.ts +++ b/client/features/name-services/clusters/utils/action-bar-utils.spec.ts @@ -5,7 +5,7 @@ import { shouldDisableViewToggle, getSearchPlaceholder, shouldShowActionBar, -} from './actionBarUtils'; +} from './action-bar-utils'; describe('actionBarUtils', () => { describe('shouldShowClearButton', () => { diff --git a/lib/clusters/actionBarUtils.ts b/client/features/name-services/clusters/utils/action-bar-utils.ts similarity index 100% rename from lib/clusters/actionBarUtils.ts rename to client/features/name-services/clusters/utils/action-bar-utils.ts diff --git a/lib/clusters/clustersUtils.spec.ts b/client/features/name-services/clusters/utils/clusters-utils.spec.ts similarity index 99% rename from lib/clusters/clustersUtils.spec.ts rename to client/features/name-services/clusters/utils/clusters-utils.spec.ts index 5d5ae41b63..b3657fc179 100644 --- a/lib/clusters/clustersUtils.spec.ts +++ b/client/features/name-services/clusters/utils/clusters-utils.spec.ts @@ -8,7 +8,7 @@ import { getGridRows, hasMoreClusters, type ClusterData, -} from './clustersUtils'; +} from './clusters-utils'; describe('clustersUtils', () => { const mockClusters: Array = [ diff --git a/lib/clusters/clustersUtils.ts b/client/features/name-services/clusters/utils/clusters-utils.ts similarity index 90% rename from lib/clusters/clustersUtils.ts rename to client/features/name-services/clusters/utils/clusters-utils.ts index 276d3d43fa..04401df9e3 100644 --- a/lib/clusters/clustersUtils.ts +++ b/client/features/name-services/clusters/utils/clusters-utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ClustersByAddressResponse } from 'types/api/clusters'; +import type { ClustersByAddressResponse } from 'client/features/name-services/clusters/types/api'; export type ClusterData = ClustersByAddressResponse['result']['data'][0]; diff --git a/lib/clusters/detectInputType.spec.ts b/client/features/name-services/clusters/utils/detect-input-type.spec.ts similarity index 84% rename from lib/clusters/detectInputType.spec.ts rename to client/features/name-services/clusters/utils/detect-input-type.spec.ts index 9d854dbd27..b521c976a2 100644 --- a/lib/clusters/detectInputType.spec.ts +++ b/client/features/name-services/clusters/utils/detect-input-type.spec.ts @@ -1,6 +1,6 @@ import { it, expect } from 'vitest'; -import { detectInputType } from './detectInputType'; +import { detectInputType } from './detect-input-type'; it('should detect EVM address format', () => { expect(detectInputType('0x1234567890123456789012345678901234567890')).toBe('address'); diff --git a/lib/clusters/detectInputType.ts b/client/features/name-services/clusters/utils/detect-input-type.ts similarity index 100% rename from lib/clusters/detectInputType.ts rename to client/features/name-services/clusters/utils/detect-input-type.ts diff --git a/lib/clusters/pageUtils.spec.ts b/client/features/name-services/clusters/utils/page-utils.spec.ts similarity index 97% rename from lib/clusters/pageUtils.spec.ts rename to client/features/name-services/clusters/utils/page-utils.spec.ts index d79507a417..7b62e34e53 100644 --- a/lib/clusters/pageUtils.spec.ts +++ b/client/features/name-services/clusters/utils/page-utils.spec.ts @@ -1,5 +1,5 @@ -import { ClustersOrderBy } from 'types/api/clusters'; -import type { ClustersDirectoryObject } from 'types/api/clusters'; +import { ClustersOrderBy } from 'client/features/name-services/clusters/types/api'; +import type { ClustersDirectoryObject } from 'client/features/name-services/clusters/types/api'; import { describe, it, expect } from 'vitest'; @@ -14,7 +14,7 @@ import { isValidViewMode, getDefaultViewMode, getCurrentDataLength, -} from './pageUtils'; +} from './page-utils'; describe('pageUtils', () => { describe('getViewModeOrderBy', () => { diff --git a/lib/clusters/pageUtils.ts b/client/features/name-services/clusters/utils/page-utils.ts similarity index 96% rename from lib/clusters/pageUtils.ts rename to client/features/name-services/clusters/utils/page-utils.ts index a898cdf84b..3bbcd7719f 100644 --- a/lib/clusters/pageUtils.ts +++ b/client/features/name-services/clusters/utils/page-utils.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { ClustersOrderBy } from 'types/api/clusters'; -import type { ClustersLeaderboardObject, ClustersDirectoryObject, ClustersByAddressObject } from 'types/api/clusters'; +import { ClustersOrderBy } from 'client/features/name-services/clusters/types/api'; +import type { ClustersLeaderboardObject, ClustersDirectoryObject, ClustersByAddressObject } from 'client/features/name-services/clusters/types/api'; export type ViewMode = 'leaderboard' | 'directory'; export type InputType = 'address' | 'cluster_name'; diff --git a/ui/pages/NameServices.pw.tsx b/client/features/name-services/common/pages/NameServices.pw.tsx similarity index 94% rename from ui/pages/NameServices.pw.tsx rename to client/features/name-services/common/pages/NameServices.pw.tsx index c91a5c1da1..c405110d0d 100644 --- a/ui/pages/NameServices.pw.tsx +++ b/client/features/name-services/common/pages/NameServices.pw.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import { clustersDirectoryMock } from 'mocks/clusters/directory'; -import { clustersLeaderboardMock } from 'mocks/clusters/leaderboard'; -import * as ensDomainMock from 'mocks/ens/domain'; +import { clustersDirectoryMock } from 'client/features/name-services/clusters/mocks/directory'; +import { clustersLeaderboardMock } from 'client/features/name-services/clusters/mocks/leaderboard'; +import * as ensDomainMock from 'client/features/name-services/domains/mocks/domain'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect, devices } from 'playwright/lib'; diff --git a/ui/pages/NameServices.tsx b/client/features/name-services/common/pages/NameServices.tsx similarity index 84% rename from ui/pages/NameServices.tsx rename to client/features/name-services/common/pages/NameServices.tsx index 9a177870b9..9be71155a0 100644 --- a/ui/pages/NameServices.tsx +++ b/client/features/name-services/common/pages/NameServices.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import Clusters from 'client/features/name-services/clusters/pages/index/Clusters'; +import NameDomains from 'client/features/name-services/domains/pages/index/NameDomains'; + import config from 'configs/app'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import Clusters from 'ui/nameServices/directories/Clusters'; -import NameDomains from 'ui/nameServices/domains/NameDomains'; import PageTitle from 'ui/shared/Page/PageTitle'; const feature = config.features.nameServices; diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png diff --git a/ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png b/client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png similarity index 100% rename from ui/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png rename to client/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png diff --git a/ui/shared/entities/ens/EnsEntity.pw.tsx b/client/features/name-services/domains/components/EnsEntity.pw.tsx similarity index 95% rename from ui/shared/entities/ens/EnsEntity.pw.tsx rename to client/features/name-services/domains/components/EnsEntity.pw.tsx index 531a3be352..f01d64847f 100644 --- a/ui/shared/entities/ens/EnsEntity.pw.tsx +++ b/client/features/name-services/domains/components/EnsEntity.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as domainMock from 'mocks/ens/domain'; +import * as domainMock from 'client/features/name-services/domains/mocks/domain'; + import { stableHover } from 'playwright/helpers/stableHover'; import { test, expect } from 'playwright/lib'; diff --git a/ui/shared/entities/ens/EnsEntity.tsx b/client/features/name-services/domains/components/EnsEntity.tsx similarity index 98% rename from ui/shared/entities/ens/EnsEntity.tsx rename to client/features/name-services/domains/components/EnsEntity.tsx index 93afc48fcb..28d4986b07 100644 --- a/ui/shared/entities/ens/EnsEntity.tsx +++ b/client/features/name-services/domains/components/EnsEntity.tsx @@ -12,10 +12,9 @@ import { Link as LinkToolkit } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps, getIconProps } from 'ui/shared/entities/base/utils'; import IconSvg from 'ui/shared/IconSvg'; -import { distributeEntityProps, getIconProps } from '../base/utils'; - type LinkProps = EntityBase.LinkBaseProps & Pick; const Link = chakra((props: LinkProps) => { diff --git a/ui/nameDomain/NameDomainExpiryStatus.tsx b/client/features/name-services/domains/components/NameDomainExpiryStatus.tsx similarity index 100% rename from ui/nameDomain/NameDomainExpiryStatus.tsx rename to client/features/name-services/domains/components/NameDomainExpiryStatus.tsx diff --git a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx index 6e37879d73..6d3baf1008 100644 --- a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx +++ b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx @@ -9,10 +9,11 @@ import type * as multichain from 'types/client/multichainAggregator'; import { toBech32Address } from 'client/slices/address/utils/bech32'; +import * as EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import highlightText from 'client/shared/text/highlight-text'; import dayjs from 'lib/date/dayjs'; -import * as EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_customization-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_customization-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_customization-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_customization-1.png diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_tooltip-test-with-protocol-info-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_tooltip-test-with-protocol-info-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_tooltip-test-with-protocol-info-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_tooltip-test-with-protocol-info-1.png diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_variant-content-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_variant-content-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_variant-content-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_variant-content-1.png diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_variant-subheading-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_variant-subheading-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_variant-subheading-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_variant-subheading-1.png diff --git a/ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_with-long-name-1.png b/client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_with-long-name-1.png similarity index 100% rename from ui/shared/entities/ens/__screenshots__/EnsEntity.pw.tsx_default_with-long-name-1.png rename to client/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_with-long-name-1.png diff --git a/mocks/ens/domain.ts b/client/features/name-services/domains/mocks/domain.ts similarity index 100% rename from mocks/ens/domain.ts rename to client/features/name-services/domains/mocks/domain.ts diff --git a/mocks/ens/events.ts b/client/features/name-services/domains/mocks/events.ts similarity index 100% rename from mocks/ens/events.ts rename to client/features/name-services/domains/mocks/events.ts diff --git a/client/features/name-services/domains/stubs/search.ts b/client/features/name-services/domains/mocks/search.ts similarity index 100% rename from client/features/name-services/domains/stubs/search.ts rename to client/features/name-services/domains/mocks/search.ts diff --git a/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx b/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx index a19e9d2fbc..5ab2cd5dc3 100644 --- a/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx +++ b/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx @@ -5,7 +5,8 @@ import type * as bens from '@blockscout/bens-types'; import type { ResourceError } from 'client/api/resources'; -import * as ensDomainMock from 'mocks/ens/domain'; +import * as ensDomainMock from 'client/features/name-services/domains/mocks/domain'; + import { test, expect } from 'playwright/lib'; import AddressEnsDomains from './AddressEnsDomains'; diff --git a/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx index ce745f8f78..215fa17af7 100644 --- a/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx +++ b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx @@ -11,6 +11,8 @@ import { route } from 'nextjs-routes'; import type { ResourceError } from 'client/api/resources'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; @@ -18,7 +20,6 @@ import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolki import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import IconSvg from 'ui/shared/IconSvg'; interface Props { diff --git a/ui/pages/NameDomain.pw.tsx b/client/features/name-services/domains/pages/details/NameDomain.pw.tsx similarity index 90% rename from ui/pages/NameDomain.pw.tsx rename to client/features/name-services/domains/pages/details/NameDomain.pw.tsx index bfb60e8ebc..7ad2d33229 100644 --- a/ui/pages/NameDomain.pw.tsx +++ b/client/features/name-services/domains/pages/details/NameDomain.pw.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import * as ensDomainMock from 'mocks/ens/domain'; -import * as ensDomainEventsMock from 'mocks/ens/events'; +import * as ensDomainMock from 'client/features/name-services/domains/mocks/domain'; +import * as ensDomainEventsMock from 'client/features/name-services/domains/mocks/events'; + import { test, expect } from 'playwright/lib'; import NameDomain from './NameDomain'; diff --git a/ui/pages/NameDomain.tsx b/client/features/name-services/domains/pages/details/NameDomain.tsx similarity index 89% rename from ui/pages/NameDomain.tsx rename to client/features/name-services/domains/pages/details/NameDomain.tsx index f9b071fdab..14894ce81c 100644 --- a/ui/pages/NameDomain.tsx +++ b/client/features/name-services/domains/pages/details/NameDomain.tsx @@ -12,18 +12,19 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; +import NameDomainHistory from 'client/features/name-services/domains/pages/details/history/NameDomainHistory'; +import NameDomainDetails from 'client/features/name-services/domains/pages/details/info/NameDomainDetails'; +import { ENS_DOMAIN } from 'client/features/name-services/domains/stubs'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { ENS_DOMAIN } from 'stubs/ENS'; import { Link } from 'toolkit/chakra/link'; import { Tooltip } from 'toolkit/chakra/tooltip'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import NameDomainDetails from 'ui/nameDomain/NameDomainDetails'; -import NameDomainHistory from 'ui/nameDomain/NameDomainHistory'; import TextAd from 'ui/shared/ad/TextAd'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import IconSvg from 'ui/shared/IconSvg'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/pages/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png b/client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png similarity index 100% rename from ui/pages/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png rename to client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png diff --git a/ui/pages/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png b/client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png rename to client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png diff --git a/ui/pages/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png b/client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png rename to client/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png diff --git a/ui/nameDomain/NameDomainHistory.tsx b/client/features/name-services/domains/pages/details/history/NameDomainHistory.tsx similarity index 88% rename from ui/nameDomain/NameDomainHistory.tsx rename to client/features/name-services/domains/pages/details/history/NameDomainHistory.tsx index 224ac8e8f7..a561c483c0 100644 --- a/ui/nameDomain/NameDomainHistory.tsx +++ b/client/features/name-services/domains/pages/details/history/NameDomainHistory.tsx @@ -8,15 +8,16 @@ import type * as bens from '@blockscout/bens-types'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { ENS_DOMAIN_EVENT } from 'client/features/name-services/domains/stubs'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { ENS_DOMAIN_EVENT } from 'stubs/ENS'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import NameDomainHistoryListItem from './history/NameDomainHistoryListItem'; -import NameDomainHistoryTable from './history/NameDomainHistoryTable'; -import { getNextSortValue, type Sort, type SortField } from './history/utils'; +import NameDomainHistoryListItem from './NameDomainHistoryListItem'; +import NameDomainHistoryTable from './NameDomainHistoryTable'; +import { getNextSortValue, type Sort, type SortField } from './utils'; const feature = config.features.nameServices; const availableProtocols = feature.isEnabled && feature.ens.isEnabled ? feature.ens.protocols : []; diff --git a/ui/nameDomain/history/NameDomainHistoryListItem.tsx b/client/features/name-services/domains/pages/details/history/NameDomainHistoryListItem.tsx similarity index 100% rename from ui/nameDomain/history/NameDomainHistoryListItem.tsx rename to client/features/name-services/domains/pages/details/history/NameDomainHistoryListItem.tsx diff --git a/ui/nameDomain/history/NameDomainHistoryTable.tsx b/client/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx similarity index 100% rename from ui/nameDomain/history/NameDomainHistoryTable.tsx rename to client/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx diff --git a/ui/nameDomain/history/NameDomainHistoryTableItem.tsx b/client/features/name-services/domains/pages/details/history/NameDomainHistoryTableItem.tsx similarity index 100% rename from ui/nameDomain/history/NameDomainHistoryTableItem.tsx rename to client/features/name-services/domains/pages/details/history/NameDomainHistoryTableItem.tsx diff --git a/ui/nameDomain/history/utils.ts b/client/features/name-services/domains/pages/details/history/utils.ts similarity index 100% rename from ui/nameDomain/history/utils.ts rename to client/features/name-services/domains/pages/details/history/utils.ts diff --git a/ui/nameDomain/NameDomainDetails.tsx b/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx similarity index 98% rename from ui/nameDomain/NameDomainDetails.tsx rename to client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx index 3ae67c416d..daf14142f4 100644 --- a/ui/nameDomain/NameDomainDetails.tsx +++ b/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx @@ -12,6 +12,8 @@ import type { ResourceError } from 'client/api/resources'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import NameDomainExpiryStatus from 'client/features/name-services/domains/components/NameDomainExpiryStatus'; + import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; @@ -23,8 +25,7 @@ import NftEntity from 'ui/shared/entities/nft/NftEntity'; import IconSvg from 'ui/shared/IconSvg'; import TextSeparator from 'ui/shared/TextSeparator'; -import NameDomainDetailsAlert from './details/NameDomainDetailsAlert'; -import NameDomainExpiryStatus from './NameDomainExpiryStatus'; +import NameDomainDetailsAlert from './NameDomainDetailsAlert'; interface Props { query: UseQueryResult>; diff --git a/ui/nameDomain/details/NameDomainDetailsAlert.tsx b/client/features/name-services/domains/pages/details/info/NameDomainDetailsAlert.tsx similarity index 100% rename from ui/nameDomain/details/NameDomainDetailsAlert.tsx rename to client/features/name-services/domains/pages/details/info/NameDomainDetailsAlert.tsx diff --git a/ui/nameServices/domains/NameDomains.tsx b/client/features/name-services/domains/pages/index/NameDomains.tsx similarity index 98% rename from ui/nameServices/domains/NameDomains.tsx rename to client/features/name-services/domains/pages/index/NameDomains.tsx index ebbe65c765..5e5eda9848 100644 --- a/ui/nameServices/domains/NameDomains.tsx +++ b/client/features/name-services/domains/pages/index/NameDomains.tsx @@ -4,15 +4,16 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { EnsDomainLookupFiltersOptions, EnsLookupSorting } from 'types/api/ens'; +import type { EnsDomainLookupFiltersOptions, EnsLookupSorting } from 'client/features/name-services/domains/types/api'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { ENS_DOMAIN } from 'client/features/name-services/domains/stubs'; + import useDebounce from 'client/shared/hooks/useDebounce'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { ENS_DOMAIN } from 'stubs/ENS'; import { generateListStub } from 'stubs/utils'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/nameServices/domains/NameDomainsActionBar.tsx b/client/features/name-services/domains/pages/index/NameDomainsActionBar.tsx similarity index 98% rename from ui/nameServices/domains/NameDomainsActionBar.tsx rename to client/features/name-services/domains/pages/index/NameDomainsActionBar.tsx index 0b19f47949..9b3338a84d 100644 --- a/ui/nameServices/domains/NameDomainsActionBar.tsx +++ b/client/features/name-services/domains/pages/index/NameDomainsActionBar.tsx @@ -4,7 +4,7 @@ import { Box, Fieldset, Flex, HStack, Text, chakra, createListCollection } from import React from 'react'; import type * as bens from '@blockscout/bens-types'; -import type { EnsDomainLookupFiltersOptions } from 'types/api/ens'; +import type { EnsDomainLookupFiltersOptions } from 'client/features/name-services/domains/types/api'; import type { PaginationParams } from 'ui/shared/pagination/types'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; diff --git a/ui/nameServices/domains/NameDomainsListItem.tsx b/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx similarity index 92% rename from ui/nameServices/domains/NameDomainsListItem.tsx rename to client/features/name-services/domains/pages/index/NameDomainsListItem.tsx index 6a10626a0f..5b3a5050d8 100644 --- a/ui/nameServices/domains/NameDomainsListItem.tsx +++ b/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx @@ -6,10 +6,11 @@ import type * as bens from '@blockscout/bens-types'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; +import NameDomainExpiryStatus from 'client/features/name-services/domains/components/NameDomainExpiryStatus'; + import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/ui/nameServices/domains/NameDomainsTable.tsx b/client/features/name-services/domains/pages/index/NameDomainsTable.tsx similarity index 100% rename from ui/nameServices/domains/NameDomainsTable.tsx rename to client/features/name-services/domains/pages/index/NameDomainsTable.tsx diff --git a/ui/nameServices/domains/NameDomainsTableItem.tsx b/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx similarity index 94% rename from ui/nameServices/domains/NameDomainsTableItem.tsx rename to client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx index a247210bbb..9a3bdaa46b 100644 --- a/ui/nameServices/domains/NameDomainsTableItem.tsx +++ b/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx @@ -6,9 +6,10 @@ import type * as bens from '@blockscout/bens-types'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import dayjs from 'lib/date/dayjs'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; type Props = bens.Domain & { diff --git a/ui/nameServices/domains/utils.ts b/client/features/name-services/domains/pages/index/utils.ts similarity index 90% rename from ui/nameServices/domains/utils.ts rename to client/features/name-services/domains/pages/index/utils.ts index 5798eea35d..812da8a4dd 100644 --- a/ui/nameServices/domains/utils.ts +++ b/client/features/name-services/domains/pages/index/utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { EnsLookupSorting } from 'types/api/ens'; +import type { EnsLookupSorting } from 'client/features/name-services/domains/types/api'; import type { SelectOption } from 'toolkit/chakra/select'; import getNextSortValueShared from 'ui/shared/sort/getNextSortValue'; diff --git a/stubs/ENS.ts b/client/features/name-services/domains/stubs.ts similarity index 100% rename from stubs/ENS.ts rename to client/features/name-services/domains/stubs.ts diff --git a/client/features/name-services/domains/types/api.ts b/client/features/name-services/domains/types/api.ts index a3f39db885..0a669b3aaa 100644 --- a/client/features/name-services/domains/types/api.ts +++ b/client/features/name-services/domains/types/api.ts @@ -19,3 +19,17 @@ export interface SearchResultDomain { protocol?: bens.ProtocolInfo; }; } +export interface EnsAddressLookupFilters extends Pick { + protocols: Array | undefined; +} + +export interface EnsDomainLookupFilters extends Pick { + protocols: Array | undefined; +} + +export interface EnsLookupSorting { + sort: 'registration_date'; + order: Exclude; +} + +export type EnsDomainLookupFiltersOptions = Array<'resolved_to' | 'owned_by' | 'with_inactive'>; diff --git a/client/features/tx-interpretation/common/components/TxInterpretation.tsx b/client/features/tx-interpretation/common/components/TxInterpretation.tsx index dd0c114cab..1dfdef9c07 100644 --- a/client/features/tx-interpretation/common/components/TxInterpretation.tsx +++ b/client/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -18,6 +18,8 @@ import { route } from 'nextjs-routes'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import { currencyUnits } from 'client/shared/chain/units'; @@ -30,7 +32,6 @@ import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { SECOND } from 'toolkit/utils/consts'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/slices/address/pages/details/Address.tsx b/client/slices/address/pages/details/Address.tsx index 7778173870..c2b376fff5 100644 --- a/client/slices/address/pages/details/Address.tsx +++ b/client/slices/address/pages/details/Address.tsx @@ -41,7 +41,9 @@ import AddressDeposits from 'client/features/chain-variants/beacon-chain/pages/a import AddressEpochRewards from 'client/features/chain-variants/celo/pages/address/AddressEpochRewards'; import AddressMud from 'client/features/chain-variants/mud/pages/address/AddressMud'; import AddressMultichainInfoButton from 'client/features/multichain-button/pages/address/AddressMultichainInfoButton'; +import { useAddressClusters } from 'client/features/name-services/clusters/hooks/useAddressClusters'; import AddressClusters from 'client/features/name-services/clusters/pages/address/AddressClusters'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import useCheckDomainNameParam from 'client/features/name-services/domains/hooks/useCheckDomainNameParam'; import AddressEnsDomains from 'client/features/name-services/domains/pages/address/AddressEnsDomains'; import SolidityscanReport from 'client/features/solidity-scan/components/SolidityscanReport'; @@ -55,13 +57,11 @@ import getQueryParamString from 'client/shared/router/get-query-param-string'; import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; import config from 'configs/app'; -import { useAddressClusters } from 'lib/clusters/useAddressClusters'; import useIsSafeAddress from 'lib/hooks/useIsSafeAddress'; import useFetchXStarScore from 'lib/xStarScore/useFetchXStarScore'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import TextAd from 'ui/shared/ad/TextAd'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; import formatUserTags from 'ui/shared/EntityTags/formatUserTags'; import sortEntityTags from 'ui/shared/EntityTags/sortEntityTags'; diff --git a/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx b/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx index 92b788f9ee..e14628cae4 100644 --- a/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx +++ b/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx @@ -5,7 +5,7 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; -import { domain1 } from 'client/features/name-services/domains/stubs/search'; +import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; import { apps as appsMock } from 'mocks/apps/apps'; diff --git a/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx b/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx index d1640d34ab..48302fb800 100644 --- a/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx +++ b/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx @@ -6,7 +6,7 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; -import { domain1 } from 'client/features/name-services/domains/stubs/search'; +import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; import { apps as appsMock } from 'mocks/apps/apps'; diff --git a/client/slices/search/mocks.ts b/client/slices/search/mocks.ts index 1b3dad474e..2159d31c19 100644 --- a/client/slices/search/mocks.ts +++ b/client/slices/search/mocks.ts @@ -10,7 +10,7 @@ import type { import { metatag1 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; -import { domain1 } from 'client/features/name-services/domains/stubs/search'; +import { domain1 } from 'client/features/name-services/domains/mocks/search'; export const token1: SearchResultToken = { address_hash: '0x377c5F2B300B25a534d4639177873b7fEAA56d4B', diff --git a/client/slices/search/pages/search-results/SearchResultListItem.tsx b/client/slices/search/pages/search-results/SearchResultListItem.tsx index 3a0f046a40..872dc6afef 100644 --- a/client/slices/search/pages/search-results/SearchResultListItem.tsx +++ b/client/slices/search/pages/search-results/SearchResultListItem.tsx @@ -22,6 +22,7 @@ import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; import * as TacOperationEntity from 'client/features/chain-variants/tac/components/TacOperationEntity'; import TacOperationStatus from 'client/features/chain-variants/tac/components/TacOperationStatus'; import * as BlobEntity from 'client/features/data-availability/components/entity/BlobEntity'; +import * as EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import * as UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import * as mixpanel from 'client/shared/analytics/mixpanel'; @@ -35,7 +36,6 @@ import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; import { SECOND } from 'toolkit/utils/consts'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; -import * as EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; diff --git a/client/slices/search/pages/search-results/SearchResultTableItem.tsx b/client/slices/search/pages/search-results/SearchResultTableItem.tsx index 39a639937b..a64dcc552f 100644 --- a/client/slices/search/pages/search-results/SearchResultTableItem.tsx +++ b/client/slices/search/pages/search-results/SearchResultTableItem.tsx @@ -22,6 +22,7 @@ import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; import * as TacOperationEntity from 'client/features/chain-variants/tac/components/TacOperationEntity'; import TacOperationStatus from 'client/features/chain-variants/tac/components/TacOperationStatus'; import * as BlobEntity from 'client/features/data-availability/components/entity/BlobEntity'; +import * as EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import * as UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import * as mixpanel from 'client/shared/analytics/mixpanel'; @@ -36,7 +37,6 @@ import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; import { SECOND } from 'toolkit/utils/consts'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; -import * as EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; import Time from 'ui/shared/time/Time'; diff --git a/client/slices/search/pages/search-results/SearchResults.pw.tsx b/client/slices/search/pages/search-results/SearchResults.pw.tsx index 28177dc4f2..c499ab66f5 100644 --- a/client/slices/search/pages/search-results/SearchResults.pw.tsx +++ b/client/slices/search/pages/search-results/SearchResults.pw.tsx @@ -6,7 +6,7 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; -import { domain1 } from 'client/features/name-services/domains/stubs/search'; +import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; import { apps as appsMock } from 'mocks/apps/apps'; diff --git a/pages/name-services/clusters/[name].tsx b/pages/name-services/clusters/[name].tsx index 8c6489aa84..546706e72b 100644 --- a/pages/name-services/clusters/[name].tsx +++ b/pages/name-services/clusters/[name].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const Cluster = dynamic(() => import('ui/pages/Cluster'), { ssr: false }); +const Cluster = dynamic(() => import('client/features/name-services/clusters/pages/details/Cluster'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/name-services/domains/[name].tsx b/pages/name-services/domains/[name].tsx index 62b2baa179..eec9177f9b 100644 --- a/pages/name-services/domains/[name].tsx +++ b/pages/name-services/domains/[name].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const NameDomain = dynamic(() => import('ui/pages/NameDomain'), { ssr: false }); +const NameDomain = dynamic(() => import('client/features/name-services/domains/pages/details/NameDomain'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/name-services/index.tsx b/pages/name-services/index.tsx index 76011f79d5..d475a77187 100644 --- a/pages/name-services/index.tsx +++ b/pages/name-services/index.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const NameServices = dynamic(() => import('ui/pages/NameServices'), { ssr: false }); +const NameServices = dynamic(() => import('client/features/name-services/common/pages/NameServices'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/types/api/clusters.ts b/types/api/clusters.ts deleted file mode 100644 index 9b5fb1c8a2..0000000000 --- a/types/api/clusters.ts +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -export interface ClustersByAddressObject { - name: string; - owner: string; - totalWeiAmount: string; - createdAt: string; - updatedAt: string; - updatedBy: string; - isTestnet: boolean; - clusterId: string; - expiresAt: string | null; -} - -export interface ClustersByAddressResponse { - result: { - data: Array; - }; -} - -export interface ClusterByNameResponse { - result: { - data: { - name: string; - owner: string; - clusterId: string; - backingWei: string; - expiresAt: string | null; - createdAt: string; - updatedAt: string; - updatedBy: string; - isTestnet: boolean; - }; - }; -} - -export interface ClusterByIdQueryParams { - id: string; -} - -export interface ClusterByIdResponse { - result: { - data: { - id: string; - createdBy: string; - createdAt: string; - wallets: Array<{ - address: string; - name: string; - chainIds: Array; - }>; - isTestnet: boolean; - }; - }; -} - -export interface ClustersLeaderboardObject { - name: string; - clusterId: string; - isTestnet: boolean; - totalWeiAmount: string; - totalReferralAmount: string; - chainIds: Array; - nameCount: string; - rank: number; -} - -export interface ClustersLeaderboardResponse { - result: { - data: Array; - }; -} - -export interface ClustersDirectoryObject { - name: string; - isTestnet: boolean; - createdAt: string; - owner: string; - totalWeiAmount: string; - updatedAt: string; - updatedBy: string; - chainIds: Array; -} - -export interface ClustersDirectoryResponse { - result: { - data: { - total: number; - items: Array; - }; - }; -} - -export interface ClustersByAddressQueryParams { - address: string; -} - -export interface ClusterByNameQueryParams { - name: string; -} - -export enum ClustersOrderBy { - RANK_ASC = 'rank-asc', - CREATED_AT_DESC = 'createdAt-desc', - NAME_ASC = 'name-asc', -} - -export interface ClustersLeaderboardQueryParams { - offset?: number; - limit?: number; - orderBy?: ClustersOrderBy | string; - query?: string | null; - isExact?: boolean; -} - -export interface ClustersDirectoryQueryParams { - offset?: number; - limit?: number; - orderBy?: ClustersOrderBy | string; - query?: string | null; -} diff --git a/types/api/ens.ts b/types/api/ens.ts deleted file mode 100644 index 5552b5cc20..0000000000 --- a/types/api/ens.ts +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type * as bens from '@blockscout/bens-types'; - -export interface EnsAddressLookupFilters extends Pick { - protocols: Array | undefined; -} - -export interface EnsDomainLookupFilters extends Pick { - protocols: Array | undefined; -} - -export interface EnsLookupSorting { - sort: 'registration_date'; - order: Exclude; -} - -export type EnsDomainLookupFiltersOptions = Array<'resolved_to' | 'owned_by' | 'with_inactive'>; diff --git a/ui/multichain/address/MultichainAddress.tsx b/ui/multichain/address/MultichainAddress.tsx index f7f1616a18..8eea9cdd29 100644 --- a/ui/multichain/address/MultichainAddress.tsx +++ b/ui/multichain/address/MultichainAddress.tsx @@ -13,6 +13,8 @@ import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCod import getCheckedSummedAddress from 'client/slices/address/utils/get-checked-summed-address'; import { CONTRACT_TAB_IDS } from 'client/slices/contract/utils/tabs'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; @@ -22,7 +24,6 @@ import { ADDRESS } from 'stubs/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import ClusterChainsPopover from 'ui/multichain/components/ClusterChainsPopover'; import TextAd from 'ui/shared/ad/TextAd'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import PageTitle from 'ui/shared/Page/PageTitle'; import MultichainAddressEnsDomains from './header/MultichainAddressEnsDomains'; diff --git a/ui/multichain/address/header/MultichainAddressEnsDomains.tsx b/ui/multichain/address/header/MultichainAddressEnsDomains.tsx index 7227a448ee..0b71bb691d 100644 --- a/ui/multichain/address/header/MultichainAddressEnsDomains.tsx +++ b/ui/multichain/address/header/MultichainAddressEnsDomains.tsx @@ -6,12 +6,13 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; + import { Button } from 'toolkit/chakra/button'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import IconSvg from 'ui/shared/IconSvg'; import useLazyLoadedList from 'ui/shared/pagination/useLazyLoadedList'; diff --git a/ui/showcases/Link.tsx b/ui/showcases/Link.tsx index 81babb505a..49d2fae390 100644 --- a/ui/showcases/Link.tsx +++ b/ui/showcases/Link.tsx @@ -16,11 +16,11 @@ import * as txMock from 'client/slices/tx/mocks/tx'; import BlobEntity from 'client/features/data-availability/components/entity/BlobEntity'; import * as blobsMock from 'client/features/data-availability/mocks/blobs'; +import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; +import * as ensMock from 'client/features/name-services/domains/mocks/domain'; -import * as ensMock from 'mocks/ens/domain'; import * as poolMock from 'mocks/pools/pool'; import { Link, LinkBox, LinkOverlay } from 'toolkit/chakra/link'; -import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity'; import PoolEntity from 'ui/shared/entities/pool/PoolEntity'; From 3d555ec8f17aaf391ea44b65365ab7f89aff1d79 Mon Sep 17 00:00:00 2001 From: tom Date: Tue, 19 May 2026 20:28:55 +0200 Subject: [PATCH 04/85] =?UTF-8?q?track:=206-16=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3463?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index d3b731d71a..2bc20b9fae 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -244,23 +244,23 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-14 · [ ] Feature: `metasuites` **Scope:** Includes `lib/hooks/useNotifyOnNavigation` → `client/features/metasuites/` -### 6-15 · [ ] Feature: `csv-export` +### 6-15 · [x] Feature: `csv-export` -### 6-16 · [ ] Feature: `pools` +### 6-16 · [~] Feature: `pools` · [#3463](https://github.com/blockscout/frontend/issues/3463) ### 6-17 · [ ] Feature: `hot-contracts` ### 6-18 · [ ] Feature: `interchain-indexer` **Scope:** Cross-chain message indexer (not ZetaChain). See `configs/app/features/crossChainTxs.ts`. -### 6-19 · [ ] Feature: `mud-framework` +### 6-19 · [x] Feature: `mud-framework` ### 6-20 · [ ] Feature: `visualize` **Scope:** Solidity-to-UML diagrams. See `configs/app/features/sol2uml.ts`. ### 6-21 · [ ] Feature: `tx-interpretation` -### 6-22 · [ ] Feature: `public-tags` +### 6-22 · [ ] Feature: `public-tags` (merge with `address-metadata`) **Scope:** Community address labels. See `configs/app/features/publicTagsSubmission.ts`. ### 6-23 · [ ] Feature: `address-widgets` @@ -268,7 +268,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-24 · [ ] Feature: `address-metadata` -### 6-25 · [ ] Feature: `address-verification` +### 6-25 · [x] Feature: `address-verification` **Scope:** Covers `addressProfileAPI.ts` and `addressVerification.ts` configs. ### 6-26 · [ ] Feature: `bridged-tokens` From 644f2cde04d0f2a318b321680a5f884b4bddd802 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 09:10:33 +0200 Subject: [PATCH 05/85] [Migration 6-10] Feature: rewards (#3464) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Migration 6-10] Feature: rewards — move to client/features/rewards/ Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/65be2ab2-d127-4211-aacb-0cdd3692d7b1 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- .../components/auth-modal/AuthModal.tsx | 2 +- client/features/account/hooks/useLogout.ts | 3 ++- .../rewards/components}/MeritsIcon.tsx | 0 .../rewards/components}/NavLinkRewards.tsx | 6 +++--- .../components}/RewardsActivityTracker.tsx | 2 +- .../rewards/components}/RewardsButton.tsx | 3 ++- .../RewardsReadOnlyInputWithCopy.tsx | 0 .../components}/login/RewardsLoginModal.tsx | 2 +- .../login/steps/CongratsStepContent.tsx | 3 ++- .../login/steps/LoginStepContent.tsx | 2 +- .../features/rewards/contexts.tsx | 0 .../rewards/hooks/useRewardsActivity.ts | 2 +- .../rewards/hooks/useStreakBadges.ts | 3 ++- .../features/rewards/mocks}/activity.ts | 0 .../features/rewards/mocks}/balance.ts | 0 .../features/rewards/mocks/daily-reward.ts | 0 .../features/rewards/mocks}/referrals.ts | 0 .../features/rewards/mocks/rewards-config.ts | 0 .../features/rewards/mocks/streak-badges.ts | 0 .../dashboard/DailyRewardClaimButton.tsx | 3 ++- .../dashboard/RewardsActivityPassCard.tsx | 3 ++- .../pages/dashboard}/RewardsDashboard.pw.tsx | 10 ++++----- .../pages/dashboard}/RewardsDashboard.tsx | 19 +++++++++--------- .../pages}/dashboard/RewardsDashboardCard.tsx | 0 .../dashboard/RewardsDashboardCardValue.tsx | 2 +- .../dashboard/RewardsDashboardInfoCard.tsx | 0 .../dashboard/RewardsInstancesModal.tsx | 0 .../dashboard/RewardsTaskDetailsModal.tsx | 0 ...r-mode_activity-tab-dark-mode-mobile-1.png | Bin ...-mode_referrals-tab-dark-mode-mobile-1.png | Bin ...-mode_resources-tab-dark-mode-mobile-1.png | Bin ...efault_activity-tab-dark-mode-mobile-1.png | Bin ...fault_referrals-tab-dark-mode-mobile-1.png | Bin ...fault_resources-tab-dark-mode-mobile-1.png | Bin ...sDashboard.pw.tsx_default_with-error-1.png | Bin ...mobile_activity-tab-dark-mode-mobile-1.png | Bin ...obile_referrals-tab-dark-mode-mobile-1.png | Bin ...obile_resources-tab-dark-mode-mobile-1.png | Bin .../dashboard/streakModal/BadgeCard.tsx | 0 .../dashboard/streakModal/ProgressSegment.tsx | 0 .../streakModal/RewardsStreakModal.pw.tsx | 3 ++- .../streakModal/RewardsStreakModal.tsx | 0 ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...ark-color-mode_filled-view-dark-mode-1.png | Bin ...l.pw.tsx_default_base-view-dark-mode-1.png | Bin ...pw.tsx_default_filled-view-dark-mode-1.png | Bin ...odal.pw.tsx_default_mobile-base-view-1.png | Bin ...al.pw.tsx_default_mobile-filled-view-1.png | Bin .../pages}/dashboard/tabs/ActivityTab.tsx | 6 +++--- .../pages}/dashboard/tabs/ReferralsTab.tsx | 5 +++-- .../pages}/dashboard/tabs/ResourcesTab.tsx | 0 .../features/rewards/stubs.ts | 0 .../components/TokenAddToWallet.tsx | 3 ++- client/shared/web3/useAddChain.tsx | 3 ++- .../ContractVerificationForm.tsx | 3 ++- .../methods/useCallMethodWalletClient.ts | 2 +- .../slices/home/pages/index/HeroBanner.pw.tsx | 4 ++-- client/slices/home/pages/index/HeroBanner.tsx | 2 +- pages/_app.tsx | 6 +++--- pages/account/merits.tsx | 2 +- playwright/TestApp.tsx | 2 +- ui/marketplace/MarketplaceAppTopBar.tsx | 2 +- .../essentialDapps/revoke/hooks/useRevoke.tsx | 3 ++- ui/marketplace/useMarketplaceWallet.tsx | 3 ++- ui/snippets/header/HeaderDesktop.tsx | 2 +- ui/snippets/header/HeaderMobile.tsx | 2 +- .../horizontal/NavigationDesktop.pw.tsx | 4 ++-- .../horizontal/NavigationDesktop.tsx | 2 +- .../navigation/mobile/NavigationMobile.tsx | 2 +- .../navigation/vertical/NavigationDesktop.tsx | 2 +- 70 files changed, 71 insertions(+), 57 deletions(-) rename {ui/rewards => client/features/rewards/components}/MeritsIcon.tsx (100%) rename {ui/snippets/navigation/vertical => client/features/rewards/components}/NavLinkRewards.tsx (90%) rename {ui/rewards => client/features/rewards/components}/RewardsActivityTracker.tsx (82%) rename {ui/rewards => client/features/rewards/components}/RewardsButton.tsx (96%) rename {ui/rewards => client/features/rewards/components}/RewardsReadOnlyInputWithCopy.tsx (100%) rename {ui/rewards => client/features/rewards/components}/login/RewardsLoginModal.tsx (98%) rename {ui/rewards => client/features/rewards/components}/login/steps/CongratsStepContent.tsx (98%) rename {ui/rewards => client/features/rewards/components}/login/steps/LoginStepContent.tsx (99%) rename lib/contexts/rewards.tsx => client/features/rewards/contexts.tsx (100%) rename lib/hooks/useRewardsActivity.tsx => client/features/rewards/hooks/useRewardsActivity.ts (98%) rename {ui => client/features}/rewards/hooks/useStreakBadges.ts (96%) rename {mocks/rewards => client/features/rewards/mocks}/activity.ts (100%) rename {mocks/rewards => client/features/rewards/mocks}/balance.ts (100%) rename mocks/rewards/dailyReward.ts => client/features/rewards/mocks/daily-reward.ts (100%) rename {mocks/rewards => client/features/rewards/mocks}/referrals.ts (100%) rename mocks/rewards/rewardsConfig.ts => client/features/rewards/mocks/rewards-config.ts (100%) rename mocks/rewards/streakBadges.ts => client/features/rewards/mocks/streak-badges.ts (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/DailyRewardClaimButton.tsx (97%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsActivityPassCard.tsx (97%) rename {ui/pages => client/features/rewards/pages/dashboard}/RewardsDashboard.pw.tsx (85%) rename {ui/pages => client/features/rewards/pages/dashboard}/RewardsDashboard.tsx (90%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsDashboardCard.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsDashboardCardValue.tsx (96%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsDashboardInfoCard.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsInstancesModal.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/RewardsTaskDetailsModal.tsx (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/rewards/pages/dashboard}/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/BadgeCard.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/ProgressSegment.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/RewardsStreakModal.pw.tsx (92%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/RewardsStreakModal.tsx (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_filled-view-dark-mode-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_filled-view-dark-mode-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-base-view-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-filled-view-1.png (100%) rename {ui/rewards => client/features/rewards/pages}/dashboard/tabs/ActivityTab.tsx (98%) rename {ui/rewards => client/features/rewards/pages}/dashboard/tabs/ReferralsTab.tsx (90%) rename {ui/rewards => client/features/rewards/pages}/dashboard/tabs/ResourcesTab.tsx (100%) rename stubs/rewards.ts => client/features/rewards/stubs.ts (100%) diff --git a/client/features/account/components/auth-modal/AuthModal.tsx b/client/features/account/components/auth-modal/AuthModal.tsx index d97bf9e9e5..c8c3b9e05e 100644 --- a/client/features/account/components/auth-modal/AuthModal.tsx +++ b/client/features/account/components/auth-modal/AuthModal.tsx @@ -9,11 +9,11 @@ import type { Screen, ScreenSuccess } from './types'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; import useGetCsrfToken from 'client/features/account/hooks/useGetCsrfToken'; +import { useRewardsContext } from 'client/features/rewards/contexts'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import AuthModalScreenConnectWallet from './screens/AuthModalScreenConnectWallet'; diff --git a/client/features/account/hooks/useLogout.ts b/client/features/account/hooks/useLogout.ts index 1d121a2210..ea226b92ea 100644 --- a/client/features/account/hooks/useLogout.ts +++ b/client/features/account/hooks/useLogout.ts @@ -9,11 +9,12 @@ import type { Route } from 'nextjs-routes'; import useApiFetch from 'client/api/hooks/useApiFetch'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import * as cookies from 'client/shared/storage/cookies'; import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { toaster } from 'toolkit/chakra/toaster'; const PROTECTED_ROUTES: Array = [ diff --git a/ui/rewards/MeritsIcon.tsx b/client/features/rewards/components/MeritsIcon.tsx similarity index 100% rename from ui/rewards/MeritsIcon.tsx rename to client/features/rewards/components/MeritsIcon.tsx diff --git a/ui/snippets/navigation/vertical/NavLinkRewards.tsx b/client/features/rewards/components/NavLinkRewards.tsx similarity index 90% rename from ui/snippets/navigation/vertical/NavLinkRewards.tsx rename to client/features/rewards/components/NavLinkRewards.tsx index 856bf99cea..2b9eddf592 100644 --- a/ui/snippets/navigation/vertical/NavLinkRewards.tsx +++ b/client/features/rewards/components/NavLinkRewards.tsx @@ -5,10 +5,10 @@ import React, { useCallback } from 'react'; import type { Route } from 'nextjs-routes'; -import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; +import { useRewardsContext } from 'client/features/rewards/contexts'; -import NavLink from './NavLink'; +import config from 'configs/app'; +import NavLink from 'ui/snippets/navigation/vertical/NavLink'; type Props = { isCollapsed?: boolean; diff --git a/ui/rewards/RewardsActivityTracker.tsx b/client/features/rewards/components/RewardsActivityTracker.tsx similarity index 82% rename from ui/rewards/RewardsActivityTracker.tsx rename to client/features/rewards/components/RewardsActivityTracker.tsx index e22e17378b..1338b58aa6 100644 --- a/ui/rewards/RewardsActivityTracker.tsx +++ b/client/features/rewards/components/RewardsActivityTracker.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import { useEffect } from 'react'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; const RewardsActivityTracker = () => { const router = useRouter(); diff --git a/ui/rewards/RewardsButton.tsx b/client/features/rewards/components/RewardsButton.tsx similarity index 96% rename from ui/rewards/RewardsButton.tsx rename to client/features/rewards/components/RewardsButton.tsx index 9296c21e73..9b2dd9351b 100644 --- a/ui/rewards/RewardsButton.tsx +++ b/client/features/rewards/components/RewardsButton.tsx @@ -5,9 +5,10 @@ import React, { useCallback } from 'react'; import { route } from 'nextjs-routes'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useRewardsContext } from 'lib/contexts/rewards'; import type { ButtonProps } from 'toolkit/chakra/button'; import { Button } from 'toolkit/chakra/button'; import { Tooltip } from 'toolkit/chakra/tooltip'; diff --git a/ui/rewards/RewardsReadOnlyInputWithCopy.tsx b/client/features/rewards/components/RewardsReadOnlyInputWithCopy.tsx similarity index 100% rename from ui/rewards/RewardsReadOnlyInputWithCopy.tsx rename to client/features/rewards/components/RewardsReadOnlyInputWithCopy.tsx diff --git a/ui/rewards/login/RewardsLoginModal.tsx b/client/features/rewards/components/login/RewardsLoginModal.tsx similarity index 98% rename from ui/rewards/login/RewardsLoginModal.tsx rename to client/features/rewards/components/login/RewardsLoginModal.tsx index 6d54e4eca1..a8ea1faafc 100644 --- a/ui/rewards/login/RewardsLoginModal.tsx +++ b/client/features/rewards/components/login/RewardsLoginModal.tsx @@ -6,8 +6,8 @@ import type { Screen } from 'client/features/account/components/auth-modal/types import AuthModal from 'client/features/account/components/auth-modal/AuthModal'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; +import { useRewardsContext } from 'client/features/rewards/contexts'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { DialogBody, DialogContent, DialogRoot, DialogHeader } from 'toolkit/chakra/dialog'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/ui/rewards/login/steps/CongratsStepContent.tsx b/client/features/rewards/components/login/steps/CongratsStepContent.tsx similarity index 98% rename from ui/rewards/login/steps/CongratsStepContent.tsx rename to client/features/rewards/components/login/steps/CongratsStepContent.tsx index 9a5fca534b..58365e6eaa 100644 --- a/ui/rewards/login/steps/CongratsStepContent.tsx +++ b/client/features/rewards/components/login/steps/CongratsStepContent.tsx @@ -5,7 +5,8 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import { useRewardsContext } from 'lib/contexts/rewards'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import { Button } from 'toolkit/chakra/button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/ui/rewards/login/steps/LoginStepContent.tsx b/client/features/rewards/components/login/steps/LoginStepContent.tsx similarity index 99% rename from ui/rewards/login/steps/LoginStepContent.tsx rename to client/features/rewards/components/login/steps/LoginStepContent.tsx index 9ded595de4..8bf1ab8781 100644 --- a/ui/rewards/login/steps/LoginStepContent.tsx +++ b/client/features/rewards/components/login/steps/LoginStepContent.tsx @@ -7,10 +7,10 @@ import React from 'react'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; +import { useRewardsContext } from 'client/features/rewards/contexts'; import * as cookies from 'client/shared/storage/cookies'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { Field } from 'toolkit/chakra/field'; diff --git a/lib/contexts/rewards.tsx b/client/features/rewards/contexts.tsx similarity index 100% rename from lib/contexts/rewards.tsx rename to client/features/rewards/contexts.tsx diff --git a/lib/hooks/useRewardsActivity.tsx b/client/features/rewards/hooks/useRewardsActivity.ts similarity index 98% rename from lib/hooks/useRewardsActivity.tsx rename to client/features/rewards/hooks/useRewardsActivity.ts index b5281ba683..9996dcb5bf 100644 --- a/lib/hooks/useRewardsActivity.tsx +++ b/client/features/rewards/hooks/useRewardsActivity.ts @@ -8,9 +8,9 @@ import useApiFetch from 'client/api/hooks/useApiFetch'; import useApiQuery from 'client/api/hooks/useApiQuery'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; +import { useRewardsContext } from 'client/features/rewards/contexts'; import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { MINUTE } from 'toolkit/utils/consts'; const feature = config.features.rewards; diff --git a/ui/rewards/hooks/useStreakBadges.ts b/client/features/rewards/hooks/useStreakBadges.ts similarity index 96% rename from ui/rewards/hooks/useStreakBadges.ts rename to client/features/rewards/hooks/useStreakBadges.ts index 6ccab67f75..46bf1177ad 100644 --- a/ui/rewards/hooks/useStreakBadges.ts +++ b/client/features/rewards/hooks/useStreakBadges.ts @@ -6,8 +6,9 @@ import type { GetAvailableBadgesResponse } from '@blockscout/points-types'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; const feature = config.features.rewards; diff --git a/mocks/rewards/activity.ts b/client/features/rewards/mocks/activity.ts similarity index 100% rename from mocks/rewards/activity.ts rename to client/features/rewards/mocks/activity.ts diff --git a/mocks/rewards/balance.ts b/client/features/rewards/mocks/balance.ts similarity index 100% rename from mocks/rewards/balance.ts rename to client/features/rewards/mocks/balance.ts diff --git a/mocks/rewards/dailyReward.ts b/client/features/rewards/mocks/daily-reward.ts similarity index 100% rename from mocks/rewards/dailyReward.ts rename to client/features/rewards/mocks/daily-reward.ts diff --git a/mocks/rewards/referrals.ts b/client/features/rewards/mocks/referrals.ts similarity index 100% rename from mocks/rewards/referrals.ts rename to client/features/rewards/mocks/referrals.ts diff --git a/mocks/rewards/rewardsConfig.ts b/client/features/rewards/mocks/rewards-config.ts similarity index 100% rename from mocks/rewards/rewardsConfig.ts rename to client/features/rewards/mocks/rewards-config.ts diff --git a/mocks/rewards/streakBadges.ts b/client/features/rewards/mocks/streak-badges.ts similarity index 100% rename from mocks/rewards/streakBadges.ts rename to client/features/rewards/mocks/streak-badges.ts diff --git a/ui/rewards/dashboard/DailyRewardClaimButton.tsx b/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx similarity index 97% rename from ui/rewards/dashboard/DailyRewardClaimButton.tsx rename to client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx index c4fb0f059e..b9552ac02d 100644 --- a/ui/rewards/dashboard/DailyRewardClaimButton.tsx +++ b/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx @@ -6,7 +6,8 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import splitSecondsInPeriods from 'client/slices/block/pages/countdown-details/split-seconds-in-periods'; -import { useRewardsContext } from 'lib/contexts/rewards'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import { Button } from 'toolkit/chakra/button'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/ui/rewards/dashboard/RewardsActivityPassCard.tsx b/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx similarity index 97% rename from ui/rewards/dashboard/RewardsActivityPassCard.tsx rename to client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx index 1fd039db69..31acf32ea1 100644 --- a/ui/rewards/dashboard/RewardsActivityPassCard.tsx +++ b/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx @@ -2,8 +2,9 @@ import { Flex, Text } from '@chakra-ui/react'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { Heading } from 'toolkit/chakra/heading'; import { Image } from 'toolkit/chakra/image'; diff --git a/ui/pages/RewardsDashboard.pw.tsx b/client/features/rewards/pages/dashboard/RewardsDashboard.pw.tsx similarity index 85% rename from ui/pages/RewardsDashboard.pw.tsx rename to client/features/rewards/pages/dashboard/RewardsDashboard.pw.tsx index 523ab9ef58..649c79f2b6 100644 --- a/ui/pages/RewardsDashboard.pw.tsx +++ b/client/features/rewards/pages/dashboard/RewardsDashboard.pw.tsx @@ -2,12 +2,12 @@ import type { BrowserContext } from '@playwright/test'; import React from 'react'; import * as profileMock from 'client/features/account/mocks/user-profile'; +import * as activityMock from 'client/features/rewards/mocks/activity'; +import * as rewardsBalanceMock from 'client/features/rewards/mocks/balance'; +import * as dailyRewardMock from 'client/features/rewards/mocks/daily-reward'; +import * as referralsMock from 'client/features/rewards/mocks/referrals'; +import * as rewardsConfigMock from 'client/features/rewards/mocks/rewards-config'; -import * as activityMock from 'mocks/rewards/activity'; -import * as rewardsBalanceMock from 'mocks/rewards/balance'; -import * as dailyRewardMock from 'mocks/rewards/dailyReward'; -import * as referralsMock from 'mocks/rewards/referrals'; -import * as rewardsConfigMock from 'mocks/rewards/rewardsConfig'; import { contextWithAuth } from 'playwright/fixtures/auth'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { contextWithRewards } from 'playwright/fixtures/rewards'; diff --git a/ui/pages/RewardsDashboard.tsx b/client/features/rewards/pages/dashboard/RewardsDashboard.tsx similarity index 90% rename from ui/pages/RewardsDashboard.tsx rename to client/features/rewards/pages/dashboard/RewardsDashboard.tsx index 90da79ecd0..fc002205cf 100644 --- a/ui/pages/RewardsDashboard.tsx +++ b/client/features/rewards/pages/dashboard/RewardsDashboard.tsx @@ -4,28 +4,29 @@ import { Flex } from '@chakra-ui/react'; import React, { useEffect, useState } from 'react'; import useRedirectForInvalidAuthToken from 'client/features/account/hooks/useRedirectForInvalidAuthToken'; +import { useRewardsContext } from 'client/features/rewards/contexts'; +import useStreakBadges from 'client/features/rewards/hooks/useStreakBadges'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { apos } from 'toolkit/utils/htmlEntities'; -import DailyRewardClaimButton from 'ui/rewards/dashboard/DailyRewardClaimButton'; -import RewardsDashboardCard from 'ui/rewards/dashboard/RewardsDashboardCard'; -import RewardsDashboardCardValue from 'ui/rewards/dashboard/RewardsDashboardCardValue'; -import RewardsStreakModal from 'ui/rewards/dashboard/streakModal/RewardsStreakModal'; -import ActivityTab from 'ui/rewards/dashboard/tabs/ActivityTab'; -import ReferralsTab from 'ui/rewards/dashboard/tabs/ReferralsTab'; -import ResourcesTab from 'ui/rewards/dashboard/tabs/ResourcesTab'; -import useStreakBadges from 'ui/rewards/hooks/useStreakBadges'; import AdBanner from 'ui/shared/ad/AdBanner'; import PageTitle from 'ui/shared/Page/PageTitle'; +import DailyRewardClaimButton from './DailyRewardClaimButton'; +import RewardsDashboardCard from './RewardsDashboardCard'; +import RewardsDashboardCardValue from './RewardsDashboardCardValue'; +import RewardsStreakModal from './streakModal/RewardsStreakModal'; +import ActivityTab from './tabs/ActivityTab'; +import ReferralsTab from './tabs/ReferralsTab'; +import ResourcesTab from './tabs/ResourcesTab'; + const RewardsDashboard = () => { const { balancesQuery, isAuth, referralsQuery, rewardsConfigQuery, dailyRewardQuery, isInitialized } = useRewardsContext(); const { nextAchievementText, isLoading: isBadgesLoading, badgesQuery } = useStreakBadges(); diff --git a/ui/rewards/dashboard/RewardsDashboardCard.tsx b/client/features/rewards/pages/dashboard/RewardsDashboardCard.tsx similarity index 100% rename from ui/rewards/dashboard/RewardsDashboardCard.tsx rename to client/features/rewards/pages/dashboard/RewardsDashboardCard.tsx diff --git a/ui/rewards/dashboard/RewardsDashboardCardValue.tsx b/client/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx similarity index 96% rename from ui/rewards/dashboard/RewardsDashboardCardValue.tsx rename to client/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx index 0cb3e87cd7..5555234059 100644 --- a/ui/rewards/dashboard/RewardsDashboardCardValue.tsx +++ b/client/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx @@ -7,7 +7,7 @@ import { Heading } from 'toolkit/chakra/heading'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Hint } from 'toolkit/components/Hint/Hint'; -import MeritsIcon from '../MeritsIcon'; +import MeritsIcon from '../../components/MeritsIcon'; type Props = { label?: string; diff --git a/ui/rewards/dashboard/RewardsDashboardInfoCard.tsx b/client/features/rewards/pages/dashboard/RewardsDashboardInfoCard.tsx similarity index 100% rename from ui/rewards/dashboard/RewardsDashboardInfoCard.tsx rename to client/features/rewards/pages/dashboard/RewardsDashboardInfoCard.tsx diff --git a/ui/rewards/dashboard/RewardsInstancesModal.tsx b/client/features/rewards/pages/dashboard/RewardsInstancesModal.tsx similarity index 100% rename from ui/rewards/dashboard/RewardsInstancesModal.tsx rename to client/features/rewards/pages/dashboard/RewardsInstancesModal.tsx diff --git a/ui/rewards/dashboard/RewardsTaskDetailsModal.tsx b/client/features/rewards/pages/dashboard/RewardsTaskDetailsModal.tsx similarity index 100% rename from ui/rewards/dashboard/RewardsTaskDetailsModal.tsx rename to client/features/rewards/pages/dashboard/RewardsTaskDetailsModal.tsx diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png b/client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png rename to client/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png diff --git a/ui/rewards/dashboard/streakModal/BadgeCard.tsx b/client/features/rewards/pages/dashboard/streakModal/BadgeCard.tsx similarity index 100% rename from ui/rewards/dashboard/streakModal/BadgeCard.tsx rename to client/features/rewards/pages/dashboard/streakModal/BadgeCard.tsx diff --git a/ui/rewards/dashboard/streakModal/ProgressSegment.tsx b/client/features/rewards/pages/dashboard/streakModal/ProgressSegment.tsx similarity index 100% rename from ui/rewards/dashboard/streakModal/ProgressSegment.tsx rename to client/features/rewards/pages/dashboard/streakModal/ProgressSegment.tsx diff --git a/ui/rewards/dashboard/streakModal/RewardsStreakModal.pw.tsx b/client/features/rewards/pages/dashboard/streakModal/RewardsStreakModal.pw.tsx similarity index 92% rename from ui/rewards/dashboard/streakModal/RewardsStreakModal.pw.tsx rename to client/features/rewards/pages/dashboard/streakModal/RewardsStreakModal.pw.tsx index cb4b11364f..ebe4ca84eb 100644 --- a/ui/rewards/dashboard/streakModal/RewardsStreakModal.pw.tsx +++ b/client/features/rewards/pages/dashboard/streakModal/RewardsStreakModal.pw.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { GetAvailableBadgesResponse } from '@blockscout/points-types'; -import * as streakBadgesMock from 'mocks/rewards/streakBadges'; +import * as streakBadgesMock from 'client/features/rewards/mocks/streak-badges'; + import type { TestFnArgs } from 'playwright/lib'; import { test, expect, devices } from 'playwright/lib'; diff --git a/ui/rewards/dashboard/streakModal/RewardsStreakModal.tsx b/client/features/rewards/pages/dashboard/streakModal/RewardsStreakModal.tsx similarity index 100% rename from ui/rewards/dashboard/streakModal/RewardsStreakModal.tsx rename to client/features/rewards/pages/dashboard/streakModal/RewardsStreakModal.tsx diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_filled-view-dark-mode-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_filled-view-dark-mode-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_filled-view-dark-mode-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_dark-color-mode_filled-view-dark-mode-1.png diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_base-view-dark-mode-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_filled-view-dark-mode-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_filled-view-dark-mode-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_filled-view-dark-mode-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_filled-view-dark-mode-1.png diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-base-view-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-base-view-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-filled-view-1.png b/client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-filled-view-1.png similarity index 100% rename from ui/rewards/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-filled-view-1.png rename to client/features/rewards/pages/dashboard/streakModal/__screenshots__/RewardsStreakModal.pw.tsx_default_mobile-filled-view-1.png diff --git a/ui/rewards/dashboard/tabs/ActivityTab.tsx b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx similarity index 98% rename from ui/rewards/dashboard/tabs/ActivityTab.tsx rename to client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx index 9077ce449d..90d8fc64b1 100644 --- a/ui/rewards/dashboard/tabs/ActivityTab.tsx +++ b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx @@ -10,13 +10,13 @@ import { route } from 'nextjs-routes'; import useApiQuery from 'client/api/hooks/useApiQuery'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; +import { useRewardsContext } from 'client/features/rewards/contexts'; +import { USER_ACTIVITY } from 'client/features/rewards/stubs'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import { useRewardsContext } from 'lib/contexts/rewards'; import dayjs from 'lib/date/dayjs'; -import { USER_ACTIVITY } from 'stubs/rewards'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; @@ -26,7 +26,7 @@ import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { mdash } from 'toolkit/utils/htmlEntities'; import IconSvg from 'ui/shared/IconSvg'; -import MeritsIcon from '../../MeritsIcon'; +import MeritsIcon from '../../../components/MeritsIcon'; import RewardsActivityPassCard from '../RewardsActivityPassCard'; import RewardsInstancesModal from '../RewardsInstancesModal'; import RewardsTaskDetailsModal from '../RewardsTaskDetailsModal'; diff --git a/ui/rewards/dashboard/tabs/ReferralsTab.tsx b/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx similarity index 90% rename from ui/rewards/dashboard/tabs/ReferralsTab.tsx rename to client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx index 5d8d9bba1c..1b7c2123a1 100644 --- a/ui/rewards/dashboard/tabs/ReferralsTab.tsx +++ b/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx @@ -2,10 +2,11 @@ import { Flex } from '@chakra-ui/react'; -import { useRewardsContext } from 'lib/contexts/rewards'; +import { useRewardsContext } from 'client/features/rewards/contexts'; + import { Skeleton } from 'toolkit/chakra/skeleton'; -import RewardsReadOnlyInputWithCopy from '../../RewardsReadOnlyInputWithCopy'; +import RewardsReadOnlyInputWithCopy from '../../../components/RewardsReadOnlyInputWithCopy'; import RewardsDashboardCard from '../RewardsDashboardCard'; export default function ReferralsTab() { diff --git a/ui/rewards/dashboard/tabs/ResourcesTab.tsx b/client/features/rewards/pages/dashboard/tabs/ResourcesTab.tsx similarity index 100% rename from ui/rewards/dashboard/tabs/ResourcesTab.tsx rename to client/features/rewards/pages/dashboard/tabs/ResourcesTab.tsx diff --git a/stubs/rewards.ts b/client/features/rewards/stubs.ts similarity index 100% rename from stubs/rewards.ts rename to client/features/rewards/stubs.ts diff --git a/client/features/web3-wallet/components/TokenAddToWallet.tsx b/client/features/web3-wallet/components/TokenAddToWallet.tsx index 9e9dbdda25..7f4a459258 100644 --- a/client/features/web3-wallet/components/TokenAddToWallet.tsx +++ b/client/features/web3-wallet/components/TokenAddToWallet.tsx @@ -6,6 +6,8 @@ import type { WatchAssetParams } from 'viem'; import type { TokenInfo } from 'client/slices/token/types/api'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import useProvider from 'client/shared/web3/useProvider'; @@ -13,7 +15,6 @@ import useSwitchOrAddChain from 'client/shared/web3/useSwitchOrAddChain'; import { WALLETS_INFO } from 'client/shared/web3/wallets'; import config from 'configs/app'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { toaster } from 'toolkit/chakra/toaster'; diff --git a/client/shared/web3/useAddChain.tsx b/client/shared/web3/useAddChain.tsx index e90ec3ae86..ca3e922897 100644 --- a/client/shared/web3/useAddChain.tsx +++ b/client/shared/web3/useAddChain.tsx @@ -3,9 +3,10 @@ import React from 'react'; import type { AddEthereumChainParameter } from 'viem'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { SECOND } from 'toolkit/utils/consts'; import useProvider from './useProvider'; diff --git a/client/slices/contract/pages/contract-verification/ContractVerificationForm.tsx b/client/slices/contract/pages/contract-verification/ContractVerificationForm.tsx index 62ce3c6e08..c1b64f36b0 100644 --- a/client/slices/contract/pages/contract-verification/ContractVerificationForm.tsx +++ b/client/slices/contract/pages/contract-verification/ContractVerificationForm.tsx @@ -17,12 +17,13 @@ import useSocketMessage from 'client/api/socket/useSocketMessage'; import type { SmartContractVerificationConfig } from 'client/slices/contract/pages/contract-verification/utils'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; import capitalizeFirstLetter from 'client/shared/text/capitalize-first-letter'; import delay from 'client/shared/utils/delay'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; import { useUpdateEffect } from 'toolkit/hooks/useUpdateEffect'; diff --git a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts index dfdd89efa6..d2b5a8aa44 100644 --- a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts +++ b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts @@ -7,10 +7,10 @@ import { useAccount, useSwitchChain, useWriteContract, useSendTransaction } from import type { FormSubmitResult, SmartContractMethod } from './types'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { getNativeCoinValue } from './utils'; diff --git a/client/slices/home/pages/index/HeroBanner.pw.tsx b/client/slices/home/pages/index/HeroBanner.pw.tsx index a985c5430a..b6cf50010d 100644 --- a/client/slices/home/pages/index/HeroBanner.pw.tsx +++ b/client/slices/home/pages/index/HeroBanner.pw.tsx @@ -2,9 +2,9 @@ import type { BrowserContext } from '@playwright/test'; import React from 'react'; import * as profileMock from 'client/features/account/mocks/user-profile'; +import * as rewardsBalanceMock from 'client/features/rewards/mocks/balance'; +import * as dailyRewardMock from 'client/features/rewards/mocks/daily-reward'; -import * as rewardsBalanceMock from 'mocks/rewards/balance'; -import * as dailyRewardMock from 'mocks/rewards/dailyReward'; import { contextWithAuth } from 'playwright/fixtures/auth'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { contextWithRewards } from 'playwright/fixtures/rewards'; diff --git a/client/slices/home/pages/index/HeroBanner.tsx b/client/slices/home/pages/index/HeroBanner.tsx index d7c8d1f2e3..e1933c0f61 100644 --- a/client/slices/home/pages/index/HeroBanner.tsx +++ b/client/slices/home/pages/index/HeroBanner.tsx @@ -9,11 +9,11 @@ import SearchBar from 'client/slices/search/components/search-bar/SearchBarDeskt import SearchBarMobile from 'client/slices/search/components/search-bar/SearchBarMobile'; import UserProfileDesktop from 'client/features/account/components/user-profile/UserProfileDesktop'; +import RewardsButton from 'client/features/rewards/components/RewardsButton'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import RewardsButton from 'ui/rewards/RewardsButton'; import AdBanner from 'ui/shared/ad/AdBanner'; export const BACKGROUND_DEFAULT = diff --git a/pages/_app.tsx b/pages/_app.tsx index 60ec793048..02d46b5137 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -36,9 +36,9 @@ import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import Layout from 'ui/shared/layout/Layout'; import Web3Provider from 'ui/shared/web3/Web3Provider'; -const RewardsContextProvider = dynamic(() => import('lib/contexts/rewards').then(module => module.RewardsContextProvider), { ssr: false }); -const RewardsLoginModal = dynamic(() => import('ui/rewards/login/RewardsLoginModal'), { ssr: false }); -const RewardsActivityTracker = dynamic(() => import('ui/rewards/RewardsActivityTracker'), { ssr: false }); +const RewardsContextProvider = dynamic(() => import('client/features/rewards/contexts').then(module => module.RewardsContextProvider), { ssr: false }); +const RewardsLoginModal = dynamic(() => import('client/features/rewards/components/login/RewardsLoginModal'), { ssr: false }); +const RewardsActivityTracker = dynamic(() => import('client/features/rewards/components/RewardsActivityTracker'), { ssr: false }); import 'client/shared/i18n/set-locale'; // import 'focus-visible/dist/focus-visible'; diff --git a/pages/account/merits.tsx b/pages/account/merits.tsx index 943e472155..7545ed16e4 100644 --- a/pages/account/merits.tsx +++ b/pages/account/merits.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const RewardsDashboard = dynamic(() => import('ui/pages/RewardsDashboard'), { ssr: false }); +const RewardsDashboard = dynamic(() => import('client/features/rewards/pages/dashboard/RewardsDashboard'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx index b3508df4b0..ddde94af31 100644 --- a/playwright/TestApp.tsx +++ b/playwright/TestApp.tsx @@ -11,11 +11,11 @@ import { SocketProvider } from 'client/api/socket/context'; import { currentChain } from 'client/features/connect-wallet/utils/chains'; import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; +import { RewardsContextProvider } from 'client/features/rewards/contexts'; import config from 'configs/app'; import { AppContextProvider } from 'lib/contexts/app'; import { MarketplaceContext } from 'lib/contexts/marketplace'; -import { RewardsContextProvider } from 'lib/contexts/rewards'; import { SettingsContextProvider } from 'lib/contexts/settings'; import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; diff --git a/ui/marketplace/MarketplaceAppTopBar.tsx b/ui/marketplace/MarketplaceAppTopBar.tsx index 476a40267f..66dec4fb24 100644 --- a/ui/marketplace/MarketplaceAppTopBar.tsx +++ b/ui/marketplace/MarketplaceAppTopBar.tsx @@ -8,6 +8,7 @@ import type { MarketplaceApp } from 'types/client/marketplace'; import { route } from 'nextjs-routes'; import UserProfileDesktop from 'client/features/account/components/user-profile/UserProfileDesktop'; +import RewardsButton from 'client/features/rewards/components/RewardsButton'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMobile from 'client/shared/hooks/useIsMobile'; @@ -17,7 +18,6 @@ import { useAppContext } from 'lib/contexts/app'; import { Link } from 'toolkit/chakra/link'; import { BackToButton } from 'toolkit/components/buttons/BackToButton'; import { makePrettyLink } from 'toolkit/utils/url'; -import RewardsButton from 'ui/rewards/RewardsButton'; import NetworkIcon from 'ui/snippets/networkLogo/NetworkIcon'; import MarketplaceAppInfo from './MarketplaceAppInfo'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx index 4779ad2d1f..1d6d85c352 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx @@ -12,9 +12,10 @@ import type { AllowanceType } from 'types/client/revoke'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { toaster } from 'toolkit/chakra/toaster'; import createPublicClient from '../lib/createPublicClient'; diff --git a/ui/marketplace/useMarketplaceWallet.tsx b/ui/marketplace/useMarketplaceWallet.tsx index df75fd9c73..24dcfbb517 100644 --- a/ui/marketplace/useMarketplaceWallet.tsx +++ b/ui/marketplace/useMarketplaceWallet.tsx @@ -5,10 +5,11 @@ import { useCallback } from 'react'; import type { Account, SignTypedDataParameters } from 'viem'; import { useAccount, useSendTransaction, useSwitchChain, useSignMessage, useSignTypedData } from 'wagmi'; +import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import config from 'configs/app'; -import useRewardsActivity from 'lib/hooks/useRewardsActivity'; type SendTransactionArgs = { chainId?: number; diff --git a/ui/snippets/header/HeaderDesktop.tsx b/ui/snippets/header/HeaderDesktop.tsx index bd8bb9ca77..adeb6d6202 100644 --- a/ui/snippets/header/HeaderDesktop.tsx +++ b/ui/snippets/header/HeaderDesktop.tsx @@ -6,9 +6,9 @@ import React from 'react'; import SearchBar from 'client/slices/search/components/search-bar/SearchBarDesktop'; import UserProfileDesktop from 'client/features/account/components/user-profile/UserProfileDesktop'; +import RewardsButton from 'client/features/rewards/components/RewardsButton'; import config from 'configs/app'; -import RewardsButton from 'ui/rewards/RewardsButton'; type Props = { renderSearchBar?: () => React.ReactNode; diff --git a/ui/snippets/header/HeaderMobile.tsx b/ui/snippets/header/HeaderMobile.tsx index dcb6380a89..70363d0543 100644 --- a/ui/snippets/header/HeaderMobile.tsx +++ b/ui/snippets/header/HeaderMobile.tsx @@ -8,10 +8,10 @@ import SearchBarMobile from 'client/slices/search/components/search-bar/SearchBa import UserProfileAuth0 from 'client/features/account/components/user-profile/auth0/UserProfileMobile'; import UserWalletMobile from 'client/features/account/components/user-profile/wallet/UserWalletMobile'; +import RewardsButton from 'client/features/rewards/components/RewardsButton'; import config from 'configs/app'; import { useIsSticky } from 'toolkit/hooks/useIsSticky'; -import RewardsButton from 'ui/rewards/RewardsButton'; import NetworkIcon from 'ui/snippets/networkLogo/NetworkIcon'; import RollupStageBadge from '../navigation/RollupStageBadge'; diff --git a/ui/snippets/navigation/horizontal/NavigationDesktop.pw.tsx b/ui/snippets/navigation/horizontal/NavigationDesktop.pw.tsx index fe146d58ca..9ab20139a7 100644 --- a/ui/snippets/navigation/horizontal/NavigationDesktop.pw.tsx +++ b/ui/snippets/navigation/horizontal/NavigationDesktop.pw.tsx @@ -2,9 +2,9 @@ import type { BrowserContext, Locator } from '@playwright/test'; import React from 'react'; import * as profileMock from 'client/features/account/mocks/user-profile'; +import * as rewardsBalanceMock from 'client/features/rewards/mocks/balance'; +import * as dailyRewardMock from 'client/features/rewards/mocks/daily-reward'; -import * as rewardsBalanceMock from 'mocks/rewards/balance'; -import * as dailyRewardMock from 'mocks/rewards/dailyReward'; import { contextWithAuth } from 'playwright/fixtures/auth'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { contextWithRewards } from 'playwright/fixtures/rewards'; diff --git a/ui/snippets/navigation/horizontal/NavigationDesktop.tsx b/ui/snippets/navigation/horizontal/NavigationDesktop.tsx index 9da1252c1d..6168544055 100644 --- a/ui/snippets/navigation/horizontal/NavigationDesktop.tsx +++ b/ui/snippets/navigation/horizontal/NavigationDesktop.tsx @@ -5,10 +5,10 @@ import React from 'react'; import UserProfileDesktop from 'client/features/account/components/user-profile/UserProfileDesktop'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; +import RewardsButton from 'client/features/rewards/components/RewardsButton'; import config from 'configs/app'; import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems'; -import RewardsButton from 'ui/rewards/RewardsButton'; import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils'; import NetworkLogo from 'ui/snippets/networkLogo/NetworkLogo'; diff --git a/ui/snippets/navigation/mobile/NavigationMobile.tsx b/ui/snippets/navigation/mobile/NavigationMobile.tsx index 604751f7b4..29d15f5478 100644 --- a/ui/snippets/navigation/mobile/NavigationMobile.tsx +++ b/ui/snippets/navigation/mobile/NavigationMobile.tsx @@ -4,6 +4,7 @@ import { Box, Flex, Text, VStack } from '@chakra-ui/react'; import React, { useCallback } from 'react'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; +import NavLinkRewards from 'client/features/rewards/components/NavLinkRewards'; import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; @@ -11,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg'; import NavigationPromoBanner from '../promoBanner/NavigationPromoBanner'; import NavLink from '../vertical/NavLink'; -import NavLinkRewards from '../vertical/NavLinkRewards'; import NavLinkGroup from './NavLinkGroup'; const ANIMATION_DURATION = 300; diff --git a/ui/snippets/navigation/vertical/NavigationDesktop.tsx b/ui/snippets/navigation/vertical/NavigationDesktop.tsx index ecc1d61659..7b7a73e5e0 100644 --- a/ui/snippets/navigation/vertical/NavigationDesktop.tsx +++ b/ui/snippets/navigation/vertical/NavigationDesktop.tsx @@ -4,6 +4,7 @@ import { Flex, Box, VStack } from '@chakra-ui/react'; import React from 'react'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; +import NavLinkRewards from 'client/features/rewards/components/NavLinkRewards'; import * as cookies from 'client/shared/storage/cookies'; @@ -18,7 +19,6 @@ import RollupStageBadge from '../RollupStageBadge'; import TestnetBadge from '../TestnetBadge'; import NavLink from './NavLink'; import NavLinkGroup from './NavLinkGroup'; -import NavLinkRewards from './NavLinkRewards'; const NavigationDesktop = () => { const appProps = useAppContext(); From aeddd8db8b86537f749b5777973c67196dd6ad80 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 09:21:16 +0200 Subject: [PATCH 06/85] =?UTF-8?q?track:=206-9=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3466?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 2bc20b9fae..7965576c4e 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -227,7 +227,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-8 · [ ] Feature: `validators` -### 6-9 · [ ] Feature: `marketplace` +### 6-9 · [~] Feature: `marketplace` · [#3466](https://github.com/blockscout/frontend/issues/3466) **Scope:** Includes `lib/contexts/marketplace.tsx`, `lib/hooks/useGraphLinks` → `client/features/marketplace/` ### 6-10 · [~] Feature: `rewards` · [#3462](https://github.com/blockscout/frontend/issues/3462) From 1de9eb2cea9b9a7a559627f9d37f7621dbace3a1 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 10:57:38 +0200 Subject: [PATCH 07/85] rename context file --- client/features/account/components/auth-modal/AuthModal.tsx | 2 +- client/features/account/hooks/useLogout.ts | 2 +- client/features/rewards/components/NavLinkRewards.tsx | 2 +- client/features/rewards/components/RewardsButton.tsx | 2 +- client/features/rewards/components/login/RewardsLoginModal.tsx | 2 +- .../rewards/components/login/steps/CongratsStepContent.tsx | 2 +- .../rewards/components/login/steps/LoginStepContent.tsx | 2 +- client/features/rewards/{contexts.tsx => context.tsx} | 0 client/features/rewards/hooks/useRewardsActivity.ts | 2 +- client/features/rewards/hooks/useStreakBadges.ts | 2 +- .../features/rewards/pages/dashboard/DailyRewardClaimButton.tsx | 2 +- .../rewards/pages/dashboard/RewardsActivityPassCard.tsx | 2 +- client/features/rewards/pages/dashboard/RewardsDashboard.tsx | 2 +- client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx | 2 +- client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx | 2 +- pages/_app.tsx | 2 +- playwright/TestApp.tsx | 2 +- 17 files changed, 16 insertions(+), 16 deletions(-) rename client/features/rewards/{contexts.tsx => context.tsx} (100%) diff --git a/client/features/account/components/auth-modal/AuthModal.tsx b/client/features/account/components/auth-modal/AuthModal.tsx index c8c3b9e05e..219c89a66f 100644 --- a/client/features/account/components/auth-modal/AuthModal.tsx +++ b/client/features/account/components/auth-modal/AuthModal.tsx @@ -9,7 +9,7 @@ import type { Screen, ScreenSuccess } from './types'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; import useGetCsrfToken from 'client/features/account/hooks/useGetCsrfToken'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import * as mixpanel from 'client/shared/analytics/mixpanel'; diff --git a/client/features/account/hooks/useLogout.ts b/client/features/account/hooks/useLogout.ts index ea226b92ea..df8f9bf6ab 100644 --- a/client/features/account/hooks/useLogout.ts +++ b/client/features/account/hooks/useLogout.ts @@ -9,7 +9,7 @@ import type { Route } from 'nextjs-routes'; import useApiFetch from 'client/api/hooks/useApiFetch'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import * as cookies from 'client/shared/storage/cookies'; diff --git a/client/features/rewards/components/NavLinkRewards.tsx b/client/features/rewards/components/NavLinkRewards.tsx index 2b9eddf592..e0b70bf782 100644 --- a/client/features/rewards/components/NavLinkRewards.tsx +++ b/client/features/rewards/components/NavLinkRewards.tsx @@ -5,7 +5,7 @@ import React, { useCallback } from 'react'; import type { Route } from 'nextjs-routes'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import config from 'configs/app'; import NavLink from 'ui/snippets/navigation/vertical/NavLink'; diff --git a/client/features/rewards/components/RewardsButton.tsx b/client/features/rewards/components/RewardsButton.tsx index 9b2dd9351b..cebc652882 100644 --- a/client/features/rewards/components/RewardsButton.tsx +++ b/client/features/rewards/components/RewardsButton.tsx @@ -5,7 +5,7 @@ import React, { useCallback } from 'react'; import { route } from 'nextjs-routes'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; diff --git a/client/features/rewards/components/login/RewardsLoginModal.tsx b/client/features/rewards/components/login/RewardsLoginModal.tsx index a8ea1faafc..7f366a99ea 100644 --- a/client/features/rewards/components/login/RewardsLoginModal.tsx +++ b/client/features/rewards/components/login/RewardsLoginModal.tsx @@ -6,7 +6,7 @@ import type { Screen } from 'client/features/account/components/auth-modal/types import AuthModal from 'client/features/account/components/auth-modal/AuthModal'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import { DialogBody, DialogContent, DialogRoot, DialogHeader } from 'toolkit/chakra/dialog'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/client/features/rewards/components/login/steps/CongratsStepContent.tsx b/client/features/rewards/components/login/steps/CongratsStepContent.tsx index 58365e6eaa..4fbb2c2b83 100644 --- a/client/features/rewards/components/login/steps/CongratsStepContent.tsx +++ b/client/features/rewards/components/login/steps/CongratsStepContent.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import { Button } from 'toolkit/chakra/button'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/rewards/components/login/steps/LoginStepContent.tsx b/client/features/rewards/components/login/steps/LoginStepContent.tsx index 8bf1ab8781..4602a9bbf9 100644 --- a/client/features/rewards/components/login/steps/LoginStepContent.tsx +++ b/client/features/rewards/components/login/steps/LoginStepContent.tsx @@ -7,7 +7,7 @@ import React from 'react'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import * as cookies from 'client/shared/storage/cookies'; diff --git a/client/features/rewards/contexts.tsx b/client/features/rewards/context.tsx similarity index 100% rename from client/features/rewards/contexts.tsx rename to client/features/rewards/context.tsx diff --git a/client/features/rewards/hooks/useRewardsActivity.ts b/client/features/rewards/hooks/useRewardsActivity.ts index 9996dcb5bf..efc837a482 100644 --- a/client/features/rewards/hooks/useRewardsActivity.ts +++ b/client/features/rewards/hooks/useRewardsActivity.ts @@ -8,7 +8,7 @@ import useApiFetch from 'client/api/hooks/useApiFetch'; import useApiQuery from 'client/api/hooks/useApiQuery'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import config from 'configs/app'; import { MINUTE } from 'toolkit/utils/consts'; diff --git a/client/features/rewards/hooks/useStreakBadges.ts b/client/features/rewards/hooks/useStreakBadges.ts index 46bf1177ad..5a23ca73da 100644 --- a/client/features/rewards/hooks/useStreakBadges.ts +++ b/client/features/rewards/hooks/useStreakBadges.ts @@ -6,7 +6,7 @@ import type { GetAvailableBadgesResponse } from '@blockscout/points-types'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import config from 'configs/app'; diff --git a/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx b/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx index b9552ac02d..2fca52144e 100644 --- a/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx +++ b/client/features/rewards/pages/dashboard/DailyRewardClaimButton.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import splitSecondsInPeriods from 'client/slices/block/pages/countdown-details/split-seconds-in-periods'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import { Button } from 'toolkit/chakra/button'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx b/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx index 31acf32ea1..385bc0a434 100644 --- a/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx +++ b/client/features/rewards/pages/dashboard/RewardsActivityPassCard.tsx @@ -2,7 +2,7 @@ import { Flex, Text } from '@chakra-ui/react'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import config from 'configs/app'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; diff --git a/client/features/rewards/pages/dashboard/RewardsDashboard.tsx b/client/features/rewards/pages/dashboard/RewardsDashboard.tsx index fc002205cf..d4969bc7ea 100644 --- a/client/features/rewards/pages/dashboard/RewardsDashboard.tsx +++ b/client/features/rewards/pages/dashboard/RewardsDashboard.tsx @@ -4,7 +4,7 @@ import { Flex } from '@chakra-ui/react'; import React, { useEffect, useState } from 'react'; import useRedirectForInvalidAuthToken from 'client/features/account/hooks/useRedirectForInvalidAuthToken'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import useStreakBadges from 'client/features/rewards/hooks/useStreakBadges'; import useIsMobile from 'client/shared/hooks/useIsMobile'; diff --git a/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx index 90d8fc64b1..0b60c9e807 100644 --- a/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx +++ b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx @@ -10,7 +10,7 @@ import { route } from 'nextjs-routes'; import useApiQuery from 'client/api/hooks/useApiQuery'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import { USER_ACTIVITY } from 'client/features/rewards/stubs'; import useIsMobile from 'client/shared/hooks/useIsMobile'; diff --git a/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx b/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx index 1b7c2123a1..7f17f2773c 100644 --- a/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx +++ b/client/features/rewards/pages/dashboard/tabs/ReferralsTab.tsx @@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react'; -import { useRewardsContext } from 'client/features/rewards/contexts'; +import { useRewardsContext } from 'client/features/rewards/context'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/pages/_app.tsx b/pages/_app.tsx index 02d46b5137..29c718c2b2 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -36,7 +36,7 @@ import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import Layout from 'ui/shared/layout/Layout'; import Web3Provider from 'ui/shared/web3/Web3Provider'; -const RewardsContextProvider = dynamic(() => import('client/features/rewards/contexts').then(module => module.RewardsContextProvider), { ssr: false }); +const RewardsContextProvider = dynamic(() => import('client/features/rewards/context').then(module => module.RewardsContextProvider), { ssr: false }); const RewardsLoginModal = dynamic(() => import('client/features/rewards/components/login/RewardsLoginModal'), { ssr: false }); const RewardsActivityTracker = dynamic(() => import('client/features/rewards/components/RewardsActivityTracker'), { ssr: false }); diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx index ddde94af31..7bb6688da4 100644 --- a/playwright/TestApp.tsx +++ b/playwright/TestApp.tsx @@ -11,7 +11,7 @@ import { SocketProvider } from 'client/api/socket/context'; import { currentChain } from 'client/features/connect-wallet/utils/chains'; import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; -import { RewardsContextProvider } from 'client/features/rewards/contexts'; +import { RewardsContextProvider } from 'client/features/rewards/context'; import config from 'configs/app'; import { AppContextProvider } from 'lib/contexts/app'; From 0dc924aaf66b0f80cb4ca5b17ba8df8d8c8ea95e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 11:27:22 +0200 Subject: [PATCH 08/85] [Migration 6-16] Feature: dex-pools (#3467) feat(migration/6-16): move dex-pools feature to client/features/dex-pools Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/f20ff970-15af-4975-a30e-5483aeb19830 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- client/api/services/contract-info.ts | 2 +- .../dex-pools/components/entity}/PoolEntity.pw.tsx | 3 ++- .../dex-pools/components/entity}/PoolEntity.tsx | 8 ++++---- ....pw.tsx_dark-color-mode_no-icons-dark-mode-1.png | Bin ...w.tsx_dark-color-mode_with-icons-dark-mode-1.png | Bin ...olEntity.pw.tsx_default_no-icons-dark-mode-1.png | Bin ...Entity.pw.tsx_default_with-icons-dark-mode-1.png | Bin .../features/dex-pools/mocks}/pool.ts | 2 +- .../features/dex-pools/pages/details}/Pool.pw.tsx | 3 ++- .../features/dex-pools/pages/details}/Pool.tsx | 11 ++++++----- .../features/dex-pools/pages/details}/PoolInfo.tsx | 2 +- ...dark-color-mode_base-view-mobile-dark-mode-1.png | Bin ....pw.tsx_default_base-view-mobile-dark-mode-1.png | Bin ...l.pw.tsx_mobile_base-view-mobile-dark-mode-1.png | Bin .../features/dex-pools/pages/index}/Pools.pw.tsx | 3 ++- .../features/dex-pools/pages/index}/Pools.tsx | 7 ++++--- .../dex-pools/pages/index}/PoolsListItem.tsx | 7 ++++--- .../features/dex-pools/pages/index}/PoolsTable.tsx | 2 +- .../dex-pools/pages/index}/PoolsTableItem.tsx | 7 ++++--- ...pw.tsx_dark-color-mode_base-view-dark-mode-1.png | Bin .../Pools.pw.tsx_default_base-view-dark-mode-1.png | Bin .../Pools.pw.tsx_default_mobile-base-view-1.png | Bin .../pools.ts => client/features/dex-pools/stubs.ts | 0 .../features/dex-pools/types/api.ts | 0 .../features/dex-pools/utils/get-pool-links.ts | 2 +- .../features/dex-pools/utils/get-pool-title.ts | 2 +- configs/app/features/{pools.ts => dex-pools.ts} | 0 configs/app/features/index.ts | 2 +- lib/hooks/useNavItems.tsx | 2 +- nextjs/getServerSideProps/guards.ts | 2 +- pages/pools/[hash].tsx | 2 +- pages/pools/index.tsx | 2 +- ui/showcases/Link.tsx | 4 ++-- 33 files changed, 41 insertions(+), 34 deletions(-) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/PoolEntity.pw.tsx (92%) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/PoolEntity.tsx (94%) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_no-icons-dark-mode-1.png (100%) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_with-icons-dark-mode-1.png (100%) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/__screenshots__/PoolEntity.pw.tsx_default_no-icons-dark-mode-1.png (100%) rename {ui/shared/entities/pool => client/features/dex-pools/components/entity}/__screenshots__/PoolEntity.pw.tsx_default_with-icons-dark-mode-1.png (100%) rename {mocks/pools => client/features/dex-pools/mocks}/pool.ts (93%) rename {ui/pages => client/features/dex-pools/pages/details}/Pool.pw.tsx (94%) rename {ui/pages => client/features/dex-pools/pages/details}/Pool.tsx (92%) rename {ui/pool => client/features/dex-pools/pages/details}/PoolInfo.tsx (98%) rename {ui/pages => client/features/dex-pools/pages/details}/__screenshots__/Pool.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png (100%) rename {ui/pages => client/features/dex-pools/pages/details}/__screenshots__/Pool.pw.tsx_default_base-view-mobile-dark-mode-1.png (100%) rename {ui/pages => client/features/dex-pools/pages/details}/__screenshots__/Pool.pw.tsx_mobile_base-view-mobile-dark-mode-1.png (100%) rename {ui/pages => client/features/dex-pools/pages/index}/Pools.pw.tsx (95%) rename {ui/pages => client/features/dex-pools/pages/index}/Pools.tsx (93%) rename {ui/pools => client/features/dex-pools/pages/index}/PoolsListItem.tsx (92%) rename {ui/pools => client/features/dex-pools/pages/index}/PoolsTable.tsx (94%) rename {ui/pools => client/features/dex-pools/pages/index}/PoolsTableItem.tsx (92%) rename {ui/pages => client/features/dex-pools/pages/index}/__screenshots__/Pools.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/dex-pools/pages/index}/__screenshots__/Pools.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/dex-pools/pages/index}/__screenshots__/Pools.pw.tsx_default_mobile-base-view-1.png (100%) rename stubs/pools.ts => client/features/dex-pools/stubs.ts (100%) rename types/api/pools.tsx => client/features/dex-pools/types/api.ts (100%) rename lib/pools/getPoolLinks.ts => client/features/dex-pools/utils/get-pool-links.ts (86%) rename lib/pools/getPoolTitle.ts => client/features/dex-pools/utils/get-pool-title.ts (76%) rename configs/app/features/{pools.ts => dex-pools.ts} (100%) diff --git a/client/api/services/contract-info.ts b/client/api/services/contract-info.ts index 617fdc296c..546c46bca4 100644 --- a/client/api/services/contract-info.ts +++ b/client/api/services/contract-info.ts @@ -2,8 +2,8 @@ import type { ApiResource } from '../types'; import type { VerifiedAddressResponse } from 'client/features/account/types/api'; +import type { Pool, PoolsResponse } from 'client/features/dex-pools/types/api'; import type { TokenVerifiedInfo } from 'client/features/verified-tokens/types/api'; -import type { Pool, PoolsResponse } from 'types/api/pools'; export const CONTRACT_INFO_API_RESOURCES = { address_verification: { diff --git a/ui/shared/entities/pool/PoolEntity.pw.tsx b/client/features/dex-pools/components/entity/PoolEntity.pw.tsx similarity index 92% rename from ui/shared/entities/pool/PoolEntity.pw.tsx rename to client/features/dex-pools/components/entity/PoolEntity.pw.tsx index 4a4d5fad2c..e751e15bde 100644 --- a/ui/shared/entities/pool/PoolEntity.pw.tsx +++ b/client/features/dex-pools/components/entity/PoolEntity.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as poolMock from 'mocks/pools/pool'; +import * as poolMock from 'client/features/dex-pools/mocks/pool'; + import { test, expect } from 'playwright/lib'; import PoolEntity from './PoolEntity'; diff --git a/ui/shared/entities/pool/PoolEntity.tsx b/client/features/dex-pools/components/entity/PoolEntity.tsx similarity index 94% rename from ui/shared/entities/pool/PoolEntity.tsx rename to client/features/dex-pools/components/entity/PoolEntity.tsx index 2e8c2ccfda..48adda811f 100644 --- a/ui/shared/entities/pool/PoolEntity.tsx +++ b/client/features/dex-pools/components/entity/PoolEntity.tsx @@ -3,18 +3,18 @@ import { Flex, chakra } from '@chakra-ui/react'; import React from 'react'; -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; import { route } from 'nextjs-routes'; import * as TokenEntity from 'client/slices/token/components/entity/TokenEntity'; -import { getPoolTitle } from 'lib/pools/getPoolTitle'; +import { getPoolTitle } from 'client/features/dex-pools/utils/get-pool-title'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TruncatedTextTooltip } from 'toolkit/components/truncation/TruncatedTextTooltip'; import * as EntityBase from 'ui/shared/entities/base/components'; - -import { distributeEntityProps } from '../base/utils'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; type LinkProps = EntityBase.LinkBaseProps & Pick; diff --git a/ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_no-icons-dark-mode-1.png b/client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_no-icons-dark-mode-1.png similarity index 100% rename from ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_no-icons-dark-mode-1.png rename to client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_no-icons-dark-mode-1.png diff --git a/ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_with-icons-dark-mode-1.png b/client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_with-icons-dark-mode-1.png similarity index 100% rename from ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_with-icons-dark-mode-1.png rename to client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_dark-color-mode_with-icons-dark-mode-1.png diff --git a/ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_default_no-icons-dark-mode-1.png b/client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_default_no-icons-dark-mode-1.png similarity index 100% rename from ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_default_no-icons-dark-mode-1.png rename to client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_default_no-icons-dark-mode-1.png diff --git a/ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_default_with-icons-dark-mode-1.png b/client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_default_with-icons-dark-mode-1.png similarity index 100% rename from ui/shared/entities/pool/__screenshots__/PoolEntity.pw.tsx_default_with-icons-dark-mode-1.png rename to client/features/dex-pools/components/entity/__screenshots__/PoolEntity.pw.tsx_default_with-icons-dark-mode-1.png diff --git a/mocks/pools/pool.ts b/client/features/dex-pools/mocks/pool.ts similarity index 93% rename from mocks/pools/pool.ts rename to client/features/dex-pools/mocks/pool.ts index c31967da22..4249fdf71a 100644 --- a/mocks/pools/pool.ts +++ b/client/features/dex-pools/mocks/pool.ts @@ -1,4 +1,4 @@ -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; export const base: Pool = { pool_id: '0x06da0fd433c1a5d7a4faa01111c044910a184553', diff --git a/ui/pages/Pool.pw.tsx b/client/features/dex-pools/pages/details/Pool.pw.tsx similarity index 94% rename from ui/pages/Pool.pw.tsx rename to client/features/dex-pools/pages/details/Pool.pw.tsx index 5ed3f5d1b6..edf6322fbb 100644 --- a/ui/pages/Pool.pw.tsx +++ b/client/features/dex-pools/pages/details/Pool.pw.tsx @@ -2,8 +2,9 @@ import React from 'react'; import * as addressMock from 'client/slices/address/mocks/address'; +import * as poolMock from 'client/features/dex-pools/mocks/pool'; + import config from 'configs/app'; -import * as poolMock from 'mocks/pools/pool'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/pages/Pool.tsx b/client/features/dex-pools/pages/details/Pool.tsx similarity index 92% rename from ui/pages/Pool.tsx rename to client/features/dex-pools/pages/details/Pool.tsx index bdc41a4966..ea4ab135a1 100644 --- a/ui/pages/Pool.tsx +++ b/client/features/dex-pools/pages/details/Pool.tsx @@ -9,22 +9,23 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import * as addressStubs from 'client/slices/address/stubs/address'; +import * as PoolEntity from 'client/features/dex-pools/components/entity/PoolEntity'; +import PoolInfo from 'client/features/dex-pools/pages/details/PoolInfo'; +import { POOL } from 'client/features/dex-pools/stubs'; +import getPoolLinks from 'client/features/dex-pools/utils/get-pool-links'; +import { getPoolTitle } from 'client/features/dex-pools/utils/get-pool-title'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import getPoolLinks from 'lib/pools/getPoolLinks'; -import { getPoolTitle } from 'lib/pools/getPoolTitle'; -import { POOL } from 'stubs/pools'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; -import PoolInfo from 'ui/pool/PoolInfo'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import * as PoolEntity from 'ui/shared/entities/pool/PoolEntity'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import InfoButton from 'ui/shared/InfoButton'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/pool/PoolInfo.tsx b/client/features/dex-pools/pages/details/PoolInfo.tsx similarity index 98% rename from ui/pool/PoolInfo.tsx rename to client/features/dex-pools/pages/details/PoolInfo.tsx index 3acdf167cd..7d046f05f5 100644 --- a/ui/pool/PoolInfo.tsx +++ b/client/features/dex-pools/pages/details/PoolInfo.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; diff --git a/ui/pages/__screenshots__/Pool.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Pool.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png rename to client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Pool.pw.tsx_default_base-view-mobile-dark-mode-1.png b/client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_default_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Pool.pw.tsx_default_base-view-mobile-dark-mode-1.png rename to client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_default_base-view-mobile-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Pool.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_mobile_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Pool.pw.tsx_mobile_base-view-mobile-dark-mode-1.png rename to client/features/dex-pools/pages/details/__screenshots__/Pool.pw.tsx_mobile_base-view-mobile-dark-mode-1.png diff --git a/ui/pages/Pools.pw.tsx b/client/features/dex-pools/pages/index/Pools.pw.tsx similarity index 95% rename from ui/pages/Pools.pw.tsx rename to client/features/dex-pools/pages/index/Pools.pw.tsx index 19c5239ac3..fe4aba7923 100644 --- a/ui/pages/Pools.pw.tsx +++ b/client/features/dex-pools/pages/index/Pools.pw.tsx @@ -1,7 +1,8 @@ import React from 'react'; +import * as poolMock from 'client/features/dex-pools/mocks/pool'; + import config from 'configs/app'; -import * as poolMock from 'mocks/pools/pool'; import { test, expect, devices } from 'playwright/lib'; import Pools from './Pools'; diff --git a/ui/pages/Pools.tsx b/client/features/dex-pools/pages/index/Pools.tsx similarity index 93% rename from ui/pages/Pools.tsx rename to client/features/dex-pools/pages/index/Pools.tsx index bdc7c5c364..847f7c9a8e 100644 --- a/ui/pages/Pools.tsx +++ b/client/features/dex-pools/pages/index/Pools.tsx @@ -4,14 +4,15 @@ import { Box, Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; +import PoolsListItem from 'client/features/dex-pools/pages/index/PoolsListItem'; +import PoolsTable from 'client/features/dex-pools/pages/index/PoolsTable'; +import { POOL } from 'client/features/dex-pools/stubs'; + import useDebounce from 'client/shared/hooks/useDebounce'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { POOL } from 'stubs/pools'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; -import PoolsListItem from 'ui/pools/PoolsListItem'; -import PoolsTable from 'ui/pools/PoolsTable'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/pools/PoolsListItem.tsx b/client/features/dex-pools/pages/index/PoolsListItem.tsx similarity index 92% rename from ui/pools/PoolsListItem.tsx rename to client/features/dex-pools/pages/index/PoolsListItem.tsx index e13dc029e4..d36c5b9bd5 100644 --- a/ui/pools/PoolsListItem.tsx +++ b/client/features/dex-pools/pages/index/PoolsListItem.tsx @@ -2,16 +2,17 @@ import React from 'react'; -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import getPoolLinks from 'lib/pools/getPoolLinks'; +import PoolEntity from 'client/features/dex-pools/components/entity/PoolEntity'; +import getPoolLinks from 'client/features/dex-pools/utils/get-pool-links'; + import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import PoolEntity from 'ui/shared/entities/pool/PoolEntity'; import HashStringShorten from 'ui/shared/HashStringShorten'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; diff --git a/ui/pools/PoolsTable.tsx b/client/features/dex-pools/pages/index/PoolsTable.tsx similarity index 94% rename from ui/pools/PoolsTable.tsx rename to client/features/dex-pools/pages/index/PoolsTable.tsx index d09f348b8e..db06680874 100644 --- a/ui/pools/PoolsTable.tsx +++ b/client/features/dex-pools/pages/index/PoolsTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; diff --git a/ui/pools/PoolsTableItem.tsx b/client/features/dex-pools/pages/index/PoolsTableItem.tsx similarity index 92% rename from ui/pools/PoolsTableItem.tsx rename to client/features/dex-pools/pages/index/PoolsTableItem.tsx index 4bf57a5c55..055dba989c 100644 --- a/ui/pools/PoolsTableItem.tsx +++ b/client/features/dex-pools/pages/index/PoolsTableItem.tsx @@ -3,20 +3,21 @@ import { Flex, Box, Text } from '@chakra-ui/react'; import React from 'react'; -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import PoolEntity from 'client/features/dex-pools/components/entity/PoolEntity'; +import getPoolLinks from 'client/features/dex-pools/utils/get-pool-links'; + import getItemIndex from 'client/shared/lists/get-item-index'; -import getPoolLinks from 'lib/pools/getPoolLinks'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tooltip } from 'toolkit/chakra/tooltip'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import PoolEntity from 'ui/shared/entities/pool/PoolEntity'; import HashStringShorten from 'ui/shared/HashStringShorten'; type Props = { diff --git a/ui/pages/__screenshots__/Pools.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Pools.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Pools.pw.tsx_default_base-view-dark-mode-1.png b/client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Pools.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Pools.pw.tsx_default_mobile-base-view-1.png b/client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/Pools.pw.tsx_default_mobile-base-view-1.png rename to client/features/dex-pools/pages/index/__screenshots__/Pools.pw.tsx_default_mobile-base-view-1.png diff --git a/stubs/pools.ts b/client/features/dex-pools/stubs.ts similarity index 100% rename from stubs/pools.ts rename to client/features/dex-pools/stubs.ts diff --git a/types/api/pools.tsx b/client/features/dex-pools/types/api.ts similarity index 100% rename from types/api/pools.tsx rename to client/features/dex-pools/types/api.ts diff --git a/lib/pools/getPoolLinks.ts b/client/features/dex-pools/utils/get-pool-links.ts similarity index 86% rename from lib/pools/getPoolLinks.ts rename to client/features/dex-pools/utils/get-pool-links.ts index 2fe58192d6..0aad93a5f9 100644 --- a/lib/pools/getPoolLinks.ts +++ b/client/features/dex-pools/utils/get-pool-links.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; type PoolLink = { url: string; diff --git a/lib/pools/getPoolTitle.ts b/client/features/dex-pools/utils/get-pool-title.ts similarity index 76% rename from lib/pools/getPoolTitle.ts rename to client/features/dex-pools/utils/get-pool-title.ts index a3473c26d5..a8520efbe0 100644 --- a/lib/pools/getPoolTitle.ts +++ b/client/features/dex-pools/utils/get-pool-title.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { Pool } from 'types/api/pools'; +import type { Pool } from 'client/features/dex-pools/types/api'; export const getPoolTitle = (pool: Pool) => { return `${ pool.base_token_symbol } / ${ pool.quote_token_symbol } ${ pool.fee ? `(${ pool.fee }%)` : '' }`; diff --git a/configs/app/features/pools.ts b/configs/app/features/dex-pools.ts similarity index 100% rename from configs/app/features/pools.ts rename to configs/app/features/dex-pools.ts diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index e60cad0b76..5b9a95834f 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -34,7 +34,7 @@ export { default as mudFramework } from './mud-framework'; export { default as multichain } from './multichain'; export { default as multichainButton } from './multichain-button'; export { default as nameServices } from './name-services'; -export { default as pools } from './pools'; +export { default as dexPools } from './dex-pools'; export { default as publicTagsSubmission } from './public-tags-submission'; export { default as rewards } from './rewards'; export { default as rollbar } from './rollbar'; diff --git a/lib/hooks/useNavItems.tsx b/lib/hooks/useNavItems.tsx index c2b2d204c9..b3612a7d01 100644 --- a/lib/hooks/useNavItems.tsx +++ b/lib/hooks/useNavItems.tsx @@ -256,7 +256,7 @@ export default function useNavItems(): ReturnType { icon: 'navigation/token_transfers', isActive: pathname === '/token-transfers', }, - config.features.pools.isEnabled && { + config.features.dexPools.isEnabled && { text: 'DEX tracker', nextRoute: { pathname: '/pools' as const }, icon: 'navigation/dex_tracker', diff --git a/nextjs/getServerSideProps/guards.ts b/nextjs/getServerSideProps/guards.ts index d0eda9a957..e28afa7ebd 100644 --- a/nextjs/getServerSideProps/guards.ts +++ b/nextjs/getServerSideProps/guards.ts @@ -202,7 +202,7 @@ export const publicTagsSubmit: Guard = (chainConfig: typeof config) => async() = }; export const pools: Guard = (chainConfig: typeof config) => async() => { - if (!chainConfig.features.pools.isEnabled) { + if (!chainConfig.features.dexPools.isEnabled) { return { notFound: true, }; diff --git a/pages/pools/[hash].tsx b/pages/pools/[hash].tsx index 922aa54f9c..3b0989fca3 100644 --- a/pages/pools/[hash].tsx +++ b/pages/pools/[hash].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const Pool = dynamic(() => import('ui/pages/Pool'), { ssr: false }); +const Pool = dynamic(() => import('client/features/dex-pools/pages/details/Pool'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/pools/index.tsx b/pages/pools/index.tsx index 3866edbff1..ec066bb787 100644 --- a/pages/pools/index.tsx +++ b/pages/pools/index.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const Pools = dynamic(() => import('ui/pages/Pools'), { ssr: false }); +const Pools = dynamic(() => import('client/features/dex-pools/pages/index/Pools'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/ui/showcases/Link.tsx b/ui/showcases/Link.tsx index 49d2fae390..be2314a767 100644 --- a/ui/showcases/Link.tsx +++ b/ui/showcases/Link.tsx @@ -16,13 +16,13 @@ import * as txMock from 'client/slices/tx/mocks/tx'; import BlobEntity from 'client/features/data-availability/components/entity/BlobEntity'; import * as blobsMock from 'client/features/data-availability/mocks/blobs'; +import PoolEntity from 'client/features/dex-pools/components/entity/PoolEntity'; +import * as poolMock from 'client/features/dex-pools/mocks/pool'; import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import * as ensMock from 'client/features/name-services/domains/mocks/domain'; -import * as poolMock from 'mocks/pools/pool'; import { Link, LinkBox, LinkOverlay } from 'toolkit/chakra/link'; import NftEntity from 'ui/shared/entities/nft/NftEntity'; -import PoolEntity from 'ui/shared/entities/pool/PoolEntity'; import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; From 5de5ea3b85caf3b7ba2277522b9e3610abf8d474 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 11:53:02 +0200 Subject: [PATCH 09/85] =?UTF-8?q?track:=206-11=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3468?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 7965576c4e..840eb5c51b 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -233,7 +233,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-10 · [~] Feature: `rewards` · [#3462](https://github.com/blockscout/frontend/issues/3462) **Scope:** Includes `lib/contexts/rewards.tsx`, `lib/hooks/useRewardsActivity` → `client/features/rewards/` -### 6-11 · [ ] Feature: `advanced-filter` +### 6-11 · [~] Feature: `advanced-filter` · [#3468](https://github.com/blockscout/frontend/issues/3468) ### 6-12 · [ ] Feature: `ad-banner` **Scope:** Includes `lib/hooks/useAdblockDetect` → `client/features/ad-banner/`. Covers both `adsBanner.ts` and `adsText.ts` configs. From 17efa6d616ba2b578e5225017a5da89a345183a7 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 14:50:29 +0200 Subject: [PATCH 10/85] exclude types packages from minimum release age rule --- pnpm-workspace.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fdf45608ea..bd7218f27c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -14,4 +14,13 @@ onlyBuiltDependencies: - '@ipshipyard/node-datachannel' - 'sharp' -minimumReleaseAge: 10080 # 1 week \ No newline at end of file +minimumReleaseAge: 10080 # 1 week +minimumReleaseAgeExclude: + - '@blockscout/bens-types' + - '@blockscout/interchain-indexer-types' + - '@blockscout/multichain-aggregator-types' + - '@blockscout/points-types' + - '@blockscout/stats-types' + - '@blockscout/tac-operation-lifecycle-types' + - '@blockscout/visualizer-types' + - '@blockscout/zetachain-cctx-types' \ No newline at end of file From 7df8fbf850d2e3427889aa54509833805d606c96 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 14:50:55 +0200 Subject: [PATCH 11/85] [Migration 6-9] Feature: marketplace (#3469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: migrate marketplace files to client/features/marketplace/ - types/client/marketplace.ts → client/features/marketplace/types/client.ts - stubs/marketplace.ts → client/features/marketplace/stubs.ts - mocks/apps/ → client/features/marketplace/mocks/ - mocks/essentialDapps/ → client/features/marketplace/mocks/ - lib/contexts/marketplace.tsx → client/features/marketplace/context.tsx - lib/hooks/useGraphLinks.tsx → client/features/marketplace/hooks/useGraphLinks.tsx - ui/marketplace/** → client/features/marketplace/ (hooks, components, pages) - ui/pages/Marketplace* → client/features/marketplace/pages/ Split ui/marketplace/utils.ts into: - client/features/marketplace/utils/sort.ts (SortValue, SORT_OPTIONS) - client/features/marketplace/utils/dapp.ts (getAppUrl) Updated all import paths across the repo including: - pages/apps/, pages/essential-dapps/ (dynamic imports) - client/api/services/admin.ts - client/slices/search/ files - client/features/account/ user-profile files - playwright/TestApp.tsx, pages/_app.tsx - global.d.ts, types/, configs/ files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * fix: update app.html path in MarketplaceApp.pw.tsx after migration Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/53f597bb-92a8-42d4-a522-72124b0667b8 Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- client/api/services/admin.ts | 2 +- .../user-profile/auth0/UserProfileButton.tsx | 2 +- .../user-profile/auth0/UserProfileContent.tsx | 2 +- .../user-profile/dynamic/UserProfile.tsx | 3 +-- .../dynamic/UserProfileButton.tsx | 2 +- .../user-profile/wallet/UserWalletDesktop.tsx | 2 +- .../user-profile/wallet/UserWalletMobile.tsx | 2 +- .../components}/MarketplaceAppIframe.tsx | 2 +- .../components/SearchBarSuggestApp.tsx | 2 +- .../components/rating/MarketplaceRating.tsx | 8 +++---- .../MarketplaceRatingPopoverContent.tsx | 4 ++-- .../rating/MarketplaceRatingTriggerButton.tsx | 0 .../features/marketplace/context.tsx | 0 .../hooks}/useAutoConnectWallet.tsx | 0 .../marketplace}/hooks/useGraphLinks.tsx | 0 .../marketplace/hooks}/useMarketplace.tsx | 2 +- .../marketplace/hooks}/useMarketplaceApps.tsx | 8 +++---- .../hooks}/useMarketplaceCategories.tsx | 5 +++-- .../hooks}/useMarketplaceWallet.tsx | 0 .../features/marketplace/mocks/dapps.html | 0 .../features/marketplace/mocks/dapps.ts | 0 .../features/marketplace/mocks/revoke-dapp.ts | 0 .../pages/dapp}/MarketplaceApp.pw.tsx | 5 +++-- .../pages/dapp}/MarketplaceApp.tsx | 12 +++++----- .../pages/dapp}/MarketplaceAppTopBar.tsx | 6 ++--- ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...p.pw.tsx_default_base-view-dark-mode-1.png | Bin ...eApp.pw.tsx_default_mobile-base-view-1.png | Bin .../marketplace/pages/dapp/info}/Content.tsx | 2 +- .../dapp/info}/MarketplaceAppInfo.pw.tsx | 3 ++- .../pages/dapp/info}/MarketplaceAppInfo.tsx | 4 ++-- .../pages/dapp/info}/SocialLink.tsx | 2 +- .../pages/dapp/info}/WebsiteLink.tsx | 0 ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...o.pw.tsx_default_base-view-dark-mode-1.png | Bin ...Info.pw.tsx_default_mobile-base-view-1.png | Bin .../MarketplaceEssentialDapp.tsx | 11 ++++----- .../multisend/Multisend.pw.tsx | 0 .../essential-dapp}/multisend/Multisend.tsx | 0 ...olor-mode_base-view-dark-mode-mobile-1.png | Bin ...x_default_base-view-dark-mode-mobile-1.png | Bin ...sx_mobile_base-view-dark-mode-mobile-1.png | Bin .../essential-dapp}/revoke/Revoke.pw.tsx | 0 .../pages/essential-dapp}/revoke/Revoke.tsx | 0 ...olor-mode_base-view-dark-mode-mobile-1.png | Bin ...x_default_base-view-dark-mode-mobile-1.png | Bin ...sx_mobile_base-view-dark-mode-mobile-1.png | Bin .../revoke/components/AddressEntity.tsx | 0 .../revoke/components/Approvals.tsx | 2 +- .../revoke/components/ApprovalsListItem.tsx | 2 +- .../revoke/components/ApprovalsTable.tsx | 2 +- .../revoke/components/ApprovalsTableItem.tsx | 2 +- .../revoke/components/ChainSelect.tsx | 0 .../revoke/components/Content.pw.tsx | 3 ++- .../revoke/components/Content.tsx | 2 +- .../revoke/components/SearchInput.tsx | 0 .../revoke/components/StartScreen.tsx | 0 ...olor-mode_base-view-dark-mode-mobile-1.png | Bin ...x_default_base-view-dark-mode-mobile-1.png | Bin ...sx_mobile_base-view-dark-mode-mobile-1.png | Bin .../revoke/hooks/useApprovalsQuery.tsx | 2 +- .../revoke/hooks/useCoinBalanceQuery.tsx | 2 +- .../revoke/hooks/useGetBlockTimestamp.tsx | 2 +- .../revoke/hooks/useRevoke.tsx | 2 +- .../revoke/hooks/useSearchErc20Allowances.tsx | 2 +- .../revoke/hooks/useSearchNftAllowances.tsx | 2 +- .../revoke/lib/createPublicClient.ts | 0 .../revoke/lib/formatAllowance.ts | 0 .../essential-dapp}/revoke/lib/getLogs.ts | 0 .../pages/essential-dapp}/swap/Swap.tsx | 3 ++- .../pages/index}/EmptySearchResult.tsx | 2 +- .../marketplace/pages/index}/FavoriteIcon.tsx | 0 .../pages/index}/Marketplace.pw.tsx | 3 ++- .../marketplace/pages/index}/Marketplace.tsx | 21 +++++++++--------- .../pages/index}/MarketplaceAppCard.tsx | 4 ++-- .../pages/index}/MarketplaceAppCardLink.tsx | 0 .../pages/index}/MarketplaceAppGraphLinks.tsx | 0 .../index}/MarketplaceAppIntegrationIcon.tsx | 0 .../pages/index}/MarketplaceAppModal.pw.tsx | 3 ++- .../pages/index}/MarketplaceAppModal.tsx | 4 ++-- .../index}/MarketplaceDisclaimerModal.tsx | 0 .../pages/index}/MarketplaceList.tsx | 2 +- ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...ark-color-mode_with-banner-dark-mode-1.png | Bin ...-mode_with-essential-dapps-dark-mode-1.png | Bin ...lor-mode_with-featured-app-dark-mode-1.png | Bin ...e.pw.tsx_default_base-view-dark-mode-1.png | Bin ...lace.pw.tsx_default_mobile-base-view-1.png | Bin ...ce.pw.tsx_default_mobile-with-banner-1.png | Bin ..._default_mobile-with-essential-dapps-1.png | Bin ...tsx_default_mobile-with-featured-app-1.png | Bin ...pw.tsx_default_with-banner-dark-mode-1.png | Bin ...fault_with-essential-dapps-dark-mode-1.png | Bin ..._default_with-featured-app-dark-mode-1.png | Bin ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...l.pw.tsx_default_base-view-dark-mode-1.png | Bin ...odal.pw.tsx_default_mobile-base-view-1.png | Bin .../pages/index/banner}/Banner.tsx | 9 ++++---- .../pages/index/banner}/FeaturedApp.tsx | 2 +- .../pages/index/banner}/FeaturedAppMobile.tsx | 2 +- .../pages/index/banner}/IframeBanner.tsx | 0 .../essential-dapps}/EssentialDappCard.tsx | 0 .../essential-dapps}/EssentialDappsList.tsx | 2 +- .../features/marketplace/stubs.ts | 2 +- .../features/marketplace/types/client.ts | 0 .../features/marketplace/utils/dapp.spec.ts | 2 +- .../features/marketplace/utils/dapp.ts | 13 ----------- client/features/marketplace/utils/sort.ts | 14 ++++++++++++ .../search-bar/SearchBarDesktop.pw.tsx | 2 +- .../search-bar/SearchBarMobile.pw.tsx | 2 +- .../SearchBarSuggest/SearchBarSuggest.tsx | 2 +- .../pages/search-results/SearchResults.pw.tsx | 2 +- .../pages/search-results/SearchResults.tsx | 2 +- .../slices/search/utils/search-categories.ts | 2 +- configs/app/features/marketplace.ts | 2 +- configs/essential-dapps-chains/index.ts | 2 +- .../schemas/features/marketplace.ts | 2 +- .../index.ts | 4 ++-- global.d.ts | 2 +- pages/_app.tsx | 2 +- pages/apps/[id].tsx | 4 ++-- pages/apps/index.tsx | 2 +- pages/essential-dapps/[id].tsx | 2 +- playwright/TestApp.tsx | 2 +- types/externalChains.ts | 2 +- 125 files changed, 126 insertions(+), 116 deletions(-) rename {ui/marketplace => client/features/marketplace/components}/MarketplaceAppIframe.tsx (98%) rename ui/marketplace/Rating/Rating.tsx => client/features/marketplace/components/rating/MarketplaceRating.tsx (92%) rename ui/marketplace/Rating/PopoverContent.tsx => client/features/marketplace/components/rating/MarketplaceRatingPopoverContent.tsx (95%) rename ui/marketplace/Rating/TriggerButton.tsx => client/features/marketplace/components/rating/MarketplaceRatingTriggerButton.tsx (100%) rename lib/contexts/marketplace.tsx => client/features/marketplace/context.tsx (100%) rename {ui/marketplace => client/features/marketplace/hooks}/useAutoConnectWallet.tsx (100%) rename {lib => client/features/marketplace}/hooks/useGraphLinks.tsx (100%) rename {ui/marketplace => client/features/marketplace/hooks}/useMarketplace.tsx (98%) rename {ui/marketplace => client/features/marketplace/hooks}/useMarketplaceApps.tsx (94%) rename {ui/marketplace => client/features/marketplace/hooks}/useMarketplaceCategories.tsx (93%) rename {ui/marketplace => client/features/marketplace/hooks}/useMarketplaceWallet.tsx (100%) rename mocks/apps/app.html => client/features/marketplace/mocks/dapps.html (100%) rename mocks/apps/apps.ts => client/features/marketplace/mocks/dapps.ts (100%) rename mocks/essentialDapps/revoke.ts => client/features/marketplace/mocks/revoke-dapp.ts (100%) rename {ui/pages => client/features/marketplace/pages/dapp}/MarketplaceApp.pw.tsx (87%) rename {ui/pages => client/features/marketplace/pages/dapp}/MarketplaceApp.tsx (89%) rename {ui/marketplace => client/features/marketplace/pages/dapp}/MarketplaceAppTopBar.tsx (92%) rename {ui/pages => client/features/marketplace/pages/dapp}/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/dapp}/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/dapp}/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png (100%) rename {ui/marketplace/MarketplaceAppInfo => client/features/marketplace/pages/dapp/info}/Content.tsx (95%) rename {ui/marketplace => client/features/marketplace/pages/dapp/info}/MarketplaceAppInfo.pw.tsx (90%) rename {ui/marketplace => client/features/marketplace/pages/dapp/info}/MarketplaceAppInfo.tsx (78%) rename {ui/marketplace/MarketplaceAppInfo => client/features/marketplace/pages/dapp/info}/SocialLink.tsx (88%) rename {ui/marketplace/MarketplaceAppInfo => client/features/marketplace/pages/dapp/info}/WebsiteLink.tsx (100%) rename {ui/marketplace => client/features/marketplace/pages/dapp/info}/__screenshots__/MarketplaceAppInfo.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/dapp/info}/__screenshots__/MarketplaceAppInfo.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/dapp/info}/__screenshots__/MarketplaceAppInfo.pw.tsx_default_mobile-base-view-1.png (100%) rename {ui/pages => client/features/marketplace/pages/essential-dapp}/MarketplaceEssentialDapp.tsx (79%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/multisend/Multisend.pw.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/multisend/Multisend.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/multisend/__screenshots__/Multisend.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/multisend/__screenshots__/Multisend.pw.tsx_default_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/multisend/__screenshots__/Multisend.pw.tsx_mobile_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/Revoke.pw.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/Revoke.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/__screenshots__/Revoke.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/__screenshots__/Revoke.pw.tsx_default_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/__screenshots__/Revoke.pw.tsx_mobile_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/AddressEntity.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/Approvals.tsx (94%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/ApprovalsListItem.tsx (98%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/ApprovalsTable.tsx (95%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/ApprovalsTableItem.tsx (97%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/ChainSelect.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/Content.pw.tsx (93%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/Content.tsx (98%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/SearchInput.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/StartScreen.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useApprovalsQuery.tsx (96%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useCoinBalanceQuery.tsx (95%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useGetBlockTimestamp.tsx (94%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useRevoke.tsx (97%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useSearchErc20Allowances.tsx (98%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/hooks/useSearchNftAllowances.tsx (98%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/lib/createPublicClient.ts (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/lib/formatAllowance.ts (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/revoke/lib/getLogs.ts (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/essential-dapp}/swap/Swap.tsx (96%) rename {ui/marketplace => client/features/marketplace/pages/index}/EmptySearchResult.tsx (94%) rename {ui/marketplace => client/features/marketplace/pages/index}/FavoriteIcon.tsx (100%) rename {ui/pages => client/features/marketplace/pages/index}/Marketplace.pw.tsx (98%) rename {ui/pages => client/features/marketplace/pages/index}/Marketplace.tsx (92%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppCard.tsx (97%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppCardLink.tsx (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppGraphLinks.tsx (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppIntegrationIcon.tsx (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppModal.pw.tsx (92%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceAppModal.tsx (98%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceDisclaimerModal.tsx (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/MarketplaceList.tsx (97%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png (100%) rename {ui/pages => client/features/marketplace/pages/index}/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/index}/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png (100%) rename {ui/marketplace => client/features/marketplace/pages/index/banner}/Banner.tsx (88%) rename {ui/marketplace/Banner => client/features/marketplace/pages/index/banner}/FeaturedApp.tsx (98%) rename {ui/marketplace/Banner => client/features/marketplace/pages/index/banner}/FeaturedAppMobile.tsx (98%) rename {ui/marketplace/Banner => client/features/marketplace/pages/index/banner}/IframeBanner.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/index/essential-dapps}/EssentialDappCard.tsx (100%) rename {ui/marketplace/essentialDapps => client/features/marketplace/pages/index/essential-dapps}/EssentialDappsList.tsx (95%) rename stubs/marketplace.ts => client/features/marketplace/stubs.ts (91%) rename types/client/marketplace.ts => client/features/marketplace/types/client.ts (100%) rename ui/marketplace/utils.spec.ts => client/features/marketplace/utils/dapp.spec.ts (98%) rename ui/marketplace/utils.ts => client/features/marketplace/utils/dapp.ts (76%) create mode 100644 client/features/marketplace/utils/sort.ts diff --git a/client/api/services/admin.ts b/client/api/services/admin.ts index c35a2f2e62..0cc7ca3db4 100644 --- a/client/api/services/admin.ts +++ b/client/api/services/admin.ts @@ -2,7 +2,7 @@ import type { ApiResource } from '../types'; import type { TokenInfoApplicationConfig, TokenInfoApplications } from 'client/features/account/types/api'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; export const ADMIN_API_RESOURCES = { public_tag_application: { diff --git a/client/features/account/components/user-profile/auth0/UserProfileButton.tsx b/client/features/account/components/user-profile/auth0/UserProfileButton.tsx index 85dbf5a5fc..0fbfcb7b36 100644 --- a/client/features/account/components/user-profile/auth0/UserProfileButton.tsx +++ b/client/features/account/components/user-profile/auth0/UserProfileButton.tsx @@ -9,11 +9,11 @@ import type { UserInfo } from 'client/features/account/types/api'; import { getUserHandle } from 'client/features/account/utils/user-handle'; import useWeb3AccountWithDomain from 'client/features/connect-wallet/hooks/useAccountWithDomain'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import shortenString from 'client/shared/text/shorten-string'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { Button } from 'toolkit/chakra/button'; import { Tooltip } from 'toolkit/chakra/tooltip'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/features/account/components/user-profile/auth0/UserProfileContent.tsx b/client/features/account/components/user-profile/auth0/UserProfileContent.tsx index 7360827fa2..8ef172e39d 100644 --- a/client/features/account/components/user-profile/auth0/UserProfileContent.tsx +++ b/client/features/account/components/user-profile/auth0/UserProfileContent.tsx @@ -9,11 +9,11 @@ import type { UserInfo } from 'client/features/account/types/api'; import { route } from 'nextjs-routes'; import useLogout from 'client/features/account/hooks/useLogout'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; import shortenString from 'client/shared/text/shorten-string'; import config from 'configs/app'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { Hint } from 'toolkit/components/Hint/Hint'; diff --git a/client/features/account/components/user-profile/dynamic/UserProfile.tsx b/client/features/account/components/user-profile/dynamic/UserProfile.tsx index 61cbd2d399..a2d9e064b7 100644 --- a/client/features/account/components/user-profile/dynamic/UserProfile.tsx +++ b/client/features/account/components/user-profile/dynamic/UserProfile.tsx @@ -5,8 +5,7 @@ import { DynamicConnectButton, DynamicUserProfile, useDynamicContext, useIsLogge import React from 'react'; import useWalletDynamic from 'client/features/connect-wallet/hooks/wallet/useWalletDynamic'; - -import { useMarketplaceContext } from 'lib/contexts/marketplace'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; import UserProfileButton from './UserProfileButton'; import UserProfilePopover from './UserProfilePopover'; diff --git a/client/features/account/components/user-profile/dynamic/UserProfileButton.tsx b/client/features/account/components/user-profile/dynamic/UserProfileButton.tsx index 4309167381..0f084045d8 100644 --- a/client/features/account/components/user-profile/dynamic/UserProfileButton.tsx +++ b/client/features/account/components/user-profile/dynamic/UserProfileButton.tsx @@ -5,10 +5,10 @@ import React from 'react'; import { getUserHandle } from 'client/features/account/utils/user-handle'; import useAccountWithDomain from 'client/features/connect-wallet/hooks/useAccountWithDomain'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; import shortenString from 'client/shared/text/shorten-string'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { Button, type ButtonProps } from 'toolkit/chakra/button'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/features/account/components/user-profile/wallet/UserWalletDesktop.tsx b/client/features/account/components/user-profile/wallet/UserWalletDesktop.tsx index 59aa1d479d..4086c6de02 100644 --- a/client/features/account/components/user-profile/wallet/UserWalletDesktop.tsx +++ b/client/features/account/components/user-profile/wallet/UserWalletDesktop.tsx @@ -5,8 +5,8 @@ import React from 'react'; import useWeb3AccountWithDomain from 'client/features/connect-wallet/hooks/useAccountWithDomain'; import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/client/features/account/components/user-profile/wallet/UserWalletMobile.tsx b/client/features/account/components/user-profile/wallet/UserWalletMobile.tsx index f90c7bda09..9837b5592d 100644 --- a/client/features/account/components/user-profile/wallet/UserWalletMobile.tsx +++ b/client/features/account/components/user-profile/wallet/UserWalletMobile.tsx @@ -4,8 +4,8 @@ import React from 'react'; import useWeb3AccountWithDomain from 'client/features/connect-wallet/hooks/useAccountWithDomain'; import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { DrawerTrigger, DrawerRoot, DrawerContent, DrawerBody } from 'toolkit/chakra/drawer'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/ui/marketplace/MarketplaceAppIframe.tsx b/client/features/marketplace/components/MarketplaceAppIframe.tsx similarity index 98% rename from ui/marketplace/MarketplaceAppIframe.tsx rename to client/features/marketplace/components/MarketplaceAppIframe.tsx index 713c3ded6e..19dc2fa2db 100644 --- a/ui/marketplace/MarketplaceAppIframe.tsx +++ b/client/features/marketplace/components/MarketplaceAppIframe.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; -import useMarketplaceWallet from '../marketplace/useMarketplaceWallet'; +import useMarketplaceWallet from '../hooks/useMarketplaceWallet'; const IFRAME_SANDBOX_ATTRIBUTE = 'allow-forms allow-orientation-lock ' + 'allow-pointer-lock allow-popups-to-escape-sandbox ' + diff --git a/client/features/marketplace/components/SearchBarSuggestApp.tsx b/client/features/marketplace/components/SearchBarSuggestApp.tsx index e63c123e6a..1e0f368b97 100644 --- a/client/features/marketplace/components/SearchBarSuggestApp.tsx +++ b/client/features/marketplace/components/SearchBarSuggestApp.tsx @@ -4,7 +4,7 @@ import { Flex, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { route } from 'nextjs-routes'; diff --git a/ui/marketplace/Rating/Rating.tsx b/client/features/marketplace/components/rating/MarketplaceRating.tsx similarity index 92% rename from ui/marketplace/Rating/Rating.tsx rename to client/features/marketplace/components/rating/MarketplaceRating.tsx index 23b4b42f90..5ff16eb441 100644 --- a/ui/marketplace/Rating/Rating.tsx +++ b/client/features/marketplace/components/rating/MarketplaceRating.tsx @@ -13,8 +13,8 @@ import { PopoverBody, PopoverContent, PopoverRoot } from 'toolkit/chakra/popover import { Rating as RatingComponent } from 'toolkit/chakra/rating'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import Content from './PopoverContent'; -import TriggerButton from './TriggerButton'; +import Content from './MarketplaceRatingPopoverContent'; +import TriggerButton from './MarketplaceRatingTriggerButton'; const feature = config.features.marketplace; const isEnabled = feature.isEnabled && 'api' in feature; @@ -30,7 +30,7 @@ type Props = { popoverContentProps?: PopoverContentProps; }; -const Rating = ({ +const MarketplaceRating = ({ appId, rating, ratingsTotalCount, userRating, isLoading, fullView, source, popoverContentProps, }: Props) => { @@ -78,4 +78,4 @@ const Rating = ({ ); }; -export default Rating; +export default MarketplaceRating; diff --git a/ui/marketplace/Rating/PopoverContent.tsx b/client/features/marketplace/components/rating/MarketplaceRatingPopoverContent.tsx similarity index 95% rename from ui/marketplace/Rating/PopoverContent.tsx rename to client/features/marketplace/components/rating/MarketplaceRatingPopoverContent.tsx index 385b295cf5..2fd29f4fa6 100644 --- a/ui/marketplace/Rating/PopoverContent.tsx +++ b/client/features/marketplace/components/rating/MarketplaceRatingPopoverContent.tsx @@ -22,7 +22,7 @@ type Props = { source: EventPayload['Source']; }; -const PopoverContent = ({ appId, userRating, source }: Props) => { +const MarketplaceRatingPopoverContent = ({ appId, userRating, source }: Props) => { const apiFetch = useApiFetch(); const [ isSending, setIsSending ] = React.useState(false); const [ ratingValue, setRatingValue ] = React.useState(userRating); @@ -92,4 +92,4 @@ const PopoverContent = ({ appId, userRating, source }: Props) => { ); }; -export default PopoverContent; +export default MarketplaceRatingPopoverContent; diff --git a/ui/marketplace/Rating/TriggerButton.tsx b/client/features/marketplace/components/rating/MarketplaceRatingTriggerButton.tsx similarity index 100% rename from ui/marketplace/Rating/TriggerButton.tsx rename to client/features/marketplace/components/rating/MarketplaceRatingTriggerButton.tsx diff --git a/lib/contexts/marketplace.tsx b/client/features/marketplace/context.tsx similarity index 100% rename from lib/contexts/marketplace.tsx rename to client/features/marketplace/context.tsx diff --git a/ui/marketplace/useAutoConnectWallet.tsx b/client/features/marketplace/hooks/useAutoConnectWallet.tsx similarity index 100% rename from ui/marketplace/useAutoConnectWallet.tsx rename to client/features/marketplace/hooks/useAutoConnectWallet.tsx diff --git a/lib/hooks/useGraphLinks.tsx b/client/features/marketplace/hooks/useGraphLinks.tsx similarity index 100% rename from lib/hooks/useGraphLinks.tsx rename to client/features/marketplace/hooks/useGraphLinks.tsx diff --git a/ui/marketplace/useMarketplace.tsx b/client/features/marketplace/hooks/useMarketplace.tsx similarity index 98% rename from ui/marketplace/useMarketplace.tsx rename to client/features/marketplace/hooks/useMarketplace.tsx index aa629a06ee..7c64e48e56 100644 --- a/ui/marketplace/useMarketplace.tsx +++ b/client/features/marketplace/hooks/useMarketplace.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; -import { MarketplaceCategory } from 'types/client/marketplace'; +import { MarketplaceCategory } from 'client/features/marketplace/types/client'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useDebounce from 'client/shared/hooks/useDebounce'; diff --git a/ui/marketplace/useMarketplaceApps.tsx b/client/features/marketplace/hooks/useMarketplaceApps.tsx similarity index 94% rename from ui/marketplace/useMarketplaceApps.tsx rename to client/features/marketplace/hooks/useMarketplaceApps.tsx index 0d4cee1629..d8ad3d293e 100644 --- a/ui/marketplace/useMarketplaceApps.tsx +++ b/client/features/marketplace/hooks/useMarketplaceApps.tsx @@ -3,19 +3,19 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; -import { MarketplaceCategory } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; +import { MarketplaceCategory } from 'client/features/marketplace/types/client'; import useApiFetch from 'client/api/hooks/useApiFetch'; import useFetch from 'client/api/hooks/useFetch'; import type { ResourceError } from 'client/api/resources'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; +import { MARKETPLACE_APP } from 'client/features/marketplace/stubs'; import config from 'configs/app'; -import { MARKETPLACE_APP } from 'stubs/marketplace'; -import type { SortValue } from './utils'; +import type { SortValue } from '../utils/sort'; const feature = config.features.marketplace; diff --git a/ui/marketplace/useMarketplaceCategories.tsx b/client/features/marketplace/hooks/useMarketplaceCategories.tsx similarity index 93% rename from ui/marketplace/useMarketplaceCategories.tsx rename to client/features/marketplace/hooks/useMarketplaceCategories.tsx index f5f2b6f94d..ac66ae2be0 100644 --- a/ui/marketplace/useMarketplaceCategories.tsx +++ b/client/features/marketplace/hooks/useMarketplaceCategories.tsx @@ -3,13 +3,14 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import useApiFetch from 'client/api/hooks/useFetch'; import type { ResourceError } from 'client/api/resources'; +import { CATEGORIES } from 'client/features/marketplace/stubs'; + import config from 'configs/app'; -import { CATEGORIES } from 'stubs/marketplace'; const feature = config.features.marketplace; const categoriesUrl = (feature.isEnabled && feature.categoriesUrl) || ''; diff --git a/ui/marketplace/useMarketplaceWallet.tsx b/client/features/marketplace/hooks/useMarketplaceWallet.tsx similarity index 100% rename from ui/marketplace/useMarketplaceWallet.tsx rename to client/features/marketplace/hooks/useMarketplaceWallet.tsx diff --git a/mocks/apps/app.html b/client/features/marketplace/mocks/dapps.html similarity index 100% rename from mocks/apps/app.html rename to client/features/marketplace/mocks/dapps.html diff --git a/mocks/apps/apps.ts b/client/features/marketplace/mocks/dapps.ts similarity index 100% rename from mocks/apps/apps.ts rename to client/features/marketplace/mocks/dapps.ts diff --git a/mocks/essentialDapps/revoke.ts b/client/features/marketplace/mocks/revoke-dapp.ts similarity index 100% rename from mocks/essentialDapps/revoke.ts rename to client/features/marketplace/mocks/revoke-dapp.ts diff --git a/ui/pages/MarketplaceApp.pw.tsx b/client/features/marketplace/pages/dapp/MarketplaceApp.pw.tsx similarity index 87% rename from ui/pages/MarketplaceApp.pw.tsx rename to client/features/marketplace/pages/dapp/MarketplaceApp.pw.tsx index f6962473ce..a9e01d3452 100644 --- a/ui/pages/MarketplaceApp.pw.tsx +++ b/client/features/marketplace/pages/dapp/MarketplaceApp.pw.tsx @@ -2,8 +2,9 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; import { numberToHex } from 'viem'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; + import config from 'configs/app'; -import { apps as appsMock } from 'mocks/apps/apps'; import type { TestFnArgs } from 'playwright/lib'; import { test, expect, devices } from 'playwright/lib'; @@ -21,7 +22,7 @@ const testFn = async({ render, mockAssetResponse, mockEnvs, mockRpcResponse, moc [ 'NEXT_PUBLIC_MARKETPLACE_ENABLED', 'true' ], ]); await mockApiResponse('admin:marketplace_dapp', appsMock[0], { pathParams: { instanceId: config.apis.admin?.instanceId, dappId: appsMock[0].id } }); - await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html'); + await mockAssetResponse(appsMock[0].url, './client/features/marketplace/mocks/dapps.html'); await mockRpcResponse([ { Method: 'eth_chainId', ReturnType: numberToHex(Number(config.chain.id)), diff --git a/ui/pages/MarketplaceApp.tsx b/client/features/marketplace/pages/dapp/MarketplaceApp.tsx similarity index 89% rename from ui/pages/MarketplaceApp.tsx rename to client/features/marketplace/pages/dapp/MarketplaceApp.tsx index ac7e282c34..aa2df8f090 100644 --- a/ui/pages/MarketplaceApp.tsx +++ b/client/features/marketplace/pages/dapp/MarketplaceApp.tsx @@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React, { useEffect, useMemo } from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { route } from 'nextjs-routes'; @@ -14,19 +14,19 @@ import useFetch from 'client/api/hooks/useFetch'; import type { ResourceError } from 'client/api/resources'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; +import { useMarketplaceContext } from 'client/features/marketplace/context'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import * as metadata from 'client/shared/metadata'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMarketplaceContext } from 'lib/contexts/marketplace'; import { useColorMode } from 'toolkit/chakra/color-mode'; -import MarketplaceAppIframe from '../marketplace/MarketplaceAppIframe'; -import MarketplaceAppTopBar from '../marketplace/MarketplaceAppTopBar'; -import useAutoConnectWallet from '../marketplace/useAutoConnectWallet'; -import { getAppUrl } from '../marketplace/utils'; +import MarketplaceAppIframe from '../../components/MarketplaceAppIframe'; +import useAutoConnectWallet from '../../hooks/useAutoConnectWallet'; +import { getAppUrl } from '../../utils/dapp'; +import MarketplaceAppTopBar from './MarketplaceAppTopBar'; const feature = config.features.marketplace; diff --git a/ui/marketplace/MarketplaceAppTopBar.tsx b/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx similarity index 92% rename from ui/marketplace/MarketplaceAppTopBar.tsx rename to client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx index 66dec4fb24..c8010c4119 100644 --- a/ui/marketplace/MarketplaceAppTopBar.tsx +++ b/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx @@ -3,7 +3,7 @@ import { chakra, Flex } from '@chakra-ui/react'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { route } from 'nextjs-routes'; @@ -20,8 +20,8 @@ import { BackToButton } from 'toolkit/components/buttons/BackToButton'; import { makePrettyLink } from 'toolkit/utils/url'; import NetworkIcon from 'ui/snippets/networkLogo/NetworkIcon'; -import MarketplaceAppInfo from './MarketplaceAppInfo'; -import Rating from './Rating/Rating'; +import Rating from '../../components/rating/MarketplaceRating'; +import MarketplaceAppInfo from './info/MarketplaceAppInfo'; type Props = { appId: string; diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png b/client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png b/client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png rename to client/features/marketplace/pages/dapp/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/marketplace/MarketplaceAppInfo/Content.tsx b/client/features/marketplace/pages/dapp/info/Content.tsx similarity index 95% rename from ui/marketplace/MarketplaceAppInfo/Content.tsx rename to client/features/marketplace/pages/dapp/info/Content.tsx index d6d83eec42..a93f1b4b91 100644 --- a/ui/marketplace/MarketplaceAppInfo/Content.tsx +++ b/client/features/marketplace/pages/dapp/info/Content.tsx @@ -3,7 +3,7 @@ import { Flex, Text, Grid } from '@chakra-ui/react'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import SocialLink from './SocialLink'; import type { Props as SocialLinkProps } from './SocialLink'; diff --git a/ui/marketplace/MarketplaceAppInfo.pw.tsx b/client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.pw.tsx similarity index 90% rename from ui/marketplace/MarketplaceAppInfo.pw.tsx rename to client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.pw.tsx index 1d15158a03..531484e016 100644 --- a/ui/marketplace/MarketplaceAppInfo.pw.tsx +++ b/client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { apps as appsMock } from 'mocks/apps/apps'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; + import { test, expect, devices } from 'playwright/lib'; import MarketplaceAppInfo from './MarketplaceAppInfo'; diff --git a/ui/marketplace/MarketplaceAppInfo.tsx b/client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.tsx similarity index 78% rename from ui/marketplace/MarketplaceAppInfo.tsx rename to client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.tsx index 10ae09c8c1..d19617ba30 100644 --- a/ui/marketplace/MarketplaceAppInfo.tsx +++ b/client/features/marketplace/pages/dapp/info/MarketplaceAppInfo.tsx @@ -2,11 +2,11 @@ import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import InfoButton from 'ui/shared/InfoButton'; -import Content from './MarketplaceAppInfo/Content'; +import Content from './Content'; interface Props { data: MarketplaceApp | undefined; diff --git a/ui/marketplace/MarketplaceAppInfo/SocialLink.tsx b/client/features/marketplace/pages/dapp/info/SocialLink.tsx similarity index 88% rename from ui/marketplace/MarketplaceAppInfo/SocialLink.tsx rename to client/features/marketplace/pages/dapp/info/SocialLink.tsx index 09a7548ef1..174264e11a 100644 --- a/ui/marketplace/MarketplaceAppInfo/SocialLink.tsx +++ b/client/features/marketplace/pages/dapp/info/SocialLink.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { MarketplaceAppSocialInfo } from 'types/client/marketplace'; +import type { MarketplaceAppSocialInfo } from 'client/features/marketplace/types/client'; import { Link } from 'toolkit/chakra/link'; import type { IconName } from 'ui/shared/IconSvg'; diff --git a/ui/marketplace/MarketplaceAppInfo/WebsiteLink.tsx b/client/features/marketplace/pages/dapp/info/WebsiteLink.tsx similarity index 100% rename from ui/marketplace/MarketplaceAppInfo/WebsiteLink.tsx rename to client/features/marketplace/pages/dapp/info/WebsiteLink.tsx diff --git a/ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_default_base-view-dark-mode-1.png b/client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_default_mobile-base-view-1.png b/client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppInfo.pw.tsx_default_mobile-base-view-1.png rename to client/features/marketplace/pages/dapp/info/__screenshots__/MarketplaceAppInfo.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/pages/MarketplaceEssentialDapp.tsx b/client/features/marketplace/pages/essential-dapp/MarketplaceEssentialDapp.tsx similarity index 79% rename from ui/pages/MarketplaceEssentialDapp.tsx rename to client/features/marketplace/pages/essential-dapp/MarketplaceEssentialDapp.tsx index fe9eb99edf..c458d99de6 100644 --- a/ui/pages/MarketplaceEssentialDapp.tsx +++ b/client/features/marketplace/pages/essential-dapp/MarketplaceEssentialDapp.tsx @@ -4,17 +4,18 @@ import { Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { EssentialDappsConfig } from 'types/client/marketplace'; +import type { EssentialDappsConfig } from 'client/features/marketplace/types/client'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import Multisend from 'ui/marketplace/essentialDapps/multisend/Multisend'; -import Revoke from 'ui/marketplace/essentialDapps/revoke/Revoke'; -import Swap from 'ui/marketplace/essentialDapps/swap/Swap'; -import useAutoConnectWallet from 'ui/marketplace/useAutoConnectWallet'; import PageTitle from 'ui/shared/Page/PageTitle'; +import useAutoConnectWallet from '../../hooks/useAutoConnectWallet'; +import Multisend from './multisend/Multisend'; +import Revoke from './revoke/Revoke'; +import Swap from './swap/Swap'; + const feature = config.features.marketplace; const EssentialDapp = () => { diff --git a/ui/marketplace/essentialDapps/multisend/Multisend.pw.tsx b/client/features/marketplace/pages/essential-dapp/multisend/Multisend.pw.tsx similarity index 100% rename from ui/marketplace/essentialDapps/multisend/Multisend.pw.tsx rename to client/features/marketplace/pages/essential-dapp/multisend/Multisend.pw.tsx diff --git a/ui/marketplace/essentialDapps/multisend/Multisend.tsx b/client/features/marketplace/pages/essential-dapp/multisend/Multisend.tsx similarity index 100% rename from ui/marketplace/essentialDapps/multisend/Multisend.tsx rename to client/features/marketplace/pages/essential-dapp/multisend/Multisend.tsx diff --git a/ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_default_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_default_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_default_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_default_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_mobile_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/multisend/__screenshots__/Multisend.pw.tsx_mobile_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/multisend/__screenshots__/Multisend.pw.tsx_mobile_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/Revoke.pw.tsx b/client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/Revoke.pw.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx diff --git a/ui/marketplace/essentialDapps/revoke/Revoke.tsx b/client/features/marketplace/pages/essential-dapp/revoke/Revoke.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/Revoke.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/Revoke.tsx diff --git a/ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_default_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_default_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_default_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_default_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_mobile_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/__screenshots__/Revoke.pw.tsx_mobile_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/__screenshots__/Revoke.pw.tsx_mobile_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/AddressEntity.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/AddressEntity.tsx diff --git a/ui/marketplace/essentialDapps/revoke/components/Approvals.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx similarity index 94% rename from ui/marketplace/essentialDapps/revoke/components/Approvals.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx index 5674db8172..5189024d13 100644 --- a/ui/marketplace/essentialDapps/revoke/components/Approvals.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx @@ -2,7 +2,7 @@ import { Box, Flex } from '@chakra-ui/react'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx similarity index 98% rename from ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx index 262679214c..85cd0f6f2e 100644 --- a/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx @@ -3,7 +3,7 @@ import { Text } from '@chakra-ui/react'; import { useCallback, useState } from 'react'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/ui/marketplace/essentialDapps/revoke/components/ApprovalsTable.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx similarity index 95% rename from ui/marketplace/essentialDapps/revoke/components/ApprovalsTable.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx index 6672650dd2..aaec1119f7 100644 --- a/ui/marketplace/essentialDapps/revoke/components/ApprovalsTable.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; diff --git a/ui/marketplace/essentialDapps/revoke/components/ApprovalsTableItem.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx similarity index 97% rename from ui/marketplace/essentialDapps/revoke/components/ApprovalsTableItem.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx index 889ea1665f..46af9f90f4 100644 --- a/ui/marketplace/essentialDapps/revoke/components/ApprovalsTableItem.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx @@ -3,7 +3,7 @@ import { Flex } from '@chakra-ui/react'; import { useCallback, useState } from 'react'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/ui/marketplace/essentialDapps/revoke/components/ChainSelect.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ChainSelect.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/ChainSelect.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/ChainSelect.tsx diff --git a/ui/marketplace/essentialDapps/revoke/components/Content.pw.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx similarity index 93% rename from ui/marketplace/essentialDapps/revoke/components/Content.pw.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx index 24801eac9c..d608de7a44 100644 --- a/ui/marketplace/essentialDapps/revoke/components/Content.pw.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { hash as addressHash } from 'client/slices/address/mocks/address'; -import * as revokeMock from 'mocks/essentialDapps/revoke'; +import * as revokeMock from 'client/features/marketplace/mocks/revoke-dapp'; + import * as chainDataMock from 'mocks/multichain/chains'; import type { TestFnArgs } from 'playwright/lib'; import { test, expect } from 'playwright/lib'; diff --git a/ui/marketplace/essentialDapps/revoke/components/Content.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx similarity index 98% rename from ui/marketplace/essentialDapps/revoke/components/Content.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx index 2ae650ebed..1e06047b20 100644 --- a/ui/marketplace/essentialDapps/revoke/components/Content.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx @@ -3,7 +3,7 @@ import { Flex, Text, Separator } from '@chakra-ui/react'; import React, { useState, useCallback, useMemo } from 'react'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import { route } from 'nextjs/routes'; diff --git a/ui/marketplace/essentialDapps/revoke/components/SearchInput.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/SearchInput.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/SearchInput.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/SearchInput.tsx diff --git a/ui/marketplace/essentialDapps/revoke/components/StartScreen.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/StartScreen.tsx similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/StartScreen.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/components/StartScreen.tsx diff --git a/ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/marketplace/essentialDapps/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png rename to client/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useApprovalsQuery.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx similarity index 96% rename from ui/marketplace/essentialDapps/revoke/hooks/useApprovalsQuery.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx index 1d062f9ec7..a5820af539 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useApprovalsQuery.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx @@ -6,7 +6,7 @@ import { useCallback, useMemo } from 'react'; import type { GetLogsParameters } from 'viem'; import { isAddress, getAbiItem } from 'viem'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import { ALLOWANCES } from 'stubs/essentialDapps/revoke'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.tsx similarity index 95% rename from ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.tsx index 5d83b25091..7080e21c8e 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { formatUnits } from 'viem'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.tsx similarity index 94% rename from ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.tsx index 53e99c2459..c4a23da95c 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.tsx @@ -2,8 +2,8 @@ import { useCallback, useEffect } from 'react'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { Block } from 'client/slices/block/types/api'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import useApiFetch from 'client/api/hooks/useApiFetch'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx similarity index 97% rename from ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx index 1d6d85c352..ef0beefb06 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx @@ -7,7 +7,7 @@ import { useCallback } from 'react'; import { waitForTransactionReceipt } from 'viem/actions'; import { useAccount, useWriteContract, useSwitchChain } from 'wagmi'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AllowanceType } from 'types/client/revoke'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx similarity index 98% rename from ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx index dd80b51451..4c0bacd37a 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx @@ -6,9 +6,9 @@ import { useCallback } from 'react'; import { getAddress, formatUnits, slice } from 'viem'; import type { PublicClient, Log } from 'viem'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AddressTokenBalancesResponse } from 'client/slices/address/types/api'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; import useApiFetch from 'client/api/hooks/useApiFetch'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx similarity index 98% rename from ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx rename to client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx index c96edb91c4..dc60cadd13 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx @@ -5,8 +5,8 @@ import { useCallback } from 'react'; import { getAbiItem, getAddress, slice } from 'viem'; import type { PublicClient, GetLogsParameters, Log } from 'viem'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; import useApiFetch from 'client/api/hooks/useApiFetch'; diff --git a/ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts b/client/features/marketplace/pages/essential-dapp/revoke/lib/createPublicClient.ts similarity index 100% rename from ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts rename to client/features/marketplace/pages/essential-dapp/revoke/lib/createPublicClient.ts diff --git a/ui/marketplace/essentialDapps/revoke/lib/formatAllowance.ts b/client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts similarity index 100% rename from ui/marketplace/essentialDapps/revoke/lib/formatAllowance.ts rename to client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts diff --git a/ui/marketplace/essentialDapps/revoke/lib/getLogs.ts b/client/features/marketplace/pages/essential-dapp/revoke/lib/getLogs.ts similarity index 100% rename from ui/marketplace/essentialDapps/revoke/lib/getLogs.ts rename to client/features/marketplace/pages/essential-dapp/revoke/lib/getLogs.ts diff --git a/ui/marketplace/essentialDapps/swap/Swap.tsx b/client/features/marketplace/pages/essential-dapp/swap/Swap.tsx similarity index 96% rename from ui/marketplace/essentialDapps/swap/Swap.tsx rename to client/features/marketplace/pages/essential-dapp/swap/Swap.tsx index bca218627f..e91b46c435 100644 --- a/ui/marketplace/essentialDapps/swap/Swap.tsx +++ b/client/features/marketplace/pages/essential-dapp/swap/Swap.tsx @@ -11,9 +11,10 @@ import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; import { useColorMode } from 'toolkit/chakra/color-mode'; import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography'; -import MarketplaceAppIframe from 'ui/marketplace/MarketplaceAppIframe'; import AdBanner from 'ui/shared/ad/AdBanner'; +import MarketplaceAppIframe from '../../../components/MarketplaceAppIframe'; + const feature = getFeaturePayload(config.features.marketplace); const dappConfig = feature?.essentialDapps?.swap; diff --git a/ui/marketplace/EmptySearchResult.tsx b/client/features/marketplace/pages/index/EmptySearchResult.tsx similarity index 94% rename from ui/marketplace/EmptySearchResult.tsx rename to client/features/marketplace/pages/index/EmptySearchResult.tsx index e43beb28fa..75333c6c79 100644 --- a/ui/marketplace/EmptySearchResult.tsx +++ b/client/features/marketplace/pages/index/EmptySearchResult.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import { MarketplaceCategory } from 'types/client/marketplace'; +import { MarketplaceCategory } from 'client/features/marketplace/types/client'; import config from 'configs/app'; import { EmptyState } from 'toolkit/chakra/empty-state'; diff --git a/ui/marketplace/FavoriteIcon.tsx b/client/features/marketplace/pages/index/FavoriteIcon.tsx similarity index 100% rename from ui/marketplace/FavoriteIcon.tsx rename to client/features/marketplace/pages/index/FavoriteIcon.tsx diff --git a/ui/pages/Marketplace.pw.tsx b/client/features/marketplace/pages/index/Marketplace.pw.tsx similarity index 98% rename from ui/pages/Marketplace.pw.tsx rename to client/features/marketplace/pages/index/Marketplace.pw.tsx index c26f8c9308..cbdbc673f0 100644 --- a/ui/pages/Marketplace.pw.tsx +++ b/client/features/marketplace/pages/index/Marketplace.pw.tsx @@ -1,8 +1,9 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; + import config from 'configs/app'; -import { apps as appsMock } from 'mocks/apps/apps'; import { test, expect, devices } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/pages/Marketplace.tsx b/client/features/marketplace/pages/index/Marketplace.tsx similarity index 92% rename from ui/pages/Marketplace.tsx rename to client/features/marketplace/pages/index/Marketplace.tsx index 4753217b2a..49d87da756 100644 --- a/ui/pages/Marketplace.tsx +++ b/client/features/marketplace/pages/index/Marketplace.tsx @@ -4,34 +4,35 @@ import { createListCollection, Flex } from '@chakra-ui/react'; import React from 'react'; import type { MouseEvent } from 'react'; +import { MarketplaceCategory } from 'client/features/marketplace/types/client'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; -import { MarketplaceCategory } from 'types/client/marketplace'; + +import useGraphLinks from 'client/features/marketplace/hooks/useGraphLinks'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import useGraphLinks from 'lib/hooks/useGraphLinks'; import { Heading } from 'toolkit/chakra/heading'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Link } from 'toolkit/chakra/link'; import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu'; import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; -import Banner from 'ui/marketplace/Banner'; -import EssentialDappsList from 'ui/marketplace/essentialDapps/EssentialDappsList'; -import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal'; -import MarketplaceDisclaimerModal from 'ui/marketplace/MarketplaceDisclaimerModal'; -import MarketplaceList from 'ui/marketplace/MarketplaceList'; -import type { SortValue } from 'ui/marketplace/utils'; -import { SORT_OPTIONS } from 'ui/marketplace/utils'; import ActionBar from 'ui/shared/ActionBar'; import IconSvg from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg'; import PageTitle from 'ui/shared/Page/PageTitle'; import Sort from 'ui/shared/sort/Sort'; -import useMarketplace from '../marketplace/useMarketplace'; +import useMarketplace from '../../hooks/useMarketplace'; +import type { SortValue } from '../../utils/sort'; +import { SORT_OPTIONS } from '../../utils/sort'; +import Banner from './banner/Banner'; +import EssentialDappsList from './essential-dapps/EssentialDappsList'; +import MarketplaceAppModal from './MarketplaceAppModal'; +import MarketplaceDisclaimerModal from './MarketplaceDisclaimerModal'; +import MarketplaceList from './MarketplaceList'; const sortCollection = createListCollection({ items: SORT_OPTIONS }); diff --git a/ui/marketplace/MarketplaceAppCard.tsx b/client/features/marketplace/pages/index/MarketplaceAppCard.tsx similarity index 97% rename from ui/marketplace/MarketplaceAppCard.tsx rename to client/features/marketplace/pages/index/MarketplaceAppCard.tsx index 93f3be3e82..1f897577c1 100644 --- a/ui/marketplace/MarketplaceAppCard.tsx +++ b/client/features/marketplace/pages/index/MarketplaceAppCard.tsx @@ -4,7 +4,7 @@ import { chakra, Flex, Text } from '@chakra-ui/react'; import type { MouseEvent } from 'react'; import React, { useCallback } from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { IconButton } from 'toolkit/chakra/icon-button'; @@ -14,11 +14,11 @@ import { Skeleton } from 'toolkit/chakra/skeleton'; import { isBrowser } from 'toolkit/utils/isBrowser'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; +import Rating from '../../components/rating/MarketplaceRating'; import FavoriteIcon from './FavoriteIcon'; import MarketplaceAppCardLink from './MarketplaceAppCardLink'; import MarketplaceAppGraphLinks from './MarketplaceAppGraphLinks'; import MarketplaceAppIntegrationIcon from './MarketplaceAppIntegrationIcon'; -import Rating from './Rating/Rating'; interface Props extends MarketplaceApp { onInfoClick: (id: string) => void; diff --git a/ui/marketplace/MarketplaceAppCardLink.tsx b/client/features/marketplace/pages/index/MarketplaceAppCardLink.tsx similarity index 100% rename from ui/marketplace/MarketplaceAppCardLink.tsx rename to client/features/marketplace/pages/index/MarketplaceAppCardLink.tsx diff --git a/ui/marketplace/MarketplaceAppGraphLinks.tsx b/client/features/marketplace/pages/index/MarketplaceAppGraphLinks.tsx similarity index 100% rename from ui/marketplace/MarketplaceAppGraphLinks.tsx rename to client/features/marketplace/pages/index/MarketplaceAppGraphLinks.tsx diff --git a/ui/marketplace/MarketplaceAppIntegrationIcon.tsx b/client/features/marketplace/pages/index/MarketplaceAppIntegrationIcon.tsx similarity index 100% rename from ui/marketplace/MarketplaceAppIntegrationIcon.tsx rename to client/features/marketplace/pages/index/MarketplaceAppIntegrationIcon.tsx diff --git a/ui/marketplace/MarketplaceAppModal.pw.tsx b/client/features/marketplace/pages/index/MarketplaceAppModal.pw.tsx similarity index 92% rename from ui/marketplace/MarketplaceAppModal.pw.tsx rename to client/features/marketplace/pages/index/MarketplaceAppModal.pw.tsx index a58addff01..17601b1840 100644 --- a/ui/marketplace/MarketplaceAppModal.pw.tsx +++ b/client/features/marketplace/pages/index/MarketplaceAppModal.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { apps as appsMock } from 'mocks/apps/apps'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; + import type { TestFnArgs } from 'playwright/lib'; import { test, expect, devices } from 'playwright/lib'; diff --git a/ui/marketplace/MarketplaceAppModal.tsx b/client/features/marketplace/pages/index/MarketplaceAppModal.tsx similarity index 98% rename from ui/marketplace/MarketplaceAppModal.tsx rename to client/features/marketplace/pages/index/MarketplaceAppModal.tsx index 8bbc1c971c..ee1a8bf2e6 100644 --- a/ui/marketplace/MarketplaceAppModal.tsx +++ b/client/features/marketplace/pages/index/MarketplaceAppModal.tsx @@ -3,7 +3,7 @@ import { Box, Flex, Text } from '@chakra-ui/react'; import React, { useCallback } from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { route } from 'nextjs-routes'; @@ -23,10 +23,10 @@ import CopyToClipboard from 'ui/shared/CopyToClipboard'; import type { IconName } from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg'; +import Rating from '../../components/rating/MarketplaceRating'; import FavoriteIcon from './FavoriteIcon'; import MarketplaceAppGraphLinks from './MarketplaceAppGraphLinks'; import MarketplaceAppIntegrationIcon from './MarketplaceAppIntegrationIcon'; -import Rating from './Rating/Rating'; const feature = config.features.marketplace; const isRatingEnabled = feature.isEnabled && 'api' in feature; diff --git a/ui/marketplace/MarketplaceDisclaimerModal.tsx b/client/features/marketplace/pages/index/MarketplaceDisclaimerModal.tsx similarity index 100% rename from ui/marketplace/MarketplaceDisclaimerModal.tsx rename to client/features/marketplace/pages/index/MarketplaceDisclaimerModal.tsx diff --git a/ui/marketplace/MarketplaceList.tsx b/client/features/marketplace/pages/index/MarketplaceList.tsx similarity index 97% rename from ui/marketplace/MarketplaceList.tsx rename to client/features/marketplace/pages/index/MarketplaceList.tsx index 63f1da2011..267a22c310 100644 --- a/ui/marketplace/MarketplaceList.tsx +++ b/client/features/marketplace/pages/index/MarketplaceList.tsx @@ -5,7 +5,7 @@ import type { UseQueryResult } from '@tanstack/react-query'; import React, { useCallback } from 'react'; import type { MouseEvent } from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png b/client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png b/client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png rename to client/features/marketplace/pages/index/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/marketplace/Banner.tsx b/client/features/marketplace/pages/index/banner/Banner.tsx similarity index 88% rename from ui/marketplace/Banner.tsx rename to client/features/marketplace/pages/index/banner/Banner.tsx index 1184a73d82..f60b2da637 100644 --- a/ui/marketplace/Banner.tsx +++ b/client/features/marketplace/pages/index/banner/Banner.tsx @@ -4,16 +4,17 @@ import { Flex } from '@chakra-ui/react'; import type { MouseEvent } from 'react'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; + +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import { apps as appsMock } from 'mocks/apps/apps'; import AdBanner from 'ui/shared/ad/AdBanner'; -import FeaturedApp from './Banner/FeaturedApp'; -import IframeBanner from './Banner/IframeBanner'; +import FeaturedApp from './FeaturedApp'; +import IframeBanner from './IframeBanner'; const feature = config.features.marketplace; diff --git a/ui/marketplace/Banner/FeaturedApp.tsx b/client/features/marketplace/pages/index/banner/FeaturedApp.tsx similarity index 98% rename from ui/marketplace/Banner/FeaturedApp.tsx rename to client/features/marketplace/pages/index/banner/FeaturedApp.tsx index 0b55cfab11..6bc1f8fd4c 100644 --- a/ui/marketplace/Banner/FeaturedApp.tsx +++ b/client/features/marketplace/pages/index/banner/FeaturedApp.tsx @@ -4,7 +4,7 @@ import { Flex, Text } from '@chakra-ui/react'; import type { MouseEvent } from 'react'; import React, { useCallback } from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMobile from 'client/shared/hooks/useIsMobile'; diff --git a/ui/marketplace/Banner/FeaturedAppMobile.tsx b/client/features/marketplace/pages/index/banner/FeaturedAppMobile.tsx similarity index 98% rename from ui/marketplace/Banner/FeaturedAppMobile.tsx rename to client/features/marketplace/pages/index/banner/FeaturedAppMobile.tsx index 63309154ec..0f78ff69df 100644 --- a/ui/marketplace/Banner/FeaturedAppMobile.tsx +++ b/client/features/marketplace/pages/index/banner/FeaturedAppMobile.tsx @@ -4,7 +4,7 @@ import { Flex, Text } from '@chakra-ui/react'; import type { MouseEvent } from 'react'; import React from 'react'; -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { Heading } from 'toolkit/chakra/heading'; diff --git a/ui/marketplace/Banner/IframeBanner.tsx b/client/features/marketplace/pages/index/banner/IframeBanner.tsx similarity index 100% rename from ui/marketplace/Banner/IframeBanner.tsx rename to client/features/marketplace/pages/index/banner/IframeBanner.tsx diff --git a/ui/marketplace/essentialDapps/EssentialDappCard.tsx b/client/features/marketplace/pages/index/essential-dapps/EssentialDappCard.tsx similarity index 100% rename from ui/marketplace/essentialDapps/EssentialDappCard.tsx rename to client/features/marketplace/pages/index/essential-dapps/EssentialDappCard.tsx diff --git a/ui/marketplace/essentialDapps/EssentialDappsList.tsx b/client/features/marketplace/pages/index/essential-dapps/EssentialDappsList.tsx similarity index 95% rename from ui/marketplace/essentialDapps/EssentialDappsList.tsx rename to client/features/marketplace/pages/index/essential-dapps/EssentialDappsList.tsx index a261b1c1c7..9f598010d3 100644 --- a/ui/marketplace/essentialDapps/EssentialDappsList.tsx +++ b/client/features/marketplace/pages/index/essential-dapps/EssentialDappsList.tsx @@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react'; -import type { EssentialDappsConfig } from 'types/client/marketplace'; +import type { EssentialDappsConfig } from 'client/features/marketplace/types/client'; import config from 'configs/app'; diff --git a/stubs/marketplace.ts b/client/features/marketplace/stubs.ts similarity index 91% rename from stubs/marketplace.ts rename to client/features/marketplace/stubs.ts index f42d07501e..8b4c971f7d 100644 --- a/stubs/marketplace.ts +++ b/client/features/marketplace/stubs.ts @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import type { MarketplaceApp } from 'types/client/marketplace'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; export const MARKETPLACE_APP: MarketplaceApp = { author: 'StubApp Inc.', diff --git a/types/client/marketplace.ts b/client/features/marketplace/types/client.ts similarity index 100% rename from types/client/marketplace.ts rename to client/features/marketplace/types/client.ts diff --git a/ui/marketplace/utils.spec.ts b/client/features/marketplace/utils/dapp.spec.ts similarity index 98% rename from ui/marketplace/utils.spec.ts rename to client/features/marketplace/utils/dapp.spec.ts index 50046e8306..d2e4f45b34 100644 --- a/ui/marketplace/utils.spec.ts +++ b/client/features/marketplace/utils/dapp.spec.ts @@ -2,7 +2,7 @@ import type { NextRouter } from 'next/router'; import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { getAppUrl } from './utils'; +import { getAppUrl } from './dapp'; describe('getAppUrl', () => { let router: NextRouter; diff --git a/ui/marketplace/utils.ts b/client/features/marketplace/utils/dapp.ts similarity index 76% rename from ui/marketplace/utils.ts rename to client/features/marketplace/utils/dapp.ts index 1aa5138795..965205c078 100644 --- a/ui/marketplace/utils.ts +++ b/client/features/marketplace/utils/dapp.ts @@ -5,19 +5,6 @@ import type { NextRouter } from 'next/router'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import removeQueryParam from 'client/shared/router/remove-query-param'; -import config from 'configs/app'; -import type { SelectOption } from 'toolkit/chakra/select'; - -const feature = config.features.marketplace; - -export type SortValue = 'default' | 'rating_score' | 'rating_count'; - -export const SORT_OPTIONS: Array> = [ - { label: 'Default', value: 'default' }, - (feature.isEnabled && 'api' in feature) && { label: 'Top rated', value: 'rating_score' }, - (feature.isEnabled && 'api' in feature) && { label: 'Most rated', value: 'rating_count' }, -].filter(Boolean) as Array>; - export function getAppUrl(url: string | undefined, router: NextRouter) { if (!url) { return; diff --git a/client/features/marketplace/utils/sort.ts b/client/features/marketplace/utils/sort.ts new file mode 100644 index 0000000000..d248bd0483 --- /dev/null +++ b/client/features/marketplace/utils/sort.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import config from 'configs/app'; +import type { SelectOption } from 'toolkit/chakra/select'; + +const feature = config.features.marketplace; + +export type SortValue = 'default' | 'rating_score' | 'rating_count'; + +export const SORT_OPTIONS: Array> = [ + { label: 'Default', value: 'default' }, + (feature.isEnabled && 'api' in feature) && { label: 'Top rated', value: 'rating_score' }, + (feature.isEnabled && 'api' in feature) && { label: 'Most rated', value: 'rating_count' }, +].filter(Boolean) as Array>; diff --git a/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx b/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx index e14628cae4..f8020e684e 100644 --- a/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx +++ b/client/slices/search/components/search-bar/SearchBarDesktop.pw.tsx @@ -5,10 +5,10 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; -import { apps as appsMock } from 'mocks/apps/apps'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx b/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx index 48302fb800..b236a57440 100644 --- a/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx +++ b/client/slices/search/components/search-bar/SearchBarMobile.pw.tsx @@ -6,10 +6,10 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; -import { apps as appsMock } from 'mocks/apps/apps'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect, devices } from 'playwright/lib'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggest.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggest.tsx index 51fa4d971c..9ee9655955 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggest.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggest.tsx @@ -17,6 +17,7 @@ import ExternalSearchItem from 'client/features/chain-variants/zeta-chain/compon import SearchBarSuggestZetaChainCCTX from 'client/features/chain-variants/zeta-chain/components/SearchBarSuggestZetaChainCCTX'; import type { ExternalSearchItem as ExternalSearchItemType } from 'client/features/chain-variants/zeta-chain/utils/external-search'; import SearchBarSuggestApp from 'client/features/marketplace/components/SearchBarSuggestApp'; +import useMarketplaceApps from 'client/features/marketplace/hooks/useMarketplaceApps'; import useIsMobile from 'client/shared/hooks/useIsMobile'; @@ -26,7 +27,6 @@ import { useSettingsContext } from 'lib/contexts/settings'; import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import * as regexp from 'toolkit/utils/regexp'; -import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps'; import TextAd from 'ui/shared/ad/TextAd'; import SearchBarSuggestBlockCountdown from './SearchBarSuggestBlockCountdown'; diff --git a/client/slices/search/pages/search-results/SearchResults.pw.tsx b/client/slices/search/pages/search-results/SearchResults.pw.tsx index c499ab66f5..6bb41245cd 100644 --- a/client/slices/search/pages/search-results/SearchResults.pw.tsx +++ b/client/slices/search/pages/search-results/SearchResults.pw.tsx @@ -6,10 +6,10 @@ import * as searchMock from 'client/slices/search/mocks'; import { metatag1, metatag2, metatag3 } from 'client/features/address-metadata/mocks/search'; import { tacOperation1 } from 'client/features/chain-variants/tac/mocks/search'; import { blob1 } from 'client/features/data-availability/mocks/search'; +import { apps as appsMock } from 'client/features/marketplace/mocks/dapps'; import { domain1 } from 'client/features/name-services/domains/mocks/search'; import { userOp1 } from 'client/features/user-ops/mocks/search'; -import { apps as appsMock } from 'mocks/apps/apps'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/search/pages/search-results/SearchResults.tsx b/client/slices/search/pages/search-results/SearchResults.tsx index 8395ec94d5..5ff8998c5e 100644 --- a/client/slices/search/pages/search-results/SearchResults.tsx +++ b/client/slices/search/pages/search-results/SearchResults.tsx @@ -13,6 +13,7 @@ import useSearchQuery from 'client/slices/search/hooks/useSearchQuery'; import type { SearchResultAppItem } from 'client/slices/search/utils/search-categories'; import ExternalSearchItem from 'client/features/chain-variants/zeta-chain/components/ExternalSearchItem'; +import useMarketplaceApps from 'client/features/marketplace/hooks/useMarketplaceApps'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import removeQueryParam from 'client/shared/router/remove-query-param'; @@ -23,7 +24,6 @@ import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import * as regexp from 'toolkit/utils/regexp'; -import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; diff --git a/client/slices/search/utils/search-categories.ts b/client/slices/search/utils/search-categories.ts index b9e9edfc34..d6ca494137 100644 --- a/client/slices/search/utils/search-categories.ts +++ b/client/slices/search/utils/search-categories.ts @@ -1,11 +1,11 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { CctxListItem } from '@blockscout/zetachain-cctx-types'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import type { QuickSearchResultItem } from 'client/slices/search/types/client'; import type { TokenType } from 'client/slices/token/types/api'; import { isConfidentialTokenType } from 'client/slices/token/utils/token-types'; import { getFeaturePayload } from 'configs/app/features/types'; -import type { MarketplaceApp } from 'types/client/marketplace'; import config from 'configs/app'; diff --git a/configs/app/features/marketplace.ts b/configs/app/features/marketplace.ts index 965517fb44..9c295b3463 100644 --- a/configs/app/features/marketplace.ts +++ b/configs/app/features/marketplace.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { EssentialDappsConfig, MarketplaceTitles } from 'types/client/marketplace'; +import type { EssentialDappsConfig, MarketplaceTitles } from 'client/features/marketplace/types/client'; import apis from '../apis'; import app from '../app'; diff --git a/configs/essential-dapps-chains/index.ts b/configs/essential-dapps-chains/index.ts index 83fe863e34..d1f8709738 100644 --- a/configs/essential-dapps-chains/index.ts +++ b/configs/essential-dapps-chains/index.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import config from 'configs/app'; import { isBrowser } from 'toolkit/utils/isBrowser'; diff --git a/deploy/tools/envs-validator/schemas/features/marketplace.ts b/deploy/tools/envs-validator/schemas/features/marketplace.ts index 86e50afaea..6ba8f8ec13 100644 --- a/deploy/tools/envs-validator/schemas/features/marketplace.ts +++ b/deploy/tools/envs-validator/schemas/features/marketplace.ts @@ -1,5 +1,5 @@ import * as yup from 'yup'; -import type { MarketplaceAppBase, MarketplaceAppSocialInfo, EssentialDappsConfig, MarketplaceTitles } from 'types/client/marketplace'; +import type { MarketplaceAppBase, MarketplaceAppSocialInfo, EssentialDappsConfig, MarketplaceTitles } from 'client/features/marketplace/types/client'; import { urlTest } from '../../utils'; import { replaceQuotes } from 'configs/app/utils'; diff --git a/deploy/tools/essential-dapps-chains-config-generator/index.ts b/deploy/tools/essential-dapps-chains-config-generator/index.ts index a19e391af9..6dbbcea785 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/index.ts +++ b/deploy/tools/essential-dapps-chains-config-generator/index.ts @@ -6,11 +6,11 @@ import { Worker } from 'node:worker_threads'; import * as viemChains from 'viem/chains'; import { pick, uniq, delay } from 'es-toolkit'; -import { EssentialDappsConfig } from 'types/client/marketplace'; +import { EssentialDappsConfig } from 'client/features/marketplace/types/client'; import { getEnvValue, parseEnvJson } from 'configs/app/utils'; import currentChainConfig from 'configs/app'; import appConfig from 'configs/app'; -import { EssentialDappsChainConfig } from 'types/client/marketplace'; +import { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; const currentFilePath = fileURLToPath(import.meta.url); const currentDir = dirname(currentFilePath); diff --git a/global.d.ts b/global.d.ts index 2e1414bb74..f0b25ac04b 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,4 +1,4 @@ -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { MultichainConfig } from 'types/multichain'; import type { WalletProvider } from 'types/web3'; import 'vitest-fetch-mock'; diff --git a/pages/_app.tsx b/pages/_app.tsx index 29c718c2b2..b44c30eb16 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -18,6 +18,7 @@ import useQueryClientConfig from 'client/api/hooks/useQueryClientConfig'; import { SocketProvider } from 'client/api/socket/context'; import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; +import { MarketplaceContextProvider } from 'client/features/marketplace/context'; import { initGrowthBook } from 'client/shared/feature-flags/init'; import useLoadFeatures from 'client/shared/feature-flags/useLoadFeatures'; @@ -26,7 +27,6 @@ import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'clie import config from 'configs/app'; import { AppContextProvider } from 'lib/contexts/app'; import { FallbackProvider } from 'lib/contexts/fallback'; -import { MarketplaceContextProvider } from 'lib/contexts/marketplace'; import { SettingsContextProvider } from 'lib/contexts/settings'; import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; import { Toaster } from 'toolkit/chakra/toaster'; diff --git a/pages/apps/[id].tsx b/pages/apps/[id].tsx index 269b82d2e8..fb49d4405f 100644 --- a/pages/apps/[id].tsx +++ b/pages/apps/[id].tsx @@ -5,8 +5,8 @@ import dynamic from 'next/dynamic'; import fetch from 'node-fetch'; import React from 'react'; +import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import type { NextPageWithLayout } from 'nextjs/types'; -import type { MarketplaceApp } from 'types/client/marketplace'; import type { Route } from 'nextjs-routes'; import type { Props } from 'nextjs/getServerSideProps/handlers'; @@ -20,7 +20,7 @@ import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; import LayoutApp from 'ui/shared/layout/LayoutApp'; -const MarketplaceAppPage = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false }); +const MarketplaceAppPage = dynamic(() => import('client/features/marketplace/pages/dapp/MarketplaceApp'), { ssr: false }); const pathname: Route['pathname'] = '/apps/[id]'; const feature = config.features.marketplace; diff --git a/pages/apps/index.tsx b/pages/apps/index.tsx index d1d9d0b4a0..55ccabac37 100644 --- a/pages/apps/index.tsx +++ b/pages/apps/index.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false }); +const Marketplace = dynamic(() => import('client/features/marketplace/pages/index/Marketplace'), { ssr: false }); const Page: NextPage = () => ( diff --git a/pages/essential-dapps/[id].tsx b/pages/essential-dapps/[id].tsx index 8acb2287f4..ed10133891 100644 --- a/pages/essential-dapps/[id].tsx +++ b/pages/essential-dapps/[id].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const MarketplaceEssentialDapp = dynamic(() => import('ui/pages/MarketplaceEssentialDapp'), { ssr: false }); +const MarketplaceEssentialDapp = dynamic(() => import('client/features/marketplace/pages/essential-dapp/MarketplaceEssentialDapp'), { ssr: false }); const Page: NextPage = (props: Props) => ( diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx index 7bb6688da4..fd08bd1a08 100644 --- a/playwright/TestApp.tsx +++ b/playwright/TestApp.tsx @@ -11,11 +11,11 @@ import { SocketProvider } from 'client/api/socket/context'; import { currentChain } from 'client/features/connect-wallet/utils/chains'; import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; +import { MarketplaceContext } from 'client/features/marketplace/context'; import { RewardsContextProvider } from 'client/features/rewards/context'; import config from 'configs/app'; import { AppContextProvider } from 'lib/contexts/app'; -import { MarketplaceContext } from 'lib/contexts/marketplace'; import { SettingsContextProvider } from 'lib/contexts/settings'; import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; diff --git a/types/externalChains.ts b/types/externalChains.ts index abe8a59400..41df69f790 100644 --- a/types/externalChains.ts +++ b/types/externalChains.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { EssentialDappsChainConfig } from 'types/client/marketplace'; +import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { ClusterChainConfig } from 'types/multichain'; // minimal set of fields for external chains From c15a5b28bfc6a05e8c685ca128dc3a87a6e6be3b Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 15:39:44 +0200 Subject: [PATCH 12/85] =?UTF-8?q?track:=206-21=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3471?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 840eb5c51b..ea21259bdf 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -258,7 +258,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-20 · [ ] Feature: `visualize` **Scope:** Solidity-to-UML diagrams. See `configs/app/features/sol2uml.ts`. -### 6-21 · [ ] Feature: `tx-interpretation` +### 6-21 · [~] Feature: `tx-interpretation` · [#3471](https://github.com/blockscout/frontend/issues/3471) ### 6-22 · [ ] Feature: `public-tags` (merge with `address-metadata`) **Scope:** Community address labels. See `configs/app/features/publicTagsSubmission.ts`. From fc83bc4cfb326e0e1c2ef734177c8943391b7079 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 17:57:08 +0200 Subject: [PATCH 13/85] [Migration 6-11] Feature: advanced-filter (#3470) feat: migrate advanced-filter feature to client/ (task 6-11) Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/9eb8f172-90c1-42b5-814f-28aa802b519f Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- client/api/services/general/misc.ts | 2 +- .../components/AddressAdvancedFilterLink.tsx | 5 +- .../components}/AdvancedFilterLink.tsx | 0 .../components}/ColumnsButton.tsx | 6 +- .../components}/FilterByColumn.pw.tsx | 3 +- .../components}/FilterByColumn.tsx | 6 +- .../components}/ItemByColumn.tsx | 6 +- ...dark-color-mode_age-filter-dark-mode-1.png | Bin ...k-color-mode_amount-filter-dark-mode-1.png | Bin ...rk-color-mode_asset-filter-dark-mode-1.png | Bin ...ark-color-mode_from-filter-dark-mode-1.png | Bin ...k-color-mode_method-filter-dark-mode-1.png | Bin ...k-color-mode_or-and-filter-dark-mode-1.png | Bin ..._dark-color-mode_to-filter-dark-mode-1.png | Bin ...ark-color-mode_type-filter-dark-mode-1.png | Bin ....pw.tsx_default_age-filter-dark-mode-1.png | Bin ....tsx_default_amount-filter-dark-mode-1.png | Bin ...w.tsx_default_asset-filter-dark-mode-1.png | Bin ...pw.tsx_default_from-filter-dark-mode-1.png | Bin ....tsx_default_method-filter-dark-mode-1.png | Bin ....tsx_default_or-and-filter-dark-mode-1.png | Bin ...n.pw.tsx_default_to-filter-dark-mode-1.png | Bin ...pw.tsx_default_type-filter-dark-mode-1.png | Bin .../components}/filters/AddressFilter.tsx | 2 +- .../filters/AddressRelationFilter.tsx | 2 +- .../components}/filters/AgeFilter.tsx | 2 +- .../components}/filters/AmountFilter.tsx | 2 +- .../components}/filters/AssetFilter.tsx | 4 +- .../components}/filters/BaseAgeFilter.tsx | 4 +- .../components}/filters/MethodFilter.tsx | 2 +- .../components}/filters/TypeFilter.tsx | 4 +- .../features/advanced-filter/mocks.ts | 2 +- .../pages/index}/AdvancedFilter.pw.tsx | 2 +- .../pages/index}/AdvancedFilter.tsx | 19 +++--- ..._dark-color-mode_base-view-dark-mode-1.png | Bin ...r.pw.tsx_default_base-view-dark-mode-1.png | Bin .../pages/token/TokenAdvancedFilterLink.tsx | 3 +- .../features/advanced-filter/stubs.ts | 4 +- .../features/advanced-filter/types/api.ts | 0 .../features/advanced-filter/types/client.ts | 10 +++ .../features/advanced-filter/utils/consts.ts | 60 +++++++----------- .../features/advanced-filter/utils}/lib.ts | 54 +++++++++------- .../cctx-index/TransactionsZetaChain.tsx | 2 +- .../pages/cctx-index/ZetaChainAgeFilter.tsx | 5 +- .../pages/cctx-index/ZetaChainCCTXsTab.tsx | 4 +- .../cctx-index/ZetaChainEvmTransactions.tsx | 3 +- .../cctx-index/ZetaChainFilterByColumn.tsx | 2 +- .../chain-variants/zeta-chain/types/client.ts | 2 +- client/slices/tx/pages/index/list/TxsTabs.tsx | 2 +- pages/advanced-filter.tsx | 2 +- .../advanced-filter/index.tsx | 3 +- 51 files changed, 121 insertions(+), 108 deletions(-) rename {ui/shared/links => client/features/advanced-filter/components}/AdvancedFilterLink.tsx (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/ColumnsButton.tsx (93%) rename {ui/advancedFilter => client/features/advanced-filter/components}/FilterByColumn.pw.tsx (96%) rename {ui/advancedFilter => client/features/advanced-filter/components}/FilterByColumn.tsx (97%) rename {ui/advancedFilter => client/features/advanced-filter/components}/ItemByColumn.tsx (94%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_age-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_amount-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_asset-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_method-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_or-and-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_type-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_age-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_amount-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_asset-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_method-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_or-and-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/__screenshots__/FilterByColumn.pw.tsx_default_type-filter-dark-mode-1.png (100%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/AddressFilter.tsx (98%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/AddressRelationFilter.tsx (93%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/AgeFilter.tsx (91%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/AmountFilter.tsx (97%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/AssetFilter.tsx (98%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/BaseAgeFilter.tsx (97%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/MethodFilter.tsx (99%) rename {ui/advancedFilter => client/features/advanced-filter/components}/filters/TypeFilter.tsx (94%) rename mocks/advancedFilter/advancedFilter.ts => client/features/advanced-filter/mocks.ts (97%) rename {ui/pages => client/features/advanced-filter/pages/index}/AdvancedFilter.pw.tsx (89%) rename {ui/pages => client/features/advanced-filter/pages/index}/AdvancedFilter.tsx (95%) rename {ui/pages => client/features/advanced-filter/pages/index}/__screenshots__/AdvancedFilter.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename {ui/pages => client/features/advanced-filter/pages/index}/__screenshots__/AdvancedFilter.pw.tsx_default_base-view-dark-mode-1.png (100%) rename stubs/advancedFilter.ts => client/features/advanced-filter/stubs.ts (79%) rename types/api/advancedFilter.ts => client/features/advanced-filter/types/api.ts (100%) create mode 100644 client/features/advanced-filter/types/client.ts rename ui/advancedFilter/constants.ts => client/features/advanced-filter/utils/consts.ts (53%) rename {ui/advancedFilter => client/features/advanced-filter/utils}/lib.ts (75%) diff --git a/client/api/services/general/misc.ts b/client/api/services/general/misc.ts index 881f03cba4..29eac88cff 100644 --- a/client/api/services/general/misc.ts +++ b/client/api/services/general/misc.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { ApiResource } from '../../types'; +import type { AdvancedFilterParams, AdvancedFilterResponse, AdvancedFilterMethodsResponse } from 'client/features/advanced-filter/types/api'; import type { DepositsResponse, DepositsCounters, WithdrawalsResponse, WithdrawalsCounters } from 'client/features/chain-variants/beacon-chain/types/api'; import type { CeloEpochDetails, CeloEpochElectionRewardDetailsResponse, CeloEpochListResponse } from 'client/features/chain-variants/celo/types/api'; import type { CsvExportItemResponse, CsvExportConfig } from 'client/features/csv-export/types/api'; @@ -20,7 +21,6 @@ import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchRes import type { Transaction, } from 'client/slices/tx/types/api'; -import type { AdvancedFilterParams, AdvancedFilterResponse, AdvancedFilterMethodsResponse } from 'types/api/advancedFilter'; import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig } from 'types/api/configs'; import type { IndexingStatus } from 'types/api/indexingStatus'; diff --git a/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx b/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx index f70f07cafd..86e81957dd 100644 --- a/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx +++ b/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx @@ -11,8 +11,9 @@ import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import { getAdvancedFilterTypes } from 'ui/advancedFilter/constants'; -import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; + +import { getAdvancedFilterTypes } from '../utils/lib'; +import AdvancedFilterLink from './AdvancedFilterLink'; interface Props { isLoading?: boolean; diff --git a/ui/shared/links/AdvancedFilterLink.tsx b/client/features/advanced-filter/components/AdvancedFilterLink.tsx similarity index 100% rename from ui/shared/links/AdvancedFilterLink.tsx rename to client/features/advanced-filter/components/AdvancedFilterLink.tsx diff --git a/ui/advancedFilter/ColumnsButton.tsx b/client/features/advanced-filter/components/ColumnsButton.tsx similarity index 93% rename from ui/advancedFilter/ColumnsButton.tsx rename to client/features/advanced-filter/components/ColumnsButton.tsx index 874a523e5e..1c7fd534d0 100644 --- a/ui/advancedFilter/ColumnsButton.tsx +++ b/client/features/advanced-filter/components/ColumnsButton.tsx @@ -3,13 +3,15 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; +import type { ColumnsIds } from '../types/client'; + import { Button } from 'toolkit/chakra/button'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; -import type { ColumnsIds } from 'ui/advancedFilter/constants'; -import { TABLE_COLUMNS } from 'ui/advancedFilter/constants'; import IconSvg from 'ui/shared/IconSvg'; +import { TABLE_COLUMNS } from '../utils/consts'; + interface Props { columns: Record; onChange: (val: Record) => void; diff --git a/ui/advancedFilter/FilterByColumn.pw.tsx b/client/features/advanced-filter/components/FilterByColumn.pw.tsx similarity index 96% rename from ui/advancedFilter/FilterByColumn.pw.tsx rename to client/features/advanced-filter/components/FilterByColumn.pw.tsx index 04dd3d057e..84592d726c 100644 --- a/ui/advancedFilter/FilterByColumn.pw.tsx +++ b/client/features/advanced-filter/components/FilterByColumn.pw.tsx @@ -1,8 +1,9 @@ /* eslint-disable react/jsx-no-bind */ import React from 'react'; +import type { ColumnsIds } from '../types/client'; + import { test, expect } from 'playwright/lib'; -import type { ColumnsIds } from 'ui/advancedFilter/constants'; import FilterByColumn from './FilterByColumn'; diff --git a/ui/advancedFilter/FilterByColumn.tsx b/client/features/advanced-filter/components/FilterByColumn.tsx similarity index 97% rename from ui/advancedFilter/FilterByColumn.tsx rename to client/features/advanced-filter/components/FilterByColumn.tsx index e7dd39b0e3..437d099a03 100644 --- a/ui/advancedFilter/FilterByColumn.tsx +++ b/client/features/advanced-filter/components/FilterByColumn.tsx @@ -2,12 +2,12 @@ import React from 'react'; -import type { AdvancedFilterParams, AdvancedFiltersSearchParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterParams, AdvancedFiltersSearchParams } from '../types/api'; +import type { ColumnsIds } from '../types/client'; -import type { ColumnsIds } from 'ui/advancedFilter/constants'; import TableColumnFilterWrapper from 'ui/shared/filters/TableColumnFilterWrapper'; -import { NATIVE_TOKEN } from './constants'; +import { NATIVE_TOKEN } from '../utils/consts'; import type { AddressFilterMode } from './filters/AddressFilter'; import AddressFilter from './filters/AddressFilter'; import AddressRelationFilter from './filters/AddressRelationFilter'; diff --git a/ui/advancedFilter/ItemByColumn.tsx b/client/features/advanced-filter/components/ItemByColumn.tsx similarity index 94% rename from ui/advancedFilter/ItemByColumn.tsx rename to client/features/advanced-filter/components/ItemByColumn.tsx index 309df3cb22..0eabd403f9 100644 --- a/ui/advancedFilter/ItemByColumn.tsx +++ b/client/features/advanced-filter/components/ItemByColumn.tsx @@ -3,8 +3,9 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import type { AdvancedFilterResponseItem } from '../types/api'; +import type { ColumnsIds } from '../types/client'; import { isConfidentialTokenType } from 'client/slices/token/utils/token-types'; -import type { AdvancedFilterResponseItem } from 'types/api/advancedFilter'; import type { ClusterChainConfig } from 'types/multichain'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; @@ -15,13 +16,12 @@ import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import config from 'configs/app'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import type { ColumnsIds } from 'ui/advancedFilter/constants'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import { getAdvancedFilterTypes } from './constants'; +import { getAdvancedFilterTypes } from '../utils/lib'; type Props = { item: AdvancedFilterResponseItem; diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_age-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_age-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_age-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_age-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_amount-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_amount-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_amount-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_amount-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_asset-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_asset-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_asset-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_asset-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_method-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_method-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_method-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_method-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_or-and-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_or-and-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_or-and-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_or-and-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_type-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_type-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_type-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_type-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_age-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_age-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_age-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_age-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_amount-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_amount-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_amount-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_amount-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_asset-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_asset-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_asset-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_asset-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_method-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_method-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_method-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_method-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_or-and-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_or-and-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_or-and-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_or-and-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_type-filter-dark-mode-1.png b/client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_type-filter-dark-mode-1.png similarity index 100% rename from ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_type-filter-dark-mode-1.png rename to client/features/advanced-filter/components/__screenshots__/FilterByColumn.pw.tsx_default_type-filter-dark-mode-1.png diff --git a/ui/advancedFilter/filters/AddressFilter.tsx b/client/features/advanced-filter/components/filters/AddressFilter.tsx similarity index 98% rename from ui/advancedFilter/filters/AddressFilter.tsx rename to client/features/advanced-filter/components/filters/AddressFilter.tsx index 343a13814d..da9a360c05 100644 --- a/ui/advancedFilter/filters/AddressFilter.tsx +++ b/client/features/advanced-filter/components/filters/AddressFilter.tsx @@ -5,7 +5,7 @@ import { isEqual } from 'es-toolkit'; import type { ChangeEvent } from 'react'; import React from 'react'; -import type { AdvancedFilterParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterParams } from '../../types/api'; import { Field } from 'toolkit/chakra/field'; import { Input } from 'toolkit/chakra/input'; diff --git a/ui/advancedFilter/filters/AddressRelationFilter.tsx b/client/features/advanced-filter/components/filters/AddressRelationFilter.tsx similarity index 93% rename from ui/advancedFilter/filters/AddressRelationFilter.tsx rename to client/features/advanced-filter/components/filters/AddressRelationFilter.tsx index 1ce74c99ee..3833f7d581 100644 --- a/ui/advancedFilter/filters/AddressRelationFilter.tsx +++ b/client/features/advanced-filter/components/filters/AddressRelationFilter.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import { type AdvancedFilterParams } from 'types/api/advancedFilter'; +import { type AdvancedFilterParams } from '../../types/api'; import { Radio, RadioGroup } from 'toolkit/chakra/radio'; diff --git a/ui/advancedFilter/filters/AgeFilter.tsx b/client/features/advanced-filter/components/filters/AgeFilter.tsx similarity index 91% rename from ui/advancedFilter/filters/AgeFilter.tsx rename to client/features/advanced-filter/components/filters/AgeFilter.tsx index e6797b6218..8133a2e7d9 100644 --- a/ui/advancedFilter/filters/AgeFilter.tsx +++ b/client/features/advanced-filter/components/filters/AgeFilter.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { AdvancedFilterAge, AdvancedFilterParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterAge, AdvancedFilterParams } from '../../types/api'; import BaseAgeFilter, { type DateConverter } from './BaseAgeFilter'; diff --git a/ui/advancedFilter/filters/AmountFilter.tsx b/client/features/advanced-filter/components/filters/AmountFilter.tsx similarity index 97% rename from ui/advancedFilter/filters/AmountFilter.tsx rename to client/features/advanced-filter/components/filters/AmountFilter.tsx index 9cb788d40b..3ffb262d4c 100644 --- a/ui/advancedFilter/filters/AmountFilter.tsx +++ b/client/features/advanced-filter/components/filters/AmountFilter.tsx @@ -5,7 +5,7 @@ import { isEqual } from 'es-toolkit'; import type { ChangeEvent } from 'react'; import React from 'react'; -import type { AdvancedFilterParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterParams } from '../../types/api'; import { Input } from 'toolkit/chakra/input'; import { PopoverCloseTriggerWrapper } from 'toolkit/chakra/popover'; diff --git a/ui/advancedFilter/filters/AssetFilter.tsx b/client/features/advanced-filter/components/filters/AssetFilter.tsx similarity index 98% rename from ui/advancedFilter/filters/AssetFilter.tsx rename to client/features/advanced-filter/components/filters/AssetFilter.tsx index 2fbf278090..734f5e999a 100644 --- a/ui/advancedFilter/filters/AssetFilter.tsx +++ b/client/features/advanced-filter/components/filters/AssetFilter.tsx @@ -4,8 +4,8 @@ import { Flex, Text, Spinner, createListCollection } from '@chakra-ui/react'; import { isEqual } from 'es-toolkit'; import React from 'react'; +import type { AdvancedFilterParams } from '../../types/api'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { AdvancedFilterParams } from 'types/api/advancedFilter'; import useApiQuery from 'client/api/hooks/useApiQuery'; @@ -21,7 +21,7 @@ import { ClearButton } from 'toolkit/components/buttons/ClearButton'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; -import { NATIVE_TOKEN } from '../constants'; +import { NATIVE_TOKEN } from '../../utils/consts'; const FILTER_PARAM_INCLUDE = 'token_contract_address_hashes_to_include'; const FILTER_PARAM_EXCLUDE = 'token_contract_address_hashes_to_exclude'; diff --git a/ui/advancedFilter/filters/BaseAgeFilter.tsx b/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx similarity index 97% rename from ui/advancedFilter/filters/BaseAgeFilter.tsx rename to client/features/advanced-filter/components/filters/BaseAgeFilter.tsx index 468557280c..a552926847 100644 --- a/ui/advancedFilter/filters/BaseAgeFilter.tsx +++ b/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx @@ -5,7 +5,7 @@ import { isEqual } from 'es-toolkit'; import type { ChangeEvent } from 'react'; import React from 'react'; -import { ADVANCED_FILTER_AGES, type AdvancedFilterAge } from 'types/api/advancedFilter'; +import { ADVANCED_FILTER_AGES, type AdvancedFilterAge } from '../../types/api'; import dayjs from 'lib/date/dayjs'; import { Input } from 'toolkit/chakra/input'; @@ -14,7 +14,7 @@ import { ndash } from 'toolkit/utils/htmlEntities'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect'; -import { getDurationFromAge } from '../lib'; +import { getDurationFromAge } from '../../utils/lib'; const defaultValue = { age: '', from: '', to: '' } as const; type AgeFromToValue = { age: AdvancedFilterAge | ''; from: string; to: string }; diff --git a/ui/advancedFilter/filters/MethodFilter.tsx b/client/features/advanced-filter/components/filters/MethodFilter.tsx similarity index 99% rename from ui/advancedFilter/filters/MethodFilter.tsx rename to client/features/advanced-filter/components/filters/MethodFilter.tsx index 3480961bca..907ca9f9eb 100644 --- a/ui/advancedFilter/filters/MethodFilter.tsx +++ b/client/features/advanced-filter/components/filters/MethodFilter.tsx @@ -4,7 +4,7 @@ import { Flex, Spinner, chakra } from '@chakra-ui/react'; import { isEqual, differenceBy } from 'es-toolkit'; import React from 'react'; -import type { AdvancedFilterMethodInfo, AdvancedFilterParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterMethodInfo, AdvancedFilterParams } from '../../types/api'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/ui/advancedFilter/filters/TypeFilter.tsx b/client/features/advanced-filter/components/filters/TypeFilter.tsx similarity index 94% rename from ui/advancedFilter/filters/TypeFilter.tsx rename to client/features/advanced-filter/components/filters/TypeFilter.tsx index 4f18aa64db..300a36d380 100644 --- a/ui/advancedFilter/filters/TypeFilter.tsx +++ b/client/features/advanced-filter/components/filters/TypeFilter.tsx @@ -4,13 +4,13 @@ import { Flex } from '@chakra-ui/react'; import { isEqual, without } from 'es-toolkit'; import React from 'react'; -import type { AdvancedFilterParams, AdvancedFilterType } from 'types/api/advancedFilter'; +import type { AdvancedFilterParams, AdvancedFilterType } from '../../types/api'; import { useMultichainContext } from 'lib/contexts/multichain'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; -import { getAdvancedFilterTypes } from '../constants'; +import { getAdvancedFilterTypes } from '../../utils/lib'; const RESET_VALUE = 'all'; diff --git a/mocks/advancedFilter/advancedFilter.ts b/client/features/advanced-filter/mocks.ts similarity index 97% rename from mocks/advancedFilter/advancedFilter.ts rename to client/features/advanced-filter/mocks.ts index f1f7a35e6e..66804e9e39 100644 --- a/mocks/advancedFilter/advancedFilter.ts +++ b/client/features/advanced-filter/mocks.ts @@ -1,4 +1,4 @@ -import type { AdvancedFilterResponse } from 'types/api/advancedFilter'; +import type { AdvancedFilterResponse } from './types/api'; export const baseResponse: AdvancedFilterResponse = { items: [ diff --git a/ui/pages/AdvancedFilter.pw.tsx b/client/features/advanced-filter/pages/index/AdvancedFilter.pw.tsx similarity index 89% rename from ui/pages/AdvancedFilter.pw.tsx rename to client/features/advanced-filter/pages/index/AdvancedFilter.pw.tsx index f37ef9dfd6..e0f9afbc77 100644 --- a/ui/pages/AdvancedFilter.pw.tsx +++ b/client/features/advanced-filter/pages/index/AdvancedFilter.pw.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import * as advancedFilterMock from 'mocks/advancedFilter/advancedFilter'; import { test, expect } from 'playwright/lib'; +import * as advancedFilterMock from '../../mocks'; import AdvancedFilter from './AdvancedFilter'; test('base view +@dark-mode', async({ render, mockApiResponse, mockTextAd }) => { diff --git a/ui/pages/AdvancedFilter.tsx b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx similarity index 95% rename from ui/pages/AdvancedFilter.tsx rename to client/features/advanced-filter/pages/index/AdvancedFilter.tsx index 5cff4e2d41..bbeb0d5ef3 100644 --- a/ui/pages/AdvancedFilter.tsx +++ b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx @@ -11,8 +11,9 @@ import { omit } from 'es-toolkit'; import { useRouter } from 'next/router'; import React from 'react'; -import type { AdvancedFilterParams } from 'types/api/advancedFilter'; -import { ADVANCED_FILTER_AGES, ADVANCED_FILTER_ADDRESS_RELATION } from 'types/api/advancedFilter'; +import type { AdvancedFilterParams } from '../../types/api'; +import { ADVANCED_FILTER_AGES, ADVANCED_FILTER_ADDRESS_RELATION } from '../../types/api'; +import type { ColumnsIds } from '../../types/client'; import useApiQuery from 'client/api/hooks/useApiQuery'; @@ -27,17 +28,10 @@ import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from- import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; -import { ADVANCED_FILTER_ITEM } from 'stubs/advancedFilter'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; import { TableBody, TableCell, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; -import ColumnsButton from 'ui/advancedFilter/ColumnsButton'; -import type { ColumnsIds } from 'ui/advancedFilter/constants'; -import { getAdvancedFilterTypes, TABLE_COLUMNS } from 'ui/advancedFilter/constants'; -import FilterByColumn from 'ui/advancedFilter/FilterByColumn'; -import ItemByColumn from 'ui/advancedFilter/ItemByColumn'; -import { getDurationFromAge, getFilterTags } from 'ui/advancedFilter/lib'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; @@ -47,6 +41,13 @@ import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; +import ColumnsButton from '../../components/ColumnsButton'; +import FilterByColumn from '../../components/FilterByColumn'; +import ItemByColumn from '../../components/ItemByColumn'; +import { ADVANCED_FILTER_ITEM } from '../../stubs'; +import { TABLE_COLUMNS } from '../../utils/consts'; +import { getAdvancedFilterTypes, getDurationFromAge, getFilterTags } from '../../utils/lib'; + const COLUMNS_CHECKED = {} as Record; TABLE_COLUMNS.forEach(c => COLUMNS_CHECKED[c.id] = true); diff --git a/ui/pages/__screenshots__/AdvancedFilter.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/advanced-filter/pages/index/__screenshots__/AdvancedFilter.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/AdvancedFilter.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/features/advanced-filter/pages/index/__screenshots__/AdvancedFilter.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/AdvancedFilter.pw.tsx_default_base-view-dark-mode-1.png b/client/features/advanced-filter/pages/index/__screenshots__/AdvancedFilter.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/AdvancedFilter.pw.tsx_default_base-view-dark-mode-1.png rename to client/features/advanced-filter/pages/index/__screenshots__/AdvancedFilter.pw.tsx_default_base-view-dark-mode-1.png diff --git a/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx b/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx index 4adbc0b22e..94344f423d 100644 --- a/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx +++ b/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx @@ -9,7 +9,8 @@ import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; import type { LinkProps } from 'toolkit/chakra/link'; -import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; + +import AdvancedFilterLink from '../../components/AdvancedFilterLink'; interface Props extends LinkProps { isLoading?: boolean; diff --git a/stubs/advancedFilter.ts b/client/features/advanced-filter/stubs.ts similarity index 79% rename from stubs/advancedFilter.ts rename to client/features/advanced-filter/stubs.ts index 2f58c0b7a4..4f5a92c826 100644 --- a/stubs/advancedFilter.ts +++ b/client/features/advanced-filter/stubs.ts @@ -1,4 +1,6 @@ -import type { AdvancedFilterResponseItem } from 'types/api/advancedFilter'; +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { AdvancedFilterResponseItem } from './types/api'; import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; import { TX_HASH } from 'client/slices/tx/stubs/tx'; diff --git a/types/api/advancedFilter.ts b/client/features/advanced-filter/types/api.ts similarity index 100% rename from types/api/advancedFilter.ts rename to client/features/advanced-filter/types/api.ts diff --git a/client/features/advanced-filter/types/client.ts b/client/features/advanced-filter/types/client.ts new file mode 100644 index 0000000000..b1f6f3da48 --- /dev/null +++ b/client/features/advanced-filter/types/client.ts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +export type ColumnsIds = 'tx_hash' | 'type' | 'method' | 'age' | 'from' | 'or_and' | 'to' | 'amount' | 'asset' | 'fee'; + +export type TxTableColumn = { + id: ColumnsIds; + name: string; + width: string; + isNumeric?: boolean; +}; diff --git a/ui/advancedFilter/constants.ts b/client/features/advanced-filter/utils/consts.ts similarity index 53% rename from ui/advancedFilter/constants.ts rename to client/features/advanced-filter/utils/consts.ts index 2809e3945a..a531661046 100644 --- a/ui/advancedFilter/constants.ts +++ b/client/features/advanced-filter/utils/consts.ts @@ -1,20 +1,11 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { AdvancedFilterParams } from '../types/api'; +import type { TxTableColumn } from '../types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; -import { getTokenTypes } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import config from 'configs/app'; -export type ColumnsIds = 'tx_hash' | 'type' | 'method' | 'age' | 'from' | 'or_and' | 'to' | 'amount' | 'asset' | 'fee'; - -type TxTableColumn = { - id: ColumnsIds; - name: string; - width: string; - isNumeric?: boolean; -}; - export const TABLE_COLUMNS: Array = [ { id: 'tx_hash', @@ -70,32 +61,6 @@ export const TABLE_COLUMNS: Array = [ }, ] as const; -export const getAdvancedFilterTypes = (chainConfig?: Array | ClusterChainConfig['app_config'], withAll = false) => { - return [ - ...(withAll ? [ { - id: 'all', - name: 'All', - } ] : []), - { - id: 'coin_transfer', - name: 'Coin Transfer', - }, - ...Object.entries(getTokenTypes(false, chainConfig)) - .map(([ id, name ]) => ({ - id, - name: `${ name } Transfer`, - })), - { - id: 'contract_creation', - name: 'Contract Creation', - }, - { - id: 'contract_interaction', - name: 'Contract Interaction', - }, - ]; -}; - export const NATIVE_TOKEN = { name: config.chain.currency.name || '', icon_url: '', @@ -103,3 +68,24 @@ export const NATIVE_TOKEN = { address_hash: 'native', type: 'ERC-20' as const, } as TokenInfo; + +export const FILTER_PARAM_NAMES: Record = { + // we don't show address_relation as filter tag + address_relation: '', + age: 'Age', + age_from: 'Date from', + age_to: 'Date to', + amount_from: 'Amount from', + amount_to: 'Amount to', + from_address_hashes_to_exclude: 'From Exc', + from_address_hashes_to_include: 'From', + methods: 'Methods', + methods_names: '', + to_address_hashes_to_exclude: 'To Exc', + to_address_hashes_to_include: 'To', + token_contract_address_hashes_to_exclude: 'Asset Exc', + token_contract_symbols_to_exclude: '', + token_contract_address_hashes_to_include: 'Asset', + token_contract_symbols_to_include: '', + transaction_types: 'Type', +}; diff --git a/ui/advancedFilter/lib.ts b/client/features/advanced-filter/utils/lib.ts similarity index 75% rename from ui/advancedFilter/lib.ts rename to client/features/advanced-filter/utils/lib.ts index a5f0ed44fe..927faa8131 100644 --- a/ui/advancedFilter/lib.ts +++ b/client/features/advanced-filter/utils/lib.ts @@ -2,13 +2,40 @@ import { castArray } from 'es-toolkit/compat'; -import type { AdvancedFilterAge, AdvancedFilterParams } from 'types/api/advancedFilter'; +import type { AdvancedFilterAge, AdvancedFilterParams } from '../types/api'; +import { getTokenTypes } from 'client/slices/token/utils/token-types'; import type { ClusterChainConfig } from 'types/multichain'; import dayjs from 'lib/date/dayjs'; import { HOUR, DAY, MONTH } from 'toolkit/utils/consts'; -import { getAdvancedFilterTypes } from './constants'; +import { FILTER_PARAM_NAMES } from './consts'; + +export const getAdvancedFilterTypes = (chainConfig?: Array | ClusterChainConfig['app_config'], withAll = false) => { + return [ + ...(withAll ? [ { + id: 'all', + name: 'All', + } ] : []), + { + id: 'coin_transfer', + name: 'Coin Transfer', + }, + ...Object.entries(getTokenTypes(false, chainConfig)) + .map(([ id, name ]) => ({ + id, + name: `${ name } Transfer`, + })), + { + id: 'contract_creation', + name: 'Contract Creation', + }, + { + id: 'contract_interaction', + name: 'Contract Interaction', + }, + ]; +}; export function getDurationFromAge(age: AdvancedFilterAge) { switch (age) { @@ -37,27 +64,6 @@ function getFilterValueWithNames(values?: Array, names?: Array) } } -const filterParamNames: Record = { - // we don't show address_relation as filter tag - address_relation: '', - age: 'Age', - age_from: 'Date from', - age_to: 'Date to', - amount_from: 'Amount from', - amount_to: 'Amount to', - from_address_hashes_to_exclude: 'From Exc', - from_address_hashes_to_include: 'From', - methods: 'Methods', - methods_names: '', - to_address_hashes_to_exclude: 'To Exc', - to_address_hashes_to_include: 'To', - token_contract_address_hashes_to_exclude: 'Asset Exc', - token_contract_symbols_to_exclude: '', - token_contract_address_hashes_to_include: 'Asset', - token_contract_symbols_to_include: '', - transaction_types: 'Type', -}; - export function getFilterTags(filters: AdvancedFilterParams, chainConfig?: ClusterChainConfig['app_config']) { const filtersToShow = { ...filters }; if (filtersToShow.age) { @@ -69,7 +75,7 @@ export function getFilterTags(filters: AdvancedFilterParams, chainConfig?: Clust if (!value) { return; } - const name = filterParamNames[key as keyof AdvancedFilterParams]; + const name = FILTER_PARAM_NAMES[key as keyof AdvancedFilterParams]; if (!name) { return; } diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/TransactionsZetaChain.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/TransactionsZetaChain.tsx index 6c47e85bef..7f3c51d67c 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/TransactionsZetaChain.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/TransactionsZetaChain.tsx @@ -13,6 +13,7 @@ import { TX } from 'client/slices/tx/stubs/tx'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; import TxsWatchlist from 'client/features/account/pages/tx-index-watchlist/TxsWatchlist'; +import AdvancedFilterLink from 'client/features/advanced-filter/components/AdvancedFilterLink'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; @@ -21,7 +22,6 @@ import config from 'configs/app'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import ActionBar from 'ui/shared/ActionBar'; -import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx index 3c107e715e..403fe1d9f7 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx @@ -1,11 +1,12 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { AdvancedFilterAge } from 'client/features/advanced-filter/types/api'; import type { ZetaChainCCTXFilterParams } from 'client/features/chain-variants/zeta-chain/types/client'; -import type { AdvancedFilterAge } from 'types/api/advancedFilter'; + +import BaseAgeFilter, { type DateConverter } from 'client/features/advanced-filter/components/filters/BaseAgeFilter'; import dayjs from 'lib/date/dayjs'; import { SECOND } from 'toolkit/utils/consts'; -import BaseAgeFilter, { type DateConverter } from 'ui/advancedFilter/filters/BaseAgeFilter'; const FILTER_PARAM_FROM = 'start_timestamp'; const FILTER_PARAM_TO = 'end_timestamp'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx index 8d8e596807..8a27c9300e 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx @@ -5,11 +5,12 @@ import { useRouter } from 'next/router'; import React from 'react'; import { Direction } from '@blockscout/zetachain-cctx-types'; +import { ADVANCED_FILTER_AGES, type AdvancedFilterAge } from 'client/features/advanced-filter/types/api'; import { ZETA_CHAIN_CCTX_COIN_TYPE_FILTER, ZETA_CHAIN_CCTX_STATUS_REDUCED_FILTERS } from 'client/features/chain-variants/zeta-chain/types/client'; import type { CoinTypeFilter, StatusReducedFilters, ZetaChainCCTXFilterParams } from 'client/features/chain-variants/zeta-chain/types/client'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; -import { ADVANCED_FILTER_AGES, type AdvancedFilterAge } from 'types/api/advancedFilter'; +import { getDurationFromAge } from 'client/features/advanced-filter/utils/lib'; import { ZETA_CHAIN_CCTX_LIST_ITEM } from 'client/features/chain-variants/zeta-chain/stubs'; import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; @@ -20,7 +21,6 @@ import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from- import dayjs from 'lib/date/dayjs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import { getDurationFromAge } from 'ui/advancedFilter/lib'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainEvmTransactions.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainEvmTransactions.tsx index 34b8f46ab4..5369963a98 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainEvmTransactions.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainEvmTransactions.tsx @@ -11,6 +11,8 @@ import TxsWithFrontendSorting from 'client/slices/tx/pages/index/list/TxsWithFro import TxsStats from 'client/slices/tx/pages/index/stats/TxsStats'; import { TX } from 'client/slices/tx/stubs/tx'; +import AdvancedFilterLink from 'client/features/advanced-filter/components/AdvancedFilterLink'; + import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; @@ -18,7 +20,6 @@ import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterByColumn.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterByColumn.tsx index d96ba49182..3500ac8ed9 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterByColumn.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterByColumn.tsx @@ -3,9 +3,9 @@ import { castArray } from 'es-toolkit/compat'; import React from 'react'; +import type { AdvancedFilterAge } from 'client/features/advanced-filter/types/api'; import { ZETA_CHAIN_CCTX_COIN_TYPE_FILTER, type ZetaChainCCTXFilterParams } from 'client/features/chain-variants/zeta-chain/types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { AdvancedFilterAge } from 'types/api/advancedFilter'; import TableColumnFilterWrapper from 'ui/shared/filters/TableColumnFilterWrapper'; diff --git a/client/features/chain-variants/zeta-chain/types/client.ts b/client/features/chain-variants/zeta-chain/types/client.ts index 817aed758b..409b3e585f 100644 --- a/client/features/chain-variants/zeta-chain/types/client.ts +++ b/client/features/chain-variants/zeta-chain/types/client.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { CctxListItem } from '@blockscout/zetachain-cctx-types'; -import type { AdvancedFilterAge } from 'types/api/advancedFilter'; +import type { AdvancedFilterAge } from 'client/features/advanced-filter/types/api'; import type { ExternalChain } from 'types/externalChains'; export interface SearchResultZetaChainCCTX { diff --git a/client/slices/tx/pages/index/list/TxsTabs.tsx b/client/slices/tx/pages/index/list/TxsTabs.tsx index ad61f5bf92..c6d9451b65 100644 --- a/client/slices/tx/pages/index/list/TxsTabs.tsx +++ b/client/slices/tx/pages/index/list/TxsTabs.tsx @@ -11,6 +11,7 @@ import { TX } from 'client/slices/tx/stubs/tx'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; import TxsWatchlist from 'client/features/account/pages/tx-index-watchlist/TxsWatchlist'; +import AdvancedFilterLink from 'client/features/advanced-filter/components/AdvancedFilterLink'; import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; @@ -22,7 +23,6 @@ import { useMultichainContext } from 'lib/contexts/multichain'; import { generateListStub } from 'stubs/utils'; import type { RoutedTabsProps } from 'toolkit/components/RoutedTabs/RoutedTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/pages/advanced-filter.tsx b/pages/advanced-filter.tsx index e94d70849d..b49596dc8a 100644 --- a/pages/advanced-filter.tsx +++ b/pages/advanced-filter.tsx @@ -5,7 +5,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import AdvancedFilter from 'ui/pages/AdvancedFilter'; +import AdvancedFilter from 'client/features/advanced-filter/pages/index/AdvancedFilter'; const Page: NextPage = () => { return ( diff --git a/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx index 7d903ca057..d657319edf 100644 --- a/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx +++ b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx @@ -5,8 +5,9 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; +import AdvancedFilter from 'client/features/advanced-filter/pages/index/AdvancedFilter'; + import { MultichainProvider } from 'lib/contexts/multichain'; -import AdvancedFilter from 'ui/pages/AdvancedFilter'; const Page: NextPage = () => { return ( From 6ffa1930e0428aafb6995f31559235134ce64fe5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 19:24:27 +0200 Subject: [PATCH 14/85] [Migration 6-21] Feature: tx-interpretation (#3473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Migration 6-21] Feature: tx-interpretation — move types, stubs, mocks, utils, component Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/db0c8652-ae94-4996-a4b2-ee755a01fce0 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- client/api/services/general/misc.ts | 4 ++-- client/api/services/general/tx.ts | 2 +- .../pages/tx/TxDetailsActionsInterpretation.tsx | 2 +- .../features/tx-interpretation/blockscout/mocks.ts | 2 +- .../features/tx-interpretation/blockscout/stubs.ts | 2 +- .../common/components/TxInterpretation.tsx | 4 ++-- .../features/tx-interpretation/common/types/api.ts | 0 .../features/tx-interpretation/common/types/config.ts | 0 client/features/tx-interpretation/common/utils/utils.ts | 2 +- .../tx-interpretation/noves/components}/NovesFromTo.tsx | 5 ++--- .../noves/components/NovesTokenTooltipContent.tsx | 2 +- .../features/tx-interpretation/noves/mocks.ts | 2 +- .../noves/pages/address/AddressAccountHistory.tsx | 9 +++++---- .../noves/pages/address/AddressAccountHistoryFilter.tsx | 2 +- .../pages/address/AddressAccountHistoryListItem.tsx | 5 +++-- .../pages/address/AddressAccountHistoryTableItem.tsx | 5 +++-- .../noves/pages/tx-asset-flows/TxAssetFlows.tsx | 3 ++- .../noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx | 3 ++- .../noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx | 3 ++- .../features/tx-interpretation/noves/stubs.ts | 2 +- .../features/tx-interpretation/noves/types/api.ts | 0 .../noves/utils/createNovesSummaryObject.spec.ts | 3 ++- .../noves/utils/createNovesSummaryObject.ts | 4 ++-- .../tx-interpretation/noves/utils/from-to.spec.ts | 4 ++-- .../features/tx-interpretation/noves/utils/from-to.ts | 2 +- .../noves/utils/generateFlowViewData.spec.ts | 3 ++- .../noves/utils/generateFlowViewData.ts | 2 +- .../noves/utils/getAddressValues.spec.ts | 3 ++- .../tx-interpretation/noves/utils/getAddressValues.ts | 2 +- .../tx-interpretation/noves/utils/getTokensData.spec.ts | 3 ++- .../tx-interpretation/noves/utils/getTokensData.ts | 2 +- .../user-ops/pages/details/UserOpDetailsActions.tsx | 2 +- .../features/user-ops/pages/details/UserOpSubHeading.tsx | 2 +- client/slices/tx/pages/details/TxSubHeading.pw.tsx | 5 +++-- client/slices/tx/pages/details/TxSubHeading.tsx | 4 ++-- client/slices/tx/pages/index/list/TxsListItem.tsx | 2 +- client/slices/tx/pages/index/list/TxsTableItem.tsx | 2 +- configs/app/features/tx-interpretation.ts | 4 ++-- deploy/tools/envs-validator/schema.ts | 2 +- 39 files changed, 60 insertions(+), 50 deletions(-) rename mocks/txs/txInterpretation.ts => client/features/tx-interpretation/blockscout/mocks.ts (93%) rename stubs/txInterpretation.ts => client/features/tx-interpretation/blockscout/stubs.ts (91%) rename types/api/txInterpretation.ts => client/features/tx-interpretation/common/types/api.ts (100%) rename types/client/txInterpretation.ts => client/features/tx-interpretation/common/types/config.ts (100%) rename {ui/shared/Noves => client/features/tx-interpretation/noves/components}/NovesFromTo.tsx (89%) rename mocks/noves/transaction.ts => client/features/tx-interpretation/noves/mocks.ts (96%) rename stubs/noves/NovesTranslate.ts => client/features/tx-interpretation/noves/stubs.ts (93%) rename types/api/noves.ts => client/features/tx-interpretation/noves/types/api.ts (100%) rename ui/shared/Noves/utils.spec.ts => client/features/tx-interpretation/noves/utils/from-to.spec.ts (89%) rename ui/shared/Noves/utils.ts => client/features/tx-interpretation/noves/utils/from-to.ts (96%) diff --git a/client/api/services/general/misc.ts b/client/api/services/general/misc.ts index 29eac88cff..bb471a93e3 100644 --- a/client/api/services/general/misc.ts +++ b/client/api/services/general/misc.ts @@ -14,6 +14,8 @@ import type { import type { OptimisticL2DepositsItem, } from 'client/features/rollup/optimism/types/api'; +import type { TxInterpretationResponse } from 'client/features/tx-interpretation/common/types/api'; +import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'client/features/user-ops/types/api'; import type { Block } from 'client/slices/block/types/api'; import type { HomeStats } from 'client/slices/home/types/api'; @@ -24,8 +26,6 @@ import type { import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig } from 'types/api/configs'; import type { IndexingStatus } from 'types/api/indexingStatus'; -import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'types/api/noves'; -import type { TxInterpretationResponse } from 'types/api/txInterpretation'; import type { ValidatorsStabilityCountersResponse, ValidatorsStabilityFilters, diff --git a/client/api/services/general/tx.ts b/client/api/services/general/tx.ts index b6a0b50eac..4ba1f4831d 100644 --- a/client/api/services/general/tx.ts +++ b/client/api/services/general/tx.ts @@ -3,6 +3,7 @@ import type { ApiResource } from '../../types'; import type { TransactionsResponseWatchlist } from 'client/features/account/types/api'; import type { TransactionsResponseWithBlobs, TxsWithBlobsFilters, TxBlobs } from 'client/features/data-availability/types/api'; +import type { TxInterpretationResponse } from 'client/features/tx-interpretation/common/types/api'; import type { InternalTransactionFilters, InternalTransactionsResponse } from 'client/slices/internal-tx/types/api'; import type { LogsResponseTx } from 'client/slices/log/types/api'; import type { TokenTransferResponse, TokenTransferFilters } from 'client/slices/token-transfer/types/api'; @@ -16,7 +17,6 @@ import type { TxRawTracesResponse, } from 'client/slices/tx/types/api'; import type { FheOperationsResponse } from 'types/api/fheOperations'; -import type { TxInterpretationResponse } from 'types/api/txInterpretation'; export const GENERAL_API_TX_RESOURCES = { txs_stats: { diff --git a/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx b/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx index 5f65882846..49913acb82 100644 --- a/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx @@ -4,9 +4,9 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; -import { TX_INTERPRETATION } from 'stubs/txInterpretation'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoActionsWrapper from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; diff --git a/mocks/txs/txInterpretation.ts b/client/features/tx-interpretation/blockscout/mocks.ts similarity index 93% rename from mocks/txs/txInterpretation.ts rename to client/features/tx-interpretation/blockscout/mocks.ts index 2fb296be0b..126dd7e765 100644 --- a/mocks/txs/txInterpretation.ts +++ b/client/features/tx-interpretation/blockscout/mocks.ts @@ -1,4 +1,4 @@ -import type { TxInterpretationResponse } from 'types/api/txInterpretation'; +import type { TxInterpretationResponse } from 'client/features/tx-interpretation/common/types/api'; import { hash } from 'client/slices/address/mocks/address'; diff --git a/stubs/txInterpretation.ts b/client/features/tx-interpretation/blockscout/stubs.ts similarity index 91% rename from stubs/txInterpretation.ts rename to client/features/tx-interpretation/blockscout/stubs.ts index ca0c953175..31181590bb 100644 --- a/stubs/txInterpretation.ts +++ b/client/features/tx-interpretation/blockscout/stubs.ts @@ -1,4 +1,4 @@ -import type { TxInterpretationResponse } from 'types/api/txInterpretation'; +import type { TxInterpretationResponse } from 'client/features/tx-interpretation/common/types/api'; import { TOKEN_INFO_ERC_20 } from 'client/slices/token/stubs'; diff --git a/client/features/tx-interpretation/common/components/TxInterpretation.tsx b/client/features/tx-interpretation/common/components/TxInterpretation.tsx index 1dfdef9c07..21f2a76c4c 100644 --- a/client/features/tx-interpretation/common/components/TxInterpretation.tsx +++ b/client/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -5,12 +5,12 @@ import { Box, chakra } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressParam } from 'client/slices/address/types/api'; import type { TxInterpretationSummary, TxInterpretationVariable, TxInterpretationVariableString, -} from 'types/api/txInterpretation'; +} from 'client/features/tx-interpretation/common/types/api'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; diff --git a/types/api/txInterpretation.ts b/client/features/tx-interpretation/common/types/api.ts similarity index 100% rename from types/api/txInterpretation.ts rename to client/features/tx-interpretation/common/types/api.ts diff --git a/types/client/txInterpretation.ts b/client/features/tx-interpretation/common/types/config.ts similarity index 100% rename from types/client/txInterpretation.ts rename to client/features/tx-interpretation/common/types/config.ts diff --git a/client/features/tx-interpretation/common/utils/utils.ts b/client/features/tx-interpretation/common/utils/utils.ts index 36dcb70901..49ba9cf785 100644 --- a/client/features/tx-interpretation/common/utils/utils.ts +++ b/client/features/tx-interpretation/common/utils/utils.ts @@ -2,7 +2,7 @@ // we use that regex as a separator when splitting template and don't want to capture variables -import type { TxInterpretationVariable } from 'types/api/txInterpretation'; +import type { TxInterpretationVariable } from 'client/features/tx-interpretation/common/types/api'; // eslint-disable-next-line regexp/no-useless-non-capturing-group export const VAR_REGEXP = /\{(?:[^}]+)\}/g; diff --git a/ui/shared/Noves/NovesFromTo.tsx b/client/features/tx-interpretation/noves/components/NovesFromTo.tsx similarity index 89% rename from ui/shared/Noves/NovesFromTo.tsx rename to client/features/tx-interpretation/noves/components/NovesFromTo.tsx index b550eb60ce..251cfde038 100644 --- a/ui/shared/Noves/NovesFromTo.tsx +++ b/client/features/tx-interpretation/noves/components/NovesFromTo.tsx @@ -4,17 +4,16 @@ import { Box } from '@chakra-ui/react'; import type { FC } from 'react'; import React from 'react'; -import type { NovesResponseData } from 'types/api/noves'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { getActionFromTo, getFromTo } from 'client/features/tx-interpretation/noves/utils/from-to'; import type { NovesFlowViewItem } from 'client/features/tx-interpretation/noves/utils/generateFlowViewData'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import { getActionFromTo, getFromTo } from './utils'; - interface Props { isLoaded: boolean; txData?: NovesResponseData; diff --git a/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx b/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx index 0205b9bb50..5e1932208e 100644 --- a/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx +++ b/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx @@ -4,7 +4,7 @@ import { Box, Text } from '@chakra-ui/react'; import type { FC } from 'react'; import React from 'react'; -import type { NovesNft, NovesToken } from 'types/api/noves'; +import type { NovesNft, NovesToken } from 'client/features/tx-interpretation/noves/types/api'; import shortenString from 'client/shared/text/shorten-string'; diff --git a/mocks/noves/transaction.ts b/client/features/tx-interpretation/noves/mocks.ts similarity index 96% rename from mocks/noves/transaction.ts rename to client/features/tx-interpretation/noves/mocks.ts index 09f2e0a66d..f57313622d 100644 --- a/mocks/noves/transaction.ts +++ b/client/features/tx-interpretation/noves/mocks.ts @@ -1,4 +1,4 @@ -import type { NovesResponseData } from 'types/api/noves'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import type { TokensData } from 'client/features/tx-interpretation/noves/utils/getTokensData'; diff --git a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx index c5aa762cfe..7f8b09050c 100644 --- a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx @@ -4,19 +4,20 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { NovesHistoryFilterValue } from 'types/api/noves'; -import { NovesHistoryFilterValues } from 'types/api/noves'; +import type { NovesHistoryFilterValue } from 'client/features/tx-interpretation/noves/types/api'; +import { NovesHistoryFilterValues } from 'client/features/tx-interpretation/noves/types/api'; + +import { NOVES_TRANSLATE } from 'client/features/tx-interpretation/noves/stubs'; +import { getFromToValue } from 'client/features/tx-interpretation/noves/utils/from-to'; import useIsMounted from 'client/shared/hooks/useIsMounted'; import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; import { generateListStub } from 'stubs/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import { getFromToValue } from 'ui/shared/Noves/utils'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx index 3123b1bf7b..d5aa9fa3c0 100644 --- a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx @@ -3,7 +3,7 @@ import { createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { NovesHistoryFilterValue } from 'types/api/noves'; +import type { NovesHistoryFilterValue } from 'client/features/tx-interpretation/noves/types/api'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; diff --git a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx index f218e4a1f9..b2dd5f9fd5 100644 --- a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx @@ -3,14 +3,15 @@ import { Box, Flex, Text } from '@chakra-ui/react'; import React, { useMemo } from 'react'; -import type { NovesResponseData } from 'types/api/noves'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; + +import NovesFromTo from 'client/features/tx-interpretation/noves/components/NovesFromTo'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { SECOND } from 'toolkit/utils/consts'; import IconSvg from 'ui/shared/IconSvg'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; type Props = { diff --git a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx index 195ded5a5f..ca30c4367a 100644 --- a/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx @@ -3,14 +3,15 @@ import { Box } from '@chakra-ui/react'; import React, { useMemo } from 'react'; -import type { NovesResponseData } from 'types/api/noves'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; + +import NovesFromTo from 'client/features/tx-interpretation/noves/components/NovesFromTo'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { SECOND } from 'toolkit/utils/consts'; import IconSvg from 'ui/shared/IconSvg'; -import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; type Props = { diff --git a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx index adf65d1128..d2f6dc095f 100644 --- a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx @@ -10,7 +10,8 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; +import { NOVES_TRANSLATE } from 'client/features/tx-interpretation/noves/stubs'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx index 297574953a..6d54b6f327 100644 --- a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx @@ -3,10 +3,11 @@ import { Box, Text } from '@chakra-ui/react'; import React from 'react'; +import NovesFromTo from 'client/features/tx-interpretation/noves/components/NovesFromTo'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import IconSvg from 'ui/shared/IconSvg'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import NovesActionSnippet from '../../components/NovesActionSnippet'; import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; diff --git a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx index 1b72fa9458..68710e83c1 100644 --- a/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx @@ -2,8 +2,9 @@ import React from 'react'; +import NovesFromTo from 'client/features/tx-interpretation/noves/components/NovesFromTo'; + import { TableRow, TableCell } from 'toolkit/chakra/table'; -import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; import NovesActionSnippet from '../../components/NovesActionSnippet'; import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; diff --git a/stubs/noves/NovesTranslate.ts b/client/features/tx-interpretation/noves/stubs.ts similarity index 93% rename from stubs/noves/NovesTranslate.ts rename to client/features/tx-interpretation/noves/stubs.ts index 848ed6dab9..0d360415fa 100644 --- a/stubs/noves/NovesTranslate.ts +++ b/client/features/tx-interpretation/noves/stubs.ts @@ -1,4 +1,4 @@ -import type { NovesResponseData, NovesClassificationData, NovesRawTransactionData } from 'types/api/noves'; +import type { NovesResponseData, NovesClassificationData, NovesRawTransactionData } from 'client/features/tx-interpretation/noves/types/api'; const NOVES_TRANSLATE_CLASSIFIED: NovesClassificationData = { description: 'Sent 0.04 ETH', diff --git a/types/api/noves.ts b/client/features/tx-interpretation/noves/types/api.ts similarity index 100% rename from types/api/noves.ts rename to client/features/tx-interpretation/noves/types/api.ts diff --git a/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts index 0ce96fb1a8..fe25535f3d 100644 --- a/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts +++ b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts @@ -1,4 +1,5 @@ -import * as transactionMock from 'mocks/noves/transaction'; +import * as transactionMock from 'client/features/tx-interpretation/noves/mocks'; + import { it, expect } from 'vitest'; import { createNovesSummaryObject } from './createNovesSummaryObject'; diff --git a/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts index 867bb05ae2..7cef444b0a 100644 --- a/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts +++ b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { NovesResponseData } from 'types/api/noves'; -import type { TxInterpretationSummary } from 'types/api/txInterpretation'; +import type { TxInterpretationSummary } from 'client/features/tx-interpretation/common/types/api'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import { createAddressValues } from './getAddressValues'; import type { NovesTokenInfo, TokensData } from './getTokensData'; diff --git a/ui/shared/Noves/utils.spec.ts b/client/features/tx-interpretation/noves/utils/from-to.spec.ts similarity index 89% rename from ui/shared/Noves/utils.spec.ts rename to client/features/tx-interpretation/noves/utils/from-to.spec.ts index 7151aa5f97..51cbad8a19 100644 --- a/ui/shared/Noves/utils.spec.ts +++ b/client/features/tx-interpretation/noves/utils/from-to.spec.ts @@ -1,9 +1,9 @@ +import * as transactionMock from 'client/features/tx-interpretation/noves/mocks'; import type { NovesFlowViewItem } from 'client/features/tx-interpretation/noves/utils/generateFlowViewData'; -import * as transactionMock from 'mocks/noves/transaction'; import { it, expect } from 'vitest'; -import { getActionFromTo, getFromTo, getFromToValue } from './utils'; +import { getActionFromTo, getFromTo, getFromToValue } from './from-to'; it('get data for FromTo component from transaction', async() => { const result = getFromTo(transactionMock.transaction, transactionMock.transaction.accountAddress); diff --git a/ui/shared/Noves/utils.ts b/client/features/tx-interpretation/noves/utils/from-to.ts similarity index 96% rename from ui/shared/Noves/utils.ts rename to client/features/tx-interpretation/noves/utils/from-to.ts index 46b2ba2749..2f5f713cda 100644 --- a/ui/shared/Noves/utils.ts +++ b/client/features/tx-interpretation/noves/utils/from-to.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { NovesResponseData, NovesSentReceived } from 'types/api/noves'; +import type { NovesResponseData, NovesSentReceived } from 'client/features/tx-interpretation/noves/types/api'; import type { NovesFlowViewItem } from 'client/features/tx-interpretation/noves/utils/generateFlowViewData'; diff --git a/client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts b/client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts index eb8181d83d..3563c0d396 100644 --- a/client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts +++ b/client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts @@ -1,4 +1,5 @@ -import * as transactionMock from 'mocks/noves/transaction'; +import * as transactionMock from 'client/features/tx-interpretation/noves/mocks'; + import { it, expect } from 'vitest'; import { generateFlowViewData } from './generateFlowViewData'; diff --git a/client/features/tx-interpretation/noves/utils/generateFlowViewData.ts b/client/features/tx-interpretation/noves/utils/generateFlowViewData.ts index c06046b7fe..f37e822673 100644 --- a/client/features/tx-interpretation/noves/utils/generateFlowViewData.ts +++ b/client/features/tx-interpretation/noves/utils/generateFlowViewData.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { NovesNft, NovesResponseData, NovesSentReceived, NovesToken } from 'types/api/noves'; +import type { NovesNft, NovesResponseData, NovesSentReceived, NovesToken } from 'client/features/tx-interpretation/noves/types/api'; export interface NovesAction { label: string; diff --git a/client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts b/client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts index 8dd194264c..4897ca8a1c 100644 --- a/client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts +++ b/client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts @@ -1,4 +1,5 @@ -import * as transactionMock from 'mocks/noves/transaction'; +import * as transactionMock from 'client/features/tx-interpretation/noves/mocks'; + import { it, expect } from 'vitest'; import { createAddressValues } from './getAddressValues'; diff --git a/client/features/tx-interpretation/noves/utils/getAddressValues.ts b/client/features/tx-interpretation/noves/utils/getAddressValues.ts index c4221b7c57..cd9117420d 100644 --- a/client/features/tx-interpretation/noves/utils/getAddressValues.ts +++ b/client/features/tx-interpretation/noves/utils/getAddressValues.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { NovesResponseData } from 'types/api/noves'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import type { SummaryAddress, SummaryValues } from './createNovesSummaryObject'; diff --git a/client/features/tx-interpretation/noves/utils/getTokensData.spec.ts b/client/features/tx-interpretation/noves/utils/getTokensData.spec.ts index bfa7f10a2a..d92bc0e408 100644 --- a/client/features/tx-interpretation/noves/utils/getTokensData.spec.ts +++ b/client/features/tx-interpretation/noves/utils/getTokensData.spec.ts @@ -1,4 +1,5 @@ -import * as transactionMock from 'mocks/noves/transaction'; +import * as transactionMock from 'client/features/tx-interpretation/noves/mocks'; + import { it, expect } from 'vitest'; import { getTokensData } from './getTokensData'; diff --git a/client/features/tx-interpretation/noves/utils/getTokensData.ts b/client/features/tx-interpretation/noves/utils/getTokensData.ts index 0a0edd1b67..68087ff023 100644 --- a/client/features/tx-interpretation/noves/utils/getTokensData.ts +++ b/client/features/tx-interpretation/noves/utils/getTokensData.ts @@ -2,8 +2,8 @@ import { groupBy, mapValues } from 'es-toolkit'; +import type { NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { NovesResponseData } from 'types/api/noves'; import { HEX_REGEXP } from 'toolkit/utils/regexp'; diff --git a/client/features/user-ops/pages/details/UserOpDetailsActions.tsx b/client/features/user-ops/pages/details/UserOpDetailsActions.tsx index 2a5a6628f6..1adaaff129 100644 --- a/client/features/user-ops/pages/details/UserOpDetailsActions.tsx +++ b/client/features/user-ops/pages/details/UserOpDetailsActions.tsx @@ -4,9 +4,9 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; -import { TX_INTERPRETATION } from 'stubs/txInterpretation'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoActionsWrapper from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; diff --git a/client/features/user-ops/pages/details/UserOpSubHeading.tsx b/client/features/user-ops/pages/details/UserOpSubHeading.tsx index c25378f8de..29073bfccc 100644 --- a/client/features/user-ops/pages/details/UserOpSubHeading.tsx +++ b/client/features/user-ops/pages/details/UserOpSubHeading.tsx @@ -8,13 +8,13 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; import UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import config from 'configs/app'; // import type { ResourceError } from 'client/api/resources'; import { useMultichainContext } from 'lib/contexts/multichain'; -import { TX_INTERPRETATION } from 'stubs/txInterpretation'; import { Link } from 'toolkit/chakra/link'; import { TX_ACTIONS_BLOCK_ID } from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; diff --git a/client/slices/tx/pages/details/TxSubHeading.pw.tsx b/client/slices/tx/pages/details/TxSubHeading.pw.tsx index d9b98c4efb..7503ccac7b 100644 --- a/client/slices/tx/pages/details/TxSubHeading.pw.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.pw.tsx @@ -6,10 +6,11 @@ import * as addressMock from 'client/slices/address/mocks/address'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; import * as txMock from 'client/slices/tx/mocks/tx'; +import { txInterpretation } from 'client/features/tx-interpretation/blockscout/mocks'; +import { transaction as novesTransaction } from 'client/features/tx-interpretation/noves/mocks'; + import config from 'configs/app'; import { protocolTagWithMeta } from 'mocks/metadata/address'; -import { transaction as novesTransaction } from 'mocks/noves/transaction'; -import { txInterpretation } from 'mocks/txs/txInterpretation'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/tx/pages/details/TxSubHeading.tsx b/client/slices/tx/pages/details/TxSubHeading.tsx index bc78fa3114..afcf3a3eba 100644 --- a/client/slices/tx/pages/details/TxSubHeading.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.tsx @@ -10,13 +10,13 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; +import { NOVES_TRANSLATE } from 'client/features/tx-interpretation/noves/stubs'; import { createNovesSummaryObject } from 'client/features/tx-interpretation/noves/utils/createNovesSummaryObject'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; -import { TX_INTERPRETATION } from 'stubs/txInterpretation'; import { Link } from 'toolkit/chakra/link'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; diff --git a/client/slices/tx/pages/index/list/TxsListItem.tsx b/client/slices/tx/pages/index/list/TxsListItem.tsx index 72a7803f95..fcd9641af5 100644 --- a/client/slices/tx/pages/index/list/TxsListItem.tsx +++ b/client/slices/tx/pages/index/list/TxsListItem.tsx @@ -6,8 +6,8 @@ import { } from '@chakra-ui/react'; import React from 'react'; +import type { NovesDescribeTxsResponse } from 'client/features/tx-interpretation/noves/types/api'; import type { Transaction } from 'client/slices/tx/types/api'; -import type { NovesDescribeTxsResponse } from 'types/api/noves'; import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; diff --git a/client/slices/tx/pages/index/list/TxsTableItem.tsx b/client/slices/tx/pages/index/list/TxsTableItem.tsx index c087d41096..aba8adbce9 100644 --- a/client/slices/tx/pages/index/list/TxsTableItem.tsx +++ b/client/slices/tx/pages/index/list/TxsTableItem.tsx @@ -3,8 +3,8 @@ import { Flex, VStack } from '@chakra-ui/react'; import React from 'react'; +import type { NovesDescribeTxsResponse } from 'client/features/tx-interpretation/noves/types/api'; import type { Transaction } from 'client/slices/tx/types/api'; -import type { NovesDescribeTxsResponse } from 'types/api/noves'; import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; diff --git a/configs/app/features/tx-interpretation.ts b/configs/app/features/tx-interpretation.ts index 9cc88898ef..9b988b4bdd 100644 --- a/configs/app/features/tx-interpretation.ts +++ b/configs/app/features/tx-interpretation.ts @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { Provider } from 'types/client/txInterpretation'; -import { PROVIDERS } from 'types/client/txInterpretation'; +import type { Provider } from 'client/features/tx-interpretation/common/types/config'; +import { PROVIDERS } from 'client/features/tx-interpretation/common/types/config'; import { getEnvValue } from '../utils'; diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index bb3777ef97..2674b9a63e 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -19,7 +19,7 @@ type AddressProfileAPIConfig = { import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig'; import { GAS_UNITS } from 'client/slices/gas/types/config'; import type { GasUnit } from 'client/slices/gas/types/config'; -import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from 'types/client/txInterpretation'; +import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from 'client/features/tx-interpretation/common/types/config'; import { VALIDATORS_CHAIN_TYPE } from 'types/client/validators'; import type { ValidatorsChainType } from 'types/client/validators'; import type { WalletType } from 'types/client/wallets'; From 837a7d5888fac0995d0810ce9364dae322c7f5cc Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 20 May 2026 20:16:30 +0200 Subject: [PATCH 15/85] =?UTF-8?q?track:=206-8=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3474?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index ea21259bdf..829996c91f 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -225,7 +225,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. - `ui/gasTracker/` + `ui/pages/GasTracker.tsx` → `client/features/gas-tracker/` - Imports gas-price primitives from `client/slices/gas/` -### 6-8 · [ ] Feature: `validators` +### 6-8 · [~] Feature: `validators` · [#3474](https://github.com/blockscout/frontend/issues/3474) ### 6-9 · [~] Feature: `marketplace` · [#3466](https://github.com/blockscout/frontend/issues/3466) **Scope:** Includes `lib/contexts/marketplace.tsx`, `lib/hooks/useGraphLinks` → `client/features/marketplace/` From acb3c00c5b0b08bddd6eebc00536f555f54fbe1e Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 09:11:51 +0200 Subject: [PATCH 16/85] add grepika search tool --- .agents/rules/grepika.mdc | 16 ++++++++++++++++ .cursor/mcp.json | 4 ++++ cspell.jsonc | 1 + 3 files changed, 21 insertions(+) create mode 100644 .agents/rules/grepika.mdc diff --git a/.agents/rules/grepika.mdc b/.agents/rules/grepika.mdc new file mode 100644 index 0000000000..f88a44ea30 --- /dev/null +++ b/.agents/rules/grepika.mdc @@ -0,0 +1,16 @@ +--- +description: Prefer grepika MCP tools for code search over built-in grep/glob +alwaysApply: true +--- + +## Code Search + +If available to you, prefer grepika MCP tools over built-in search tools: + +- `mcp__grepika__index` - Build/update search index (run first!) +- `mcp__grepika__search` - Pattern/regex search (replaces Grep) +- `mcp__grepika__toc` - Directory tree (replaces Glob patterns) +- `mcp__grepika__outline` - File structure extraction +- `mcp__grepika__refs` - Symbol references + +These provide ranked results with FTS5+trigram indexing for better search quality. \ No newline at end of file diff --git a/.cursor/mcp.json b/.cursor/mcp.json index e5f807b0ce..f539408cee 100644 --- a/.cursor/mcp.json +++ b/.cursor/mcp.json @@ -13,6 +13,10 @@ "-y", "next-devtools-mcp@latest" ] + }, + "grepika": { + "command": "npx", + "args": ["-y", "@agentika/grepika", "--mcp"] } } } \ No newline at end of file diff --git a/cspell.jsonc b/cspell.jsonc index 194bc30dcb..c2ff4ccbe7 100644 --- a/cspell.jsonc +++ b/cspell.jsonc @@ -121,6 +121,7 @@ "goriunov", "gotmpl", "grecaptcha", + "grepika", "growthbook", "gstatic", "hairsp", From f73c80c04ad61235a36fdc434ccf74c3735f0fe2 Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 12:05:25 +0200 Subject: [PATCH 17/85] =?UTF-8?q?track:=206-22=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3477?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 829996c91f..9aa0cbbcbb 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -260,7 +260,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-21 · [~] Feature: `tx-interpretation` · [#3471](https://github.com/blockscout/frontend/issues/3471) -### 6-22 · [ ] Feature: `public-tags` (merge with `address-metadata`) +### 6-22 · [~] Feature: `public-tags` (merge with `address-metadata`) · [#3477](https://github.com/blockscout/frontend/issues/3477) **Scope:** Community address labels. See `configs/app/features/publicTagsSubmission.ts`. ### 6-23 · [ ] Feature: `address-widgets` From 0adb9390e07e918d7e952029f06fd649c21abbce Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 12:06:51 +0200 Subject: [PATCH 18/85] [Migration 6-8] Feature: validators (#3475) * feat: [Migration 6-8] move validators to chain-variant trees Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/653c3d88-8c65-4029-bbcf-f73927181ffd Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * update screenshots --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> Co-authored-by: tom --- client/api/services/general/misc.ts | 26 +++-- .../blackfort/mocks/validators.ts | 2 +- .../ValidatorsBlackfort.pw.tsx | 3 +- .../validator-index}/ValidatorsBlackfort.tsx | 14 +-- .../validator-index}/ValidatorsCounters.tsx | 3 +- .../pages/validator-index}/ValidatorsList.tsx | 2 +- .../validator-index}/ValidatorsListItem.tsx | 2 +- .../validator-index}/ValidatorsTable.tsx | 4 +- .../validator-index}/ValidatorsTableItem.tsx | 2 +- ...fort.pw.tsx_default_base-view-mobile-1.png | Bin 0 -> 31953 bytes ...kfort.pw.tsx_mobile_base-view-mobile-1.png | Bin .../blackfort/pages/validator-index}/utils.ts | 2 +- .../blackfort/stubs/validators.ts | 19 ++++ .../chain-variants/blackfort/types/api.ts | 32 ++++++ .../components}/ValidatorStabilityStatus.tsx | 4 +- .../stability/mocks/validators.ts | 2 +- .../validator-index}/ValidatorsCounters.tsx | 3 +- .../validator-index}/ValidatorsFilter.tsx | 2 +- .../pages/validator-index}/ValidatorsList.tsx | 2 +- .../validator-index}/ValidatorsListItem.tsx | 5 +- .../ValidatorsStability.pw.tsx | 3 +- .../validator-index}/ValidatorsStability.tsx | 16 +-- .../validator-index}/ValidatorsTable.tsx | 4 +- .../validator-index}/ValidatorsTableItem.tsx | 5 +- ...lity.pw.tsx_default_base-view-mobile-1.png | Bin 0 -> 33967 bytes ...ility.pw.tsx_mobile_base-view-mobile-1.png | Bin .../stability/pages/validator-index}/utils.ts | 2 +- .../stability/stubs/validators.ts | 19 ++++ .../chain-variants/stability/types/api.ts | 37 +++++++ .../zilliqa/components}/ValidatorEntity.tsx | 3 +- .../zilliqa/mocks/validators.ts | 2 +- .../ValidatorDetails.pw.tsx | 3 +- .../validator-details}/ValidatorDetails.tsx | 2 +- .../validator-details}/ValidatorZilliqa.tsx | 8 +- ...ails.pw.tsx_default_base-view-mobile-1.png | Bin ...tails.pw.tsx_mobile_base-view-mobile-1.png | Bin .../pages/validator-index}/ValidatorsList.tsx | 2 +- .../validator-index}/ValidatorsListItem.tsx | 5 +- .../validator-index}/ValidatorsTable.tsx | 2 +- .../validator-index}/ValidatorsTableItem.tsx | 5 +- .../validator-index}/ValidatorsZilliqa.pw.tsx | 3 +- .../validator-index}/ValidatorsZilliqa.tsx | 8 +- ...liqa.pw.tsx_default_base-view-mobile-1.png | Bin ...lliqa.pw.tsx_mobile_base-view-mobile-1.png | Bin .../zilliqa/stubs/validators.ts | 24 +++++ .../chain-variants/zilliqa/types/api.ts | 25 +++++ configs/app/features/validators.ts | 11 +- deploy/tools/envs-validator/schema.ts | 4 +- pages/validators/[id].tsx | 2 +- pages/validators/index.tsx | 6 +- stubs/validators.ts | 54 ---------- types/api/validators.ts | 97 ------------------ types/client/validators.ts | 11 -- ...fort.pw.tsx_default_base-view-mobile-1.png | Bin 31967 -> 0 bytes ...lity.pw.tsx_default_base-view-mobile-1.png | Bin 33982 -> 0 bytes 55 files changed, 257 insertions(+), 235 deletions(-) rename mocks/validators/blackfort.ts => client/features/chain-variants/blackfort/mocks/validators.ts (94%) rename {ui/pages => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsBlackfort.pw.tsx (87%) rename {ui/pages => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsBlackfort.tsx (89%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsCounters.tsx (90%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsList.tsx (85%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsListItem.tsx (95%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsTable.tsx (95%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/ValidatorsTableItem.tsx (94%) create mode 100644 client/features/chain-variants/blackfort/pages/validator-index/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png rename {ui/pages => client/features/chain-variants/blackfort/pages/validator-index}/__screenshots__/ValidatorsBlackfort.pw.tsx_mobile_base-view-mobile-1.png (100%) rename {ui/validators/blackfort => client/features/chain-variants/blackfort/pages/validator-index}/utils.ts (91%) create mode 100644 client/features/chain-variants/blackfort/stubs/validators.ts create mode 100644 client/features/chain-variants/blackfort/types/api.ts rename {ui/shared/statusTag => client/features/chain-variants/stability/components}/ValidatorStabilityStatus.tsx (80%) rename mocks/validators/stability.ts => client/features/chain-variants/stability/mocks/validators.ts (94%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsCounters.tsx (92%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsFilter.tsx (90%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsList.tsx (85%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsListItem.tsx (87%) rename {ui/pages => client/features/chain-variants/stability/pages/validator-index}/ValidatorsStability.pw.tsx (87%) rename {ui/pages => client/features/chain-variants/stability/pages/validator-index}/ValidatorsStability.tsx (92%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsTable.tsx (93%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/ValidatorsTableItem.tsx (83%) create mode 100644 client/features/chain-variants/stability/pages/validator-index/__screenshots__/ValidatorsStability.pw.tsx_default_base-view-mobile-1.png rename {ui/pages => client/features/chain-variants/stability/pages/validator-index}/__screenshots__/ValidatorsStability.pw.tsx_mobile_base-view-mobile-1.png (100%) rename {ui/validators/stability => client/features/chain-variants/stability/pages/validator-index}/utils.ts (93%) create mode 100644 client/features/chain-variants/stability/stubs/validators.ts rename {ui/shared/entities/validator => client/features/chain-variants/zilliqa/components}/ValidatorEntity.tsx (96%) rename mocks/validators/zilliqa.ts => client/features/chain-variants/zilliqa/mocks/validators.ts (95%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-details}/ValidatorDetails.pw.tsx (84%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-details}/ValidatorDetails.tsx (97%) rename {ui/pages => client/features/chain-variants/zilliqa/pages/validator-details}/ValidatorZilliqa.tsx (85%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-details}/__screenshots__/ValidatorDetails.pw.tsx_default_base-view-mobile-1.png (100%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-details}/__screenshots__/ValidatorDetails.pw.tsx_mobile_base-view-mobile-1.png (100%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsList.tsx (85%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsListItem.tsx (87%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsTable.tsx (92%) rename {ui/validators/zilliqa => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsTableItem.tsx (83%) rename {ui/pages => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsZilliqa.pw.tsx (85%) rename {ui/pages => client/features/chain-variants/zilliqa/pages/validator-index}/ValidatorsZilliqa.tsx (89%) rename {ui/pages => client/features/chain-variants/zilliqa/pages/validator-index}/__screenshots__/ValidatorsZilliqa.pw.tsx_default_base-view-mobile-1.png (100%) rename {ui/pages => client/features/chain-variants/zilliqa/pages/validator-index}/__screenshots__/ValidatorsZilliqa.pw.tsx_mobile_base-view-mobile-1.png (100%) create mode 100644 client/features/chain-variants/zilliqa/stubs/validators.ts delete mode 100644 stubs/validators.ts delete mode 100644 types/api/validators.ts delete mode 100644 types/client/validators.ts delete mode 100644 ui/pages/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png delete mode 100644 ui/pages/__screenshots__/ValidatorsStability.pw.tsx_default_base-view-mobile-1.png diff --git a/client/api/services/general/misc.ts b/client/api/services/general/misc.ts index bb471a93e3..f12b5c0b2c 100644 --- a/client/api/services/general/misc.ts +++ b/client/api/services/general/misc.ts @@ -3,7 +3,22 @@ import type { ApiResource } from '../../types'; import type { AdvancedFilterParams, AdvancedFilterResponse, AdvancedFilterMethodsResponse } from 'client/features/advanced-filter/types/api'; import type { DepositsResponse, DepositsCounters, WithdrawalsResponse, WithdrawalsCounters } from 'client/features/chain-variants/beacon-chain/types/api'; +import type { + ValidatorsBlackfortCountersResponse, + ValidatorsBlackfortResponse, + ValidatorsBlackfortSorting, +} from 'client/features/chain-variants/blackfort/types/api'; import type { CeloEpochDetails, CeloEpochElectionRewardDetailsResponse, CeloEpochListResponse } from 'client/features/chain-variants/celo/types/api'; +import type { + ValidatorsStabilityCountersResponse, + ValidatorsStabilityFilters, + ValidatorsStabilityResponse, + ValidatorsStabilitySorting, +} from 'client/features/chain-variants/stability/types/api'; +import type { + ValidatorsZilliqaResponse, + ValidatorZilliqa, +} from 'client/features/chain-variants/zilliqa/types/api'; import type { CsvExportItemResponse, CsvExportConfig } from 'client/features/csv-export/types/api'; import type { Blob } from 'client/features/data-availability/types/api'; import type { HotContractsFilters, HotContractsResponse, HotContractsSorting } from 'client/features/hot-contracts/types/api'; @@ -26,17 +41,6 @@ import type { import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig } from 'types/api/configs'; import type { IndexingStatus } from 'types/api/indexingStatus'; -import type { - ValidatorsStabilityCountersResponse, - ValidatorsStabilityFilters, - ValidatorsStabilityResponse, - ValidatorsStabilitySorting, - ValidatorsBlackfortCountersResponse, - ValidatorsBlackfortResponse, - ValidatorsBlackfortSorting, - ValidatorsZilliqaResponse, - ValidatorZilliqa, -} from 'types/api/validators'; export const GENERAL_API_MISC_RESOURCES = { // WITHDRAWALS diff --git a/mocks/validators/blackfort.ts b/client/features/chain-variants/blackfort/mocks/validators.ts similarity index 94% rename from mocks/validators/blackfort.ts rename to client/features/chain-variants/blackfort/mocks/validators.ts index b62946c86e..6265013fdf 100644 --- a/mocks/validators/blackfort.ts +++ b/client/features/chain-variants/blackfort/mocks/validators.ts @@ -2,7 +2,7 @@ import type { ValidatorBlackfort, ValidatorsBlackfortCountersResponse, ValidatorsBlackfortResponse, -} from 'types/api/validators'; +} from 'client/features/chain-variants/blackfort/types/api'; import * as addressMock from 'client/slices/address/mocks/address'; diff --git a/ui/pages/ValidatorsBlackfort.pw.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.pw.tsx similarity index 87% rename from ui/pages/ValidatorsBlackfort.pw.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.pw.tsx index 7b6933a207..03ab620854 100644 --- a/ui/pages/ValidatorsBlackfort.pw.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as validatorsMock from 'mocks/validators/blackfort'; +import * as validatorsMock from 'client/features/chain-variants/blackfort/mocks/validators'; + import { test, expect } from 'playwright/lib'; import Validators from './ValidatorsBlackfort'; diff --git a/ui/pages/ValidatorsBlackfort.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.tsx similarity index 89% rename from ui/pages/ValidatorsBlackfort.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.tsx index 5ac00f01bc..21ef9f0c0b 100644 --- a/ui/pages/ValidatorsBlackfort.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort.tsx @@ -8,11 +8,12 @@ import type { ValidatorsBlackfortSorting, ValidatorsBlackfortSortingField, ValidatorsBlackfortSortingValue, -} from 'types/api/validators'; +} from 'client/features/chain-variants/blackfort/types/api'; + +import { VALIDATOR_BLACKFORT } from 'client/features/chain-variants/blackfort/stubs/validators'; import config from 'configs/app'; import { generateListStub } from 'stubs/utils'; -import { VALIDATOR_BLACKFORT } from 'stubs/validators'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; @@ -21,10 +22,11 @@ import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; import Sort from 'ui/shared/sort/Sort'; -import { VALIDATORS_BLACKFORT_SORT_OPTIONS } from 'ui/validators/blackfort/utils'; -import ValidatorsCounters from 'ui/validators/blackfort/ValidatorsCounters'; -import ValidatorsList from 'ui/validators/blackfort/ValidatorsList'; -import ValidatorsTable from 'ui/validators/blackfort/ValidatorsTable'; + +import { VALIDATORS_BLACKFORT_SORT_OPTIONS } from './utils'; +import ValidatorsCounters from './ValidatorsCounters'; +import ValidatorsList from './ValidatorsList'; +import ValidatorsTable from './ValidatorsTable'; const sortCollection = createListCollection({ items: VALIDATORS_BLACKFORT_SORT_OPTIONS, diff --git a/ui/validators/blackfort/ValidatorsCounters.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx similarity index 90% rename from ui/validators/blackfort/ValidatorsCounters.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx index bc30657165..8fd9f328d1 100644 --- a/ui/validators/blackfort/ValidatorsCounters.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx @@ -5,8 +5,9 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { VALIDATORS_BLACKFORT_COUNTERS } from 'client/features/chain-variants/blackfort/stubs/validators'; + import config from 'configs/app'; -import { VALIDATORS_BLACKFORT_COUNTERS } from 'stubs/validators'; import StatsWidget from 'ui/shared/stats/StatsWidget'; const ValidatorsCounters = () => { diff --git a/ui/validators/blackfort/ValidatorsList.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsList.tsx similarity index 85% rename from ui/validators/blackfort/ValidatorsList.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsList.tsx index eb56a8fdc1..f970cec161 100644 --- a/ui/validators/blackfort/ValidatorsList.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsList.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorBlackfort } from 'types/api/validators'; +import type { ValidatorBlackfort } from 'client/features/chain-variants/blackfort/types/api'; import ValidatorsListItem from './ValidatorsListItem'; diff --git a/ui/validators/blackfort/ValidatorsListItem.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsListItem.tsx similarity index 95% rename from ui/validators/blackfort/ValidatorsListItem.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsListItem.tsx index 1b948d2b15..f407ba5af5 100644 --- a/ui/validators/blackfort/ValidatorsListItem.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsListItem.tsx @@ -3,7 +3,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorBlackfort } from 'types/api/validators'; +import type { ValidatorBlackfort } from 'client/features/chain-variants/blackfort/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/ui/validators/blackfort/ValidatorsTable.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTable.tsx similarity index 95% rename from ui/validators/blackfort/ValidatorsTable.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTable.tsx index 0163dbd0eb..887766670b 100644 --- a/ui/validators/blackfort/ValidatorsTable.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTable.tsx @@ -6,7 +6,7 @@ import type { ValidatorBlackfort, ValidatorsBlackfortSortingField, ValidatorsBlackfortSortingValue, -} from 'types/api/validators'; +} from 'client/features/chain-variants/blackfort/types/api'; import { currencyUnits } from 'client/shared/chain/units'; @@ -41,7 +41,7 @@ const ValidatorsTable = ({ data, sort, setSorting, isLoading, top }: Props) => { onSortToggle={ onSortToggle } indicatorPosition="right" > - Validator’s address + Validator's address Name Commission diff --git a/ui/validators/blackfort/ValidatorsTableItem.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTableItem.tsx similarity index 94% rename from ui/validators/blackfort/ValidatorsTableItem.tsx rename to client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTableItem.tsx index bd000b919b..7a28cca55b 100644 --- a/ui/validators/blackfort/ValidatorsTableItem.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsTableItem.tsx @@ -3,7 +3,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorBlackfort } from 'types/api/validators'; +import type { ValidatorBlackfort } from 'client/features/chain-variants/blackfort/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/client/features/chain-variants/blackfort/pages/validator-index/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/blackfort/pages/validator-index/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a66f390e070c326b752d6e930487b1f6dc5d1956 GIT binary patch literal 31953 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

V@hfSDO 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 KC=t{D`o92mKyP>e literal 0 HcmV?d00001 diff --git a/ui/pages/__screenshots__/ValidatorsBlackfort.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/blackfort/pages/validator-index/__screenshots__/ValidatorsBlackfort.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/ValidatorsBlackfort.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/blackfort/pages/validator-index/__screenshots__/ValidatorsBlackfort.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/validators/blackfort/utils.ts b/client/features/chain-variants/blackfort/pages/validator-index/utils.ts similarity index 91% rename from ui/validators/blackfort/utils.ts rename to client/features/chain-variants/blackfort/pages/validator-index/utils.ts index 2cdeeee7ba..489360ab02 100644 --- a/ui/validators/blackfort/utils.ts +++ b/client/features/chain-variants/blackfort/pages/validator-index/utils.ts @@ -3,7 +3,7 @@ import type { ValidatorsBlackfortSortingValue, ValidatorsBlackfortSortingField, -} from 'types/api/validators'; +} from 'client/features/chain-variants/blackfort/types/api'; import type { SelectOption } from 'toolkit/chakra/select'; diff --git a/client/features/chain-variants/blackfort/stubs/validators.ts b/client/features/chain-variants/blackfort/stubs/validators.ts new file mode 100644 index 0000000000..502555635f --- /dev/null +++ b/client/features/chain-variants/blackfort/stubs/validators.ts @@ -0,0 +1,19 @@ +import type { + ValidatorBlackfort, + ValidatorsBlackfortCountersResponse, +} from 'client/features/chain-variants/blackfort/types/api'; + +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; + +export const VALIDATOR_BLACKFORT: ValidatorBlackfort = { + address: ADDRESS_PARAMS, + name: 'testnet-1', + commission: 10, + delegated_amount: '0', + self_bonded_amount: '10000', +}; + +export const VALIDATORS_BLACKFORT_COUNTERS: ValidatorsBlackfortCountersResponse = { + new_validators_count_24h: '11', + validators_count: '140', +}; diff --git a/client/features/chain-variants/blackfort/types/api.ts b/client/features/chain-variants/blackfort/types/api.ts new file mode 100644 index 0000000000..f6489ac45d --- /dev/null +++ b/client/features/chain-variants/blackfort/types/api.ts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { AddressParam } from 'client/slices/address/types/api'; + +export interface ValidatorBlackfort { + address: AddressParam; + name: string; + commission: number; + delegated_amount: string; + self_bonded_amount: string; +} + +export interface ValidatorsBlackfortResponse { + items: Array; + next_page_params: { + address_hash: string; + } | null; +} + +export interface ValidatorsBlackfortCountersResponse { + new_validators_count_24h: string; + validators_count: string; +} + +export interface ValidatorsBlackfortSorting { + sort: 'address_hash'; + order: 'asc' | 'desc'; +} + +export type ValidatorsBlackfortSortingField = ValidatorsBlackfortSorting['sort']; + +export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }` | 'default'; diff --git a/ui/shared/statusTag/ValidatorStabilityStatus.tsx b/client/features/chain-variants/stability/components/ValidatorStabilityStatus.tsx similarity index 80% rename from ui/shared/statusTag/ValidatorStabilityStatus.tsx rename to client/features/chain-variants/stability/components/ValidatorStabilityStatus.tsx index d880ef4fd8..99de9f3ae6 100644 --- a/ui/shared/statusTag/ValidatorStabilityStatus.tsx +++ b/client/features/chain-variants/stability/components/ValidatorStabilityStatus.tsx @@ -2,9 +2,9 @@ import React from 'react'; -import type { ValidatorStability } from 'types/api/validators'; +import type { ValidatorStability } from 'client/features/chain-variants/stability/types/api'; -import StatusTag from './StatusTag'; +import StatusTag from 'ui/shared/statusTag/StatusTag'; interface Props { state: ValidatorStability['state']; diff --git a/mocks/validators/stability.ts b/client/features/chain-variants/stability/mocks/validators.ts similarity index 94% rename from mocks/validators/stability.ts rename to client/features/chain-variants/stability/mocks/validators.ts index a96bbb77cd..5241b4f706 100644 --- a/mocks/validators/stability.ts +++ b/client/features/chain-variants/stability/mocks/validators.ts @@ -2,7 +2,7 @@ import type { ValidatorStability, ValidatorsStabilityCountersResponse, ValidatorsStabilityResponse, -} from 'types/api/validators'; +} from 'client/features/chain-variants/stability/types/api'; import * as addressMock from 'client/slices/address/mocks/address'; diff --git a/ui/validators/stability/ValidatorsCounters.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx similarity index 92% rename from ui/validators/stability/ValidatorsCounters.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx index 8f6e878e3c..305a8337db 100644 --- a/ui/validators/stability/ValidatorsCounters.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx @@ -5,8 +5,9 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { VALIDATORS_STABILITY_COUNTERS } from 'client/features/chain-variants/stability/stubs/validators'; + import config from 'configs/app'; -import { VALIDATORS_STABILITY_COUNTERS } from 'stubs/validators'; import StatsWidget from 'ui/shared/stats/StatsWidget'; const ValidatorsCounters = () => { diff --git a/ui/validators/stability/ValidatorsFilter.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsFilter.tsx similarity index 90% rename from ui/validators/stability/ValidatorsFilter.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsFilter.tsx index 8477ba7738..b8ab0e000a 100644 --- a/ui/validators/stability/ValidatorsFilter.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsFilter.tsx @@ -3,7 +3,7 @@ import { createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorsStabilityFilters } from 'types/api/validators'; +import type { ValidatorsStabilityFilters } from 'client/features/chain-variants/stability/types/api'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio'; diff --git a/ui/validators/stability/ValidatorsList.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsList.tsx similarity index 85% rename from ui/validators/stability/ValidatorsList.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsList.tsx index fae718ee58..1c7a8e64c4 100644 --- a/ui/validators/stability/ValidatorsList.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsList.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorStability } from 'types/api/validators'; +import type { ValidatorStability } from 'client/features/chain-variants/stability/types/api'; import ValidatorsListItem from './ValidatorsListItem'; diff --git a/ui/validators/stability/ValidatorsListItem.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsListItem.tsx similarity index 87% rename from ui/validators/stability/ValidatorsListItem.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsListItem.tsx index 059697ef17..abb60f51a1 100644 --- a/ui/validators/stability/ValidatorsListItem.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsListItem.tsx @@ -2,13 +2,14 @@ import React from 'react'; -import type { ValidatorStability } from 'types/api/validators'; +import type { ValidatorStability } from 'client/features/chain-variants/stability/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import ValidatorStatus from 'client/features/chain-variants/stability/components/ValidatorStabilityStatus'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; -import ValidatorStatus from 'ui/shared/statusTag/ValidatorStabilityStatus'; interface Props { data: ValidatorStability; diff --git a/ui/pages/ValidatorsStability.pw.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.pw.tsx similarity index 87% rename from ui/pages/ValidatorsStability.pw.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.pw.tsx index c815aee754..a4ff2b6266 100644 --- a/ui/pages/ValidatorsStability.pw.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as validatorsMock from 'mocks/validators/stability'; +import * as validatorsMock from 'client/features/chain-variants/stability/mocks/validators'; + import { test, expect } from 'playwright/lib'; import Validators from './ValidatorsStability'; diff --git a/ui/pages/ValidatorsStability.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.tsx similarity index 92% rename from ui/pages/ValidatorsStability.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.tsx index 514d0bf71d..ee77ba766a 100644 --- a/ui/pages/ValidatorsStability.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsStability.tsx @@ -9,15 +9,16 @@ import type { ValidatorsStabilitySorting, ValidatorsStabilitySortingField, ValidatorsStabilitySortingValue, -} from 'types/api/validators'; +} from 'client/features/chain-variants/stability/types/api'; // import useDebounce from 'client/shared/hooks/useDebounce'; +import { VALIDATOR_STABILITY } from 'client/features/chain-variants/stability/stubs/validators'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; import { generateListStub } from 'stubs/utils'; -import { VALIDATOR_STABILITY } from 'stubs/validators'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; // import { FilterInput } from 'toolkit/components/filters/FilterInput'; @@ -27,11 +28,12 @@ import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; import Sort from 'ui/shared/sort/Sort'; -import { VALIDATORS_STABILITY_SORT_OPTIONS } from 'ui/validators/stability/utils'; -import ValidatorsCounters from 'ui/validators/stability/ValidatorsCounters'; -import ValidatorsFilter from 'ui/validators/stability/ValidatorsFilter'; -import ValidatorsList from 'ui/validators/stability/ValidatorsList'; -import ValidatorsTable from 'ui/validators/stability/ValidatorsTable'; + +import { VALIDATORS_STABILITY_SORT_OPTIONS } from './utils'; +import ValidatorsCounters from './ValidatorsCounters'; +import ValidatorsFilter from './ValidatorsFilter'; +import ValidatorsList from './ValidatorsList'; +import ValidatorsTable from './ValidatorsTable'; const sortCollection = createListCollection({ items: VALIDATORS_STABILITY_SORT_OPTIONS, diff --git a/ui/validators/stability/ValidatorsTable.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsTable.tsx similarity index 93% rename from ui/validators/stability/ValidatorsTable.tsx rename to client/features/chain-variants/stability/pages/validator-index/ValidatorsTable.tsx index 3b59023fb8..fced7dae28 100644 --- a/ui/validators/stability/ValidatorsTable.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsTable.tsx @@ -6,7 +6,7 @@ import type { ValidatorStability, ValidatorsStabilitySortingField, ValidatorsStabilitySortingValue, -} from 'types/api/validators'; +} from 'client/features/chain-variants/stability/types/api'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; @@ -33,7 +33,7 @@ const ValidatorsTable = ({ data, sort, setSorting, isLoading }: Props) => { - Validator’s address + Validator's address AI26va>E zl&l1Vn;;>*L6Q~|R&!4~TtWY=3fn|EUF++^M@>DSSNoF9EoPF;D`#xVQx{-O>&h`= z7pu`OGo_QfQtG!)34~eAO=rxP=iL)p!F%;1cgPC@E!nwM&7n zl8|15&)KrNRSl$y+qO4UTSYjy-de5PAS0KBm+Mn(VF$PPAo<@e#`nxDNb@300p<0A zZ_tM2kjlny$P75nc)>bz&%e_NHEBJ@p5BG1*bxhzy#FFS22hp@ZG^H(joyU$77r zA7OKELQTo@;`?S^$A);_G}pRfXKR-Z+YkGbEuiA_$~{Hu90{=`VApWGd>B@Ni`9IH z^g2Fvn$xRWY33zF*M?P%gRoyBj+@E(H63{(tqDJ{-T4_Fpim`2E(054=vZ@!QdhB2 zMd6Wm=g2q0tFGXx&@NHPHW`p3W+eSxEhDWYI_&vqjHr2~UE?#ws%?MqBfanD{K*_{ zRn@*f@wNLuh(_WD>B+S!TLw zg`AjB)cJBqfRC0lI#jz4JJ&zV25p9kkx73y60@r2*Kp~bv!++x*#Q@`Q`WjXSch%r zBodCQ(t=(m;meuH(1aw&U{g#|XjiL+S2-ggr9Bimw4P4h@b#n#BPSo4f1)W^^X>3F z`##=sVs?ksVvSXReLoev_yWPnY)uL=*vheFO{7EP0CnGxh3-iS`HbeWKZ_Sz;UI3+H7T45#aK-XpM8F76u9yl6=@t6;puI zeB)dfrryf1n(%uo)sF~ct5m?nQBL2Po2qhPuK1|7p53|J=^DC(odNf}{v_1iDr;cr zI8yaqNc#b{rwF5p^SwDg%3b60Slsjwl1Gmt>~uPXo?z=QH*D#Oex!I>Or|~0tXjHQ z41DAy=M4<#Hu6`7Q_XeaUaA)WI!;fH4k4a+ z>J0+U!WtR%ZLkr*&cX9A=VfydK#Krm2X^D>rsM1U%PN0#D-onw?~rhC`{0I#H2z(o z+@_4vVnksCcE7U;>Ru3?zQF`P!pi;f)_?3&P!NWh(%vQj=YEj*Db1q#TU(9k-frf% zY8r5RUrW_(&F(W>&6Z^fxQ^p@)~i!glGGe2E@QtuDa^QEo6r9>gBjBlm9uxv6Za)r1s1S}aO42%zJg z4aAVr_0)!))|37o)>c9=_xdPZ324yLkONNPMBR8b@Q) z>Q0}qE`p)zEhTmcrSj7`G6`5;T7feTe~1y_cH5*lx}W{cL-d`Ug6LOvI_L9All`ha zUNj`G0EL-Ddfak#%L-=h1)y;QA2-K4y5Ajruy~?3U-B&(mwqYys5!b*z-BZVF~}l$ zH+U=qj5v_!zkK}#OUGCdq!a1BeLp9uZ-`Iat422bXvc0p=n5-c&RM)u3NLwGT5s_@ zG@Of-;CPQxGcTX6Mt^WIx;&miUANHjp0VSGD=&B=(`j!r*#;$Ypj3eBQ&^b(_2lQg zkklzJm}_7gi>Pq{37ypA#r&ekLETztYy->F5 zE-rq4IZF5h-Vy)&xbJ|h%5T8*B{(($;+#Uz(-Rt=k3Vv(K%*%q#g({Sp`p}zSlcIOuZ?oo6Z3?e0TfQ}|^?*3zeky2`wKwXG&4+G}Fui1l z%q;d9g{bQ5@N|$$hvuJC=Np|>2WsC`)E23hzTU5DR762qM&(s+uMv8l5M7W3J+C#3 zs8%OqCpKw!P2XLl3GsIFEL{@an<3;fu@{@oNZaX)Mb-b|kj%MC;e*KMqn`=dD3!wuvoI~jE=b|U#caH3#cX8GQ9lcARB-*{#+ z3o}6ICbJY0YFIRjXAg$s#po1MIaDV`ahE+28m2;~d?w{Nb}_xFjAzL-9QUG`32%4= zr{hv#Yp&(&%j_Y2w@>$QYCCXtY_TcVnPy=UQ$Hyzg~4*R*vUD$IzArXk}Os}gG)$n z_B4^D!>|H6@GJI^$JvQjol}u%x$Mpme()hbk!R?mf|PSdnpcCP|t_kGO|RJ&joCZ7!98ZDJln z`rPKjs8R2>O9YF7rWr-fZEoLC_dt;oF~91nwzA(EV;h~bX8|DP?12y_vCpF{9mPW6C~FJ~*EyF|_0RET^1@C-87q~C8cBCLKR?K|BP#hE9J*YTsL zeGHTHgr>4Y&PI2{=-#9^DpL$&fnUAHoB$USw{pdY^HI$fH~1iR-fJLW{>lApYQb27 zdAnW~+Wy_KYL(+RhFzY9g2lH2A4M-vYSQDxn9bC>?uD2A*YNz@JC!W*V^3lkJ&Gg) zYNV2`#8IBTl8QIUgIUcJuqAb0+WVTtN@9=+$# ziI*R7jIF(aF0Pj9ze&B^oE*S*nyGLym}@CAlh?0k-f($A3E~&hTfNkgpvdI(tIpe? zKrUZt7hkDgh}Y)IH6?Iiu(9J+6?q9r&wnF}iw4Te_>t^LN{Y1nrrA3II%t}q786_I z;9E1X2yT-YNjdBt;-uj)H51`0JvD`#Gx=?SWCaI@hc$Saf(x$s7qirD2U6O&`L06I_3FBR8CGYKge7oR z>~`iwW3C<19y<;+NtkV-z~Gjb7~q0y2fvOcq&E z&JvA|TKWc2O|Pp~p)DB5iIh@h*vn@p6vPlLKUJ#1q+3tAMz4`)Zj$#YU~zd^ongpH zFBR2TwW7qvmf2nrOouBimD%7jz{dKGlM6BuVY^Cy#h0(wAu|&Xm1kAa&#qJ(8GP&~EN2X%-mhowcgyBd?0Uq~UI1T9K z?YZSo;2RXtzl~_q0ZZdvQoF4owc4e2dk=YYCBS$!s;2uIy~$s0GlYJF*|Dxfr5rY!oz&&$?wAvR=< zzVB>aGyCpvyT5gX^EKzsOnUyyjs5ZPg{yYT-IDfUKa<|E)F0O~fmh`umP;*DGNY?3NjUvSURsT_!{2O5Pf7H6F1C{~&~nM{SUGaU z7Xi&YhGI`+Csh_JhHzwh)%4-0Y>Ue?5DNU76jO;9Z!n?LQzQrSeKk6BKjnfmMY>4J z0VA2*{JNwiRM-un9-K5ePFI$VsvjHHSH|U>%9zOYL4}V=aC;!BwL^`O6(P2DDyPpb zc*(KCO!lFZ0}5?6PSspT{uo`=TE#?szUgiGejTe|>H@Kohlxwbge+#{{yQw(b=~j1 z&m<&;26{Por;gi1dVlanI*Z|7g+$MqDUzhGn6Xx#$z|K!1*LWuK3|B0$Sr!}k?n7X z=hKRS;cmA2R8-DMyVe)Qo43%1xpXP|1eXGvV-SoWT{!J_6KXkXJFs)E7Lj4KWVdNq zXw-WjUVDp4DJ^GaUaa1HF+cAr$mEv5&hW*TcGM|?P>_{FyV>2)HIh|*kloUKHF93H zY9)SZE7*uVB|QG4#<*R#mLvwX@^|_~8xvpKPhwBOyHSx+arw8{z!2pO3TD=hF6qPC zcO5}|mlfER_q4%`k_~`Xx>^bhYISo z#S&NUwxw|}x- zqKjnCDk8y6TlY@p{;>G=g&YgCD$kx~Nn;gqqi-{a{|qiQ^m(rpE{2`2!2=CaE}!1p zNfMGy=dH}k_EDm@x01H`GM&UhMIGwlCg7(2R%%Bj=G;ocrg_mv?<9u+xHz+Bl~K~pFriey7X8jNJBPw<4tHQuh2nsrf=U69n#4mMg)O@iT6;#mxZ3*^Y5msv zkcm#()hA>iIZk^h`{X}ZfEu8@DUSt<*V@~Bg;YgOR@Uj^T})yAj_sJ$p}k1JXgfyj z&6m+2VDqyOEmlf$aeAvFLow+%JW3C}Hz-YNvQ{Mw0^&A&VJmtj9v4`bO;9~JE%tJ+ z$bmiUFQezrwD5*;9)Jf`nSP<@$D5AsjC3+A)@Cwcncfnx<}!u|CP3~idfpVH0q-xI zp`NGti8l}ScVS|M19K|X;X`mM_o@LjX$8CXSsFaOIIkPD9@gD~&$$(1<|Y6`b-4_x zf}zUtqp0_}RtwW|>Pwo614+KkV!rYxOx}Ujb-g&!z2A?TN|Vs33I|N8jJ|0d0Kd_g zxfyNj=!HEOgM0{RZ5taJ8AAep(a>Ej)=DHB(FaQP-&5o*EhwOb9q}BocgTs=*d2do zh@b}Ls%W&c8CdO&Z&1Vz_FTBmNt=tt^wCSpkMNmtU(wSF%8A#OV*3zon)<{YO&meQOY)GNCd=_cUBfjvppy?6!G2}zdr zmp*zi!W@%j-YqfzHi?LU4ApuM{h)t0mNzc{pw`*oJf1k|3U`5=W!+|@n4noR+FE*d z$O;P;)S4ZQt|ntXRIWVY&F`e#-JhdMOBlRtf-yMD&|mv&eqKfP<+2`15OdH4OYehi1(yai1(6;|%$e$HP;CzVoXLYv;yYWYkx#j<(C>;aWBc2F`e zLNTsie`@dpQWnu`xcE!GTHo6}n0%aGn?!oZxYX1rL9YkqY)~cDlT02CS%T3t7ITlJ zJ&p2FW$T0{K)yA$BCGEJVT}n9oXoiDgH3%+Tji*gAy3+)^Up*3a#jJ>j3oBoGq3hO zD97)7JbdsW;>V!n?~*1qyPUUm#bk{z(~Vxjq;-irUp6 zYt+EsA-}qgGJ4F*7RwLT3)_O3o4ofjendNyL! zN}%1cl6|s#0M{gNhT6GWbA~yOTQNsTcjh#q)%vzcZ_a1scTPR{-hKr+#`}RUmD*a( z!^7h>a!&KOX9TNc?lPJ3unxQ8x0Wg7&e!AP2DRmV`nmj}PJKd)c~gWTpKRaD2Z1%^ z!(u1bu3cE2n{{)QOQCM8zzEl&+lX_HnmnS1cM$9^Ntbv;6XR%2P_d$J;YjOJFG&<` zdyc)2u6(H@^{fdbsiu$_>a5xe?{j9qNm~AlXvvo*al3W5j+Ad$kNkPv&{JQ%NYSm} zix_Sal@arRp;c5nN<@T?i`(%vV95`WL2e2VwGbo&}Z0BDz}2;9Dd5h z>*US;sC_|4Dm;vnYI?t%;~k-#i^n1~JOFgj!_fO~9J`);SPOBHJNMtgIwT4-?TA3L zoM@O*soM%8=lf16s{*Lp8q5CMn>qI>YXr&qX|V{g`r~++A$B(8n+9l&83UbC8W^_% zs{h%SB*XWJ0~@xPp3uar`fD!77g?6!6|hvpkGMt(+{yI&eQ!_2CS&&TKMBDP?jGUm zu<(oc?SfAZAj}~i3Q_6ZvGgZM?#0<1G%7{c;rFRkYH~B@H zw!fWynfBEQxj12XrX`>>Q*`jKeycupbfE$$lZCAp7?UBqI~acJ-}I=Tkb8csuW94D zavvywNyIs~7CAmfy!o*nX;5D>t_=lF?6sLTGfMFpMHQKZ&+1^ORVlZ|e-M=to5hxy zdE`d~#Z&O7m0{-&hwr~VY=?L3P3L~U+)*Lzc%98K2}`U%aQO#6Fha%N4NM|qCsjvXy2w)Ry1>vXzQ z6we5m{D~GF7O~8KoUclFQ!+!c7n>a7YZk`1{nc@V?Y~0kxVByg2ynZUM|G}qdQHZtiD2r81N=xO( z&U*IXU>|M5;^yuw@v37*`%Gj&h{&D9WwH8#&hs%ky64Lsh_0j2U0DTg zz&_?OD(9Sz&s?TiiGRf>Nw!m~A7%M8Ue-y^xRur1UA?)MR^$ltAP5Z~W zAkP;ckXkt_f9irlXFTBwQ(x^rN0`5DVRYCsh)PreK;eW(yNtHMG`kcgbS<`V%3SQ( zCdG3o*NsQ|!J4)`*b;JQw*xLweW$mCE{uc(utXRiF|XV2yFAC{|0HIshs5qXrg1WFcAAVhbX@%wD0-_M%wkCqYu z8Aip+wnyZl3DU!*bCrVylO{byi=a}@=_{?xvt%4UkCLfW5qwnLn_Ec~gqe4S z2DdluY{%Hwso^&$3bwrZgmm(0>A%?t>McK_z4Gu@iy|V#kmr8v-*N*+6~T>dSNEr4 z;AT)A7qXqJgS&$OXq1m~G=BR1;`+LRe&_pf+ce#uw3_e_A(?_SKkUnukLgzPFui24 zrRg~anJ(eE+ztgP_zr^lh{&b8Cw80#+^O$(Ynn%CFXVeV}*2^>K6Yw-Pj~A52feD!$Zirg~Nau z=IBZ(PUqFQ5iisqJ7ebYmb)e`jEw9DwVvNUqaw}4Ddao(Y)F|5jC4qVDi@xey-TmC znk({NwZ>PQrJG9_+uXizX|mJP*m^Tcc9Y|#A;o34kWo2nRW}-t%xpA!52_%+j*X#Z z1x5;&M7!VV^%*^X0eGwvp&?YchS>Y)`({I$XC4Y4*U?2wo@dYSW5hj{^$&HMG0|LK zmbO2MPtbktco3V=3WLm*>nBR;_9|r5nG_TL!vfIUR=J~2anoX+=RYtSkuyb{up=og_;mPJsPh0_0y7@359p+R~W0lNr>lEv# zK6N9P!r9>b^1G(k`OPPd(7DPX4YN``O}dx)ma^axs|V{!XH(}eywr>hUDE_eGj&%| zo(a9j>QU;BW#O^QHZ;e5@=Ufrb(XD{lZQ`&mi2+BUVEmPOAQ zLN^2;+QYJ+Kibjt)hJWM!i6zEkfbz355B!zV~zTWDo#pZPH^hCc4 zT&`9(NHfd0Jf!w;vy#(=1j0SnI4nXg`|;Xo-oIfzJkw8y@EqW|Jx1HuInHW|Lov8B zgC!(Q@~CM<((g)Y<}Dbr8v}M6xEITcm)hNrOKzVq=f1T>SPO8U^MKH~wE-!o$0ra! zEl#e|Gq6fLg{gAium;z}RB?5pDP1aDl3I!WrgJ9L^pA(i5$>K5Prv0qC&_8%h&feh zW}w;DtY2p%(YHjD7`D5Pz; zXfP$CxQvOn=Blo;;rc|Raxi6zIf)w7WM&I6S42fdTDCd}uOM7)v0^v2J=OU%=ts+b z6$PO>IvL9^A+q`8L+ifgD+|jnDV!4B?ryZ{7y@Q?e3nP}_PlZXSH!C1NUa;;Vj$Lr zGDjV>aW5!z)G8`ngX42rELfq4{$s z9;A3P2JAU@utSc?K%$t+hRp@C9yWg3=7 z4Z07%GUt1y5-3+>L(}95lMNf1q%Ne8?uoAwD_@RH&$Y3MdnZFV2dmaa`kIOp_A>C- zH}%JwMr+f+wG&=zkLRU}(-GnyyuZOg`-kmS5B!5sy-t7p#tx2zs346=ET?vvg-rqp zu7*^LrbusJiDpB``T|l}5bz+Vert#(L{|a>Yyf06xvZmD`S}y>6SBiP2yd?_DB`c; z*NE2y%ecs;=(aK2*I9ekfK|=?or4^;HMYu0Umdi*F0Wtx$*{Y3pvuc?QTjPiP&L~U zwPrZTxn{b_HVK=qARp-cRS@Z`P6o8(^SdRU&R6@4)o9|Eq@x<5EzU?Bl5qODHfmlU z*heI!PTSxkU)HI&3DY^Ynb{@`^{N1aSEPz_sq?JW6vSk|i ztD-Rv6Rk;2a^Ghm&Tfx}m*6OAfBHOx8vY9ACN4nFqa83Zwc7%^w9dzy*i+=K{rV3U zz{`2w6Doc}W0p|ls|4P~E0(R5=)wyz#zFYGiqUoxJe!ZUQ!HDUWGE6Tq;@KNURM09 z9L1Yy$`Q+`G0c_8{lw;Jb@Wz>x}(ZNEW~Q}l3zWhLe8F(0}zt>53T3ELH%3i{q`y8KSf`oL_R+I=j&AYe->a6_Zc+KPC*0% z*vXDqOPGH&Au~*$2_iuBVW&y|?(7O`?6IQuQ|;OpKSAoGc3%+tr+@Efs%%b5kS9oAqfV>2CAtlT^}mYI}y7b3&{_jd5Us3nc$DLVLGURoVFa@fpv0CB>`oEPf{Url$us zxU(Kb>`xH*OnPOs6|6s@1%v3JErmK%q1{jGoao!|ejj;i$9+y-WV{W))BX3d<;!0k zc(!Z#wCvMP0aZAC11JG?D@0TGja$xoHn-$HEi7zI#E;uyWM_>Xukd0@RH7gNMtt9g zh0Y_Q8HOLUeO2}yLTc{Kj5Ji7vsSi?HmNfZK;qG;-R@%Inuj2;dsamOulbN&+S_Hh zk?+T``Aoe};b;o7XYU$_m#&>U3c;0)T1*ED>gXrl%zJwoo6dDu0?4|mpit>1NK$tP zp|nzzF!_mBR;7Vs4Vne`y^tpEY;OIyEv(;Cw=D&SK)_o~6gFKesg{CZCR-rq8;vBN zJ6Lu}k5>lK+>GbuIN075N)=B0;T`vS%u5sJ)s58(jh+LxwGY!*jpjB7M84)%M-Lq< z`J0YD{)=xv@>B6@se0z{nmK5W2D0Vj>gC{Oe^BwEdK`qz;%z0B^xS0-;pwWMLwDEa zTp@ZwE90)PMAJ`82L<<|ZruQ%iN(8i?I^Ke=#P9AsI9R4rUBJxqksJ2_5Lo7a`D+O zZe;}5E6g6m#x4>g=XyTZTZa1VYdepv@@w|i!ju9pGRz;b0bWLHyDfgl3@YrEUw`Ot z0TxtY{nOUsv%*&bcS)TkI70Rh;KD5F&?kDSbk_eYJpy?CG^k{XlG@z|*Wc51>v>Q= zCNX0fulHt7{)~vQ0Ezd-w+)|`^D_4A`!c*Sb?p_KP(lLSwfqt-P3tO+{ZOVC{SraW zt$J5ZEZ@KPh9Jk1-?uFT`F3`niN7WRm(8cy$mwai{gUBMTYqA#+LvvFjPz9^(YY|R zw53n-*^2;)E3%-jDfZ$@gQDbVQaNYcFJodJw~bE(zAslE*WVgzFrgoC@hfka&26Hr z&FpMWumLr7(G#c;kw}1D0BSi{mOTmY?efohKPAmUn=<2)sCNeN*|1)rN2jc7T@F(I z$QhYtPZ|*=8=KQ_v1~H}@JRhv;T`?JZ#kNfdiodCKXvdzw`M8?64!XL7kXj3${mu# zwLW*Ms~vvp0b$0$Yg3hOnz}j&A=bkKma*KI?rw^+Pns1v0oW9U?*E!*qS%Z48r1y# zfa=`aFgx{N%=1h|zW3!!$C?@k-KWl&XRFeIkf%Pr{b>4LY zoHwv-5m9ye2_3!zb}Vj!1kThfn#A+BE8xuU<`EGXh@Dx74)3DLmibf3`Q%kXt(q67 zP&n@mq_u%+YIvb)nWc4?`>s1^v+}Th2=@C`uld|4scs%3t$%lwx+VODWX~lhiRwE( zuW+@5hwA<}Y43G}E{aIzqSPRzy3`mvJ%ee&wSW>>@Ku7wyiG=Z-w^>WiHqHsATkqY z$%9Ew5`^mNZ_25D^)#e*Vx5jDQqvK<1t+OlQp2*hcuWsTU7F2)R3$=SA4$P89=fNe@z z!>ft{KYu{+K)ERQ^2?cghV<3CsAg?OSQ#^%sB9T_M98O$94X+wruP~-<@qH6_8Jfp z$tC!n+4*37qrV+~aTHbN`(2+!ujTf}gHimmjm=&|p>4=!)-4(p_iPI=y*Ju!E#_+Q zQ5^+F9Q_{0yS{BEHNkdKk9crR3n7aiQoM`rH;~AfaBE_>PLiBx>(q(dAG4% zQ~*Kt$Ubv(;Ka);4n2W(HvU@kt@MVV2u)Mf#G9OQvn3`U0xvafDtqNP*OYwQ2w8 zPr8ua!`b2K!4DJEuSqEGTVy@-gan2+G#t~><EKA2vZX>LDs;jo$p!y!%7lYQZ`ejDXP0OP4`QG20uUbE#e9T&uDtRJ|~nX2(`p4dl> zw_jb{Gnm?e31%MQW3yX%V_!lxqU*(hW{ivHy$Z-1l=nvoesVHz-sCLZ&6ZXk@wdaE z=~o>o*y63q(4k*tyHTkxUI8x53}XCh@E=yTWhF4_1d};-uRRo17pryo^ZC6;>EztE zABuv&d^Hc9S9GRlO;*_BttlG44xwsX7>JNvkMl^Z zF`>yqAlpZ3_s$}($b6bTnL_KcrHP5tL@CJrQ_$-w@h@a?`D zefm2%8I~C3DwdW|_NB|f6Fohx$z=0Ffz#{Sms7FNz*CZ8V|7vib%h*j?=(;X(Rf*( zVp6xd$-PzA)X3?0(PsPvX`RZPaj`1*2bgmC@ry{X5rA)oMv*22d%PctY56*+dh10> zsF6ZN;*tAF7#Vv(uq?yiTCwi}1&O4d5@EmitjHCgN4#1^r26a2)rbJ-`iC zrGF1!8pBMuw_}HjP@W+Zw{|GHPw^2@gyP*;Dkf!?ALE4U=OQjJM|U<+OKpIV(eet> zLGOYu2i=FW25JlK8Gh)`CJ}&=-%kDy%^W55ZUKO{=Hbb`UT7 z6Al&sOs_?_`m0)yYh9!=LxBoRapZ_IZDwXaY%JXltAl=57Q$zskH&3Jo^H&M(en8Y zi1VC-Qq4~5;`EN`n0dGV>oS;P@%2;@gGsyZEU( zO|dr6vI>goYTqi(}oMY(0r! z|2~fx)=8^g_MKM$S<45erPicb+1do96`DUfi9zpSwvCGH1N4g|Vq-NU^fl_CZIACJ z?y(f*4irz|V?wisxI*_A=psjnkL@rz`8N2K$tz~W(=vT};PdY|Ih{6Rz>sBMTcscz zASiGhc*QC?tr_}V(2e@k?f4$HbyY9X#ysk=`?7oWYIVi*V?2e&X{I9&e=y$rb@S3m zO>OD9g3nr|OsnRJr%SxG7D?5D~P%4Z4{)9bJxpv<|}8|SHaXbrxeoAZ0-{gNmK zj^?wAHMdq>FTsuuPe>8Z=t13!kw}A)ysr^W#_Z}xTZDu^YH8ONkLKUs#}a>B^w@*r5yLA# zONGDK#aGnNlva!iJFoYbT~C)Da;1#1{{9K|2uo~mG+sS9_uxd-qL}{S-Z|kC`(>Pr zF%O<%qP~m%9UcMQvCQ*cj8rUuwVe%q*$mOG(^?a8cTk~>moCq1>|OXVB5Iubd&b6l zqqj)wuBP0zw&wU7uZi6n@?i8UqmQk(nFb?jkGreB(30bi_WTz&{VlK5GJ~p20$%!D zFJT-l>r7K0jIXlRtRK_-0k8I2AuH~b?T>*CT{!hNOCEbl%J^89_-V}7zoes986M&) z+}2;>c%i^`l{AV9d)>HOoKjw!npW_QYFepfS`j23@SZY_MegIjc}r!NxKpxcgmDI; zI-tdXmS9>WRMZ~E8ble!Y*-)wq(=FK9Y5dSQUTzj8~F%(}aI8A_PRU z!}9Hq9dZ4Xsg!>@95EhwGF{ZsZa?A)gCy8FM2E>ij;KW~77efie;_L0u!mv+Xe`P{y}R%avh;N+Xep&i{9j}cY3m zwAcU7Ae1HIA8Qv6YkSRV_-tHTx`pj55r~~}4Sb$HbZFzc>MwSRRNLdkPSruMXjM@Y zyM#5Ci6fQGfZz}WWd#O(wrCY&d+k4r>{pQ~)E_leTeh+pVKqEdJ%0#%r;|R@$G`Ja z`=0SF!p1?+SBcTjTCzIUkTSWauQ0xJeN0`}Pu9}X)9K$91@}JBN=F` zVQ&TT_O!}Wce^vMb zKt1XK?tg45XDQDd7Mc+GyO8k!)yx>XE$}Z--wHgfOy7T}^sBf7(qrqFDOeA|T|MB^ z{LA%?LCgn#SQ0VksAlDF<7L`h=7x6KP-*^wVGirKx*bGrF^On$I0Lp{_HN|SQdcI zrC=?QBw&8&I+25!^!xeW(UB^6DxVsPiSX(6LvTrkH`N9XbLl&ZJK zDGWqo{~fMzqZKXS+qZ8+54(Q9Ve9RwMal^rZXlJhT6xAhh=t)`DCVwgy8I8b70d;m zrkfi8Ib|YMp}c}(0ve+|g=MQ&QsHEXd0Pz`u8+dsJ*n6Rw=M;I=Dg{>LBq5LLT3v^{)$5%4Ko6YG0sjkI|~_>kSquWrj%j zG9L{mmlBv{5kZl-`xZ5huyEy4CdIdq2s0`H-iAILv|cHM3HOIqAzB~3O}d_eqg=3~ zsN<#L^3tPhEwYsnc@0hUi(D5bt>kx^Iy-cTllrj3m> z`&$uVkzxLA=b}Q5yisM`zv}hqlRDzMcDk|E7ePLUjB{xrZ+GmP6Fp@7T#Q~e0bB;S z0`o%ML7hdTcit^QnU~FSbRtxLk9;p&;e|+>zTfq!;bwEmDerTUDx=l1oME7jMkQNF z9zH7~M~_jP^!LEPh!0!Cu-wc1Vti;u{J)YO$pssdR-AaFDRp|3SvIN(3bAiRBz68F z`{m`if3qb!f&Tx?hcQo|2sOZPN_B43H-v}%%`-%Lk=kv>_E^1mxLvRD_>{-j=HHEA zv=P#j$bpn?*PFSvb>3c}|Lyt}=h#}*jb{3cUDaxRM8L3YO(Ou6cJ9A=8!>vWGMa5# zG6GCos@kG~<|HTe@qf{$BE&;zv_Hgd#G~6?p3FF*(E|O3kRO}3j`wyqdnRt&Wt=E+ zj*v3W%~sFoUd+6IWf8j-YCt$b6t&A&Qf%KiALt-F0DmZJVCS=xuBhYWyVQ~6p9@Ek zI`;~1<_@(}7hm@87?%(`OBySX6mA|w2-|!6l4~0A6nMmr0hNh;u znc5>$BS*_o0; z0M7i+U)|)a4ZnQ(2vy{S=C!BN{M`g3lFnzHw-4EWgyX-r@H+g~RXDQOi1hF3*Nv0q zej5T%|5N*(>J{04GHJ=ola&7*xxIhG;p2bG|0~1xztvUeUi_9)H~gPSYM8ZbQ?9lQTavk>q1Nbekt3A}Mntd@sLOT~f8*JKK))J=={xW;08Kmdga(<{IeQ{a33zkl zAIJZA26VNLnz%HH+(}QSgZa|^GCx%Cd&UqE;w~r1{tm1edz5X<<~6aQuVnMspwn~< zJ{l3(I6I5U%V})PSzuSS6s9F2#vu{a^Y95J!N(^i9^H*mVD)aWas*2TBYZDN4sh+z zcKA*R(`ef3gA`UNZknv);8uf-TNS_AuHkB2TOo(5PtVs38aE|c4G?nKPV`~Li_weB zaRlcKY6P$I@=Jy8Xln{MO`XwcohOUEZ#P$TEH-q>`{|1vhcX1>=N?bxmnurWBM$p> zR1EPn25G~xhg_1Mh5H9*h3s;f4y@4%@ zh$;i4h_?&2^km6#_hkZ}_u_U3Ry2?60(9-rqDbC%v)_U~pmcN{ejp!B$8O#{%{WP;WIgLPHVVQZ;VTnNojv*F>uz%M@ICajYARSlLgHM{IXr*O z_F=Kf7MmC!N67nd?^_qA4Ishfq0I0;w0q5`p)lD4LQ`N1bXNrlB#or9 zA|rLel+5ciur^DYnqC&W9VwU+j|=%$=;sxa=b7zyxRw(pU!Rd@|C00N_qy!12MVQ? z@z_%eE3fPdEK}D7uZ+avjS6;v8*2Pt_@vQ&X&)+eSu7O1----2)%Jgy4DPAvrCz9#IO6m98Ri^{1;S40bhK5*#|+cq5sDhe{{x@AL#*sxW)r3)cgmCfPUnw1j{DjRYF z3NDt8#kBmB3kLCw@Y~$6%jB+L!Si|JsEA1RqqX}5Ur{felKHEN=8-#D>HEE^T=Gws zetJq1@3WWfLqqW&ev>12gOqr0tF#p(BVAy897LqwZ?5Ko21*Yl$ZX zam47yFomPOEVqMqH~7X}u~Q86l>qBD`Z%78N{Gog zVe@#-(reZdz8^4XIObD@(+_qRB){u)2+g_PIAeKzWQ^N;7q~?tDp1_2LIFQ$R0=kp?pJHb7Ke-)vMNKBbQ0TZU)dRimfSl zcxWGsO~~yA4`IScl_+q9R+^YCP`Xi&E;KZwuaafqj?>;0f+ROBgpp`mCgB&(zAOlGP)IMy%v z0=UYC0t5ur$GPW*n8y+!ZI<+Qs7-12wW9pxR(`hU;<8UPs#fCV6|VW98* z{g~lVyDbaVOHXT6!IuUK&{*yvxv7E?ZFDZKPilb!a$Oz>#?|}7xQkn^O;=C$+c338 zGuj#-Pf2tzlbCL&+s!1_gzc;}JHABvJdSmg^8%of3EEy0GVb4M6I@Vd>L9;qMsraTqu1T@ znK=eEtvRpm-@&Fcy9_Je=?=^}T$r=F#N&t^{$y|*U_93UK9q}vViVpb>B|dDew&cV zCBQc1>1dtqiL!ZbP@A*f9VPh5RbP?YVQ*Zjm1nCl0{$VkK|Q%4%$^ zt-XefUHbL2GmjDwcDIB+b$O2~=x|6bn)+I5{5_sP!bsR384Bz9F_Q}rqm9U z+9a5JX^gm=;vBE(M2)&p0+%vx8=Zy*n!UX00HN~S@_1)e@V2I`D4tyl(=h+}W|0+4 z(dNjLje{UhT$q%koq_V2waX*12%E=GLvLkc58)?n1JrU| zv?N4ZXq*M1DypKN)^Rz-TOM@zOjD-2q#Z3Co0@#dar2+!Qh2+YcvVGL&^Sljo{x8K zRG>d<_~;F?FZ7CW==7^TS)_I5t3f2!pS~BQ%`ZL`WVydR|KN7nk{@ssAEDbcj8Nqx z>gn9KJ;6caUO1Y9<0I+`fKk1oQfqC+ISxT?0}bsj7-_sd<2Ji>4;K%yvpUv**y3|I zsIYFhAgxXtCcgWT-EDfTq)p9bc2Sf7y7|$}KB?Zj$QROcvt})d!(8zS;bZ!>c7y2! zEU)uWLp_*)VJPevMiiGcur*C2?D`eex{+YN?V^0xxF=470&HKeEO~Gc>H!%~9Do+y zV_rO?NYupGkE3s(sU-LHo=da?I4q-sn{)>;k8XlA)WNbIvpfEi};3yyNfQJNM44S@ZswdF##juW5SqK4Rr zn<-XVgP8bk1~0{xsYR2@H-`S&zu24PMittbb?$Tp8>IxR$TBq3&UmLimuWR}QwOYZ zj)zVKbewNbz3+mT>&J&as|u9sMb`^_`~6&Gd(s zc++VE@9at^!>=8>&BvPr*8Y*!%gaeE@6vQkarX(I`;|Kh0q7X?sfAfXE%Hv!Ov1n6 zEIjA#^5g9J=}PeP5-0r+*u>)Ed(e=aXky(SCNZc4%4bK%c&?_^Xj$6d17zrDHNLXC zf*sY7V!lvQd-H*BYx{DbPitOsU2LZ)I|m+iJblvA0e7(()0(0U;%NVkf13UwHyV+W z+O;WH^!Ry;*AJ}7{kKDWNW|93tl=7DJHBuNo7q^r3iB%OWoK$ExiD)XPBovua*H&* zEZknX0NsCzp3bk1CS5O2sZF#Rb9Ay$Fki{XV+}pVuk+e_LWhW|%hYR8pd+Ki=MHs? z-rJec@|gT?i}sC_A-Y|4rB#dc3ZC0{L69{ns2prYJ{cy;7 z9lHXtb&i1naJpr!!e(zI{T@>wZR~Dkf`M&P=5O2N8UA%xsAeYeVz0NWrnJ_}cp^&; zN4*yP2DkeN4Px{>z>Sgl> z`Wz&Ca?5z@2>-}+23Q!u#Wze$Pa*%BLVLqeHL<45wYAW9ekUuFCPaH6K`*9nCeX7} zym3LaM?kHbnwg!uvb*{kI|N?f3SPHr!E}+(O-Um1P6bN8bKnfBC_5fE4`7YM;)~~6 z?&-rZ!1*m<3x13Fshv}Zz25(JG;o9>p<bI|`g zEcSm~_xRdg+bl1#SJvUF8;dF4hTIKf*EZ)J20)A-K-Qb;nMDA0b{MVjF(o2xX{hBH zB{$&N_~a$Bh5F^K7q24%lXktQu;+X5tc6wt1Z^}=E<=yUfoWLNwtD5cMbzIG`V#*& zFgq3b-E5&m8QZ-#Mmg1=_;P$s_ZViFDc~=uMa}NEiXJrop*1e{+zCosGYs$3u>fDV zBHui?O4>xYBGqUR%U*okv{9Q9MGSEQgNAk<9?6r4C~`;q(5jt>O;6Xe^0!WVu+MUQ zI0IBEE);*<-VY~xuT#boukn2q;u`FAOH~DcnD(+h@`p}FLxFyO-xF#V&yK58;%;98 z$WJ@I_SOgqySaN368ykd1J@pFCEE98?uxl}uhW+30!N0}JO0tY2#*MH$GSF>q2Z?7W!OG-KK_L;UPA?#lZx-rXA#jK-NKK&E$hCIc)YQ=Jqn0Kq zvhVj}yodGaBY8LT4n#m;Eko5)X61&Wb;$QNC11H!q}cp$h6K0cv^y4Oqu*F1YJaU1 zxFN0IL%ox=Qy$T_qI!KuXGUo@qRA~MsM?PG(8B}^nT0JTw`ze}(mgj~s9q)Y#MMce zJYg$am|CrvzqxUTY(D`+PB~3fkA26N3xb5Tt~(M;j^`w2ZU6QV0grg(!lYZLMUUW! zfh4`Rl>C_%C?l=rP0g4^`r;2ijPWF0@kFDRIFo4?HRBuveP0P4t+&!7JlLaXzS0n9 zlW;V(nvV@AQT9=*-z0CoPtK~XE6~v-r`s!(x1L$Duj*ZR#>|Q3`6eX`#60yMJ$K z>mub&7;&eqGH}HJ?-vz(7c>R0skbbO8cG9YC?PacE9y)k;~$;HQ{g|44?gXpP(Rf| zvfHnIHZcP~EDQp9_0+G^jBGM}Tdx&g*FQ4y)0zqCC98XI1^w)c9q(km1A%B=vy8N~ zdA*JOaIsg*b0Je2cYadd7ltmxl;CZ$z~?d0#(BS#30dRd;DHj38QgLOrBkFD24$ z0{CpnMY(nHtB3C<&n<2={Hq$RdO?U)^qyw(mFysovqe+-s)dyKAVkqJ-k2S~UH}}d zS8JIUC2zTb9}|xMVtVL?J2+tQ#)G6*r#W^CPKQh_>@W9z zhGVeDhYPl*rjIxei(Dv}ZdWsVRf^}9Fr9AF1wV<2fkA0P8+1!aDcvxY-;rnk5and4 z^&S<0ZXF*GqpTM@Pe@yQOTonU!&l`Zv5vx;db$u88Czy5{%lp2Hpf=;R{vF>*Tr6P z_j=X*(ekCyaeS|9bBY8h)&L(r8D2Yz#i)B5D(<;a=Y8y28FtfKWW@kR83WAbh<5in)QX$X};B7 z%Zfx}T9b7&kXpp*T4c~wavIYF<(Jqox>Tw$ndV_k6cGK7YNq4Rd1E#?slO#jlU;QN#!AUiSB8I2>)0p* zboukX82fcsH8Dd0TMl+cFgBRxe0?X4*M6l~+qT+)X^bA(!XrU}^`$w-^Hf@d z#fhDb8s>UIET-}hw+|-)BRovo$Y#cVD(LLje(7G*Y+-NQGhUYbeTNxjzh@tyuKio> zK=h0e^q36JU*!tz1fxloL8Z6D6IDaiZ?=o_=#^fbQ{uh59VV9JPcn!ou= z*HEF2l!k! zL8G;3mTL8KsMHIWZ*pr&icnBvlm zmMeoMl_gS;1|zS(P^4@@sae~vHw8)Vv&07Zt#zdia0Wq$r4E(EPX1&O$TDzb+M+z% zHU^kmcF17&cTOYW*7_W%_I+k|*y6$0~m z0vl6EBZl$mo*|xY;B~Gw9lPExe5T&ykbgmWb}Lt;1i@IsGv_(F%_~*9Ebe*fl;v(; zANkR0Qw0B<|d@9t8}t3-F&y4?qyTOX<4y5;q2>gn)d`!%`0mbm?116aJ|m#YTLuN;NXMPWH14IrPg|s?8}r@=49#F5P`O0JY#Xro5(KN zh=B!-K1HQBFCF}-wP1fugE*^>PWl}Q7fe@X9=9NT;(E39vh@@7=^8cF^1ozK<7L;| z0M+d!dCB^@pE68E)ZLGZCPiksMS>z~!PW)4dc!WXH2%s_N8cmFIla2)%eMm)Hf zgSL9r!S|5D$dPJU5fR15I?r9Lq48~jpv*`fDi(NL+FZM7C@Ba(Q;^d!Kwg7>WEY4l<5_`lht&_xH#n0i#z#ZE{ps#jUARAHn3CDs= z1E=kW0WG7c>Fl{Wkj{DufhU@o^pQEk?@uieBB`=%Ee8`PNPKq=L}o+2)M)3jVmu$l!%$wu-iE?eW2V0W#Ws8G1Ccr zL*LhkIE|t>O~kJ{ZO$C*2JO-_&&c{L#)88ySQ7Ufj zG50%VRO|WO`gRzqGg_xe8C$%3bz9Q);n;b3HfA-x%ZK@HEI80FLTW_Y@Z}3}jC*UN zoK4n8lQq>;(TYmm4;$!hwRUV*!+!P4=}#PEeQ`cuEZ4A}5K`Ml!@cI55X3}61%qI#N#UEeo? zvUggpqcK!eA9n;?k*6q<(mfK_2OON@qWa5(CVIqlTU)Em%6%^mTj=kfe0pK|VunBS zi$19VCgpX<`$=CBtYiMu>if0yODs?z3I2wu`K}1FhS#$H5f-7BQU{0WMBq5ugEZfu z_7lZ{u~L&kiIz5g!4jP~&aOn&7iY;@w`ak5BQ>EOce{0x=1W#>fJrLZnJORNEvd^I zVkLF&gM@bu9va? zM_)`%)|bgPb8|S)SJF^&9jigVquZ3Uy9kPwviSLEJ^(_q>G5r6Tb}gKdO!svC7Bul zZRpoCX<8obFVlL5jb+9SVQIaI`K)@vCdc}7aMo-_ZNFw@$?KFiU+J`I9Lc%s<}YG| z>u2VRi#2xlNh|S-EX;%wuNTrh!(b*Pko8~8c7|mo*cnLj>Y4gDSa!#8Y< zdrf|_S~E*pJGLjRS`h>1F71sV%N2~sir}9)!vQzdd{hnTBQ?#Vi9)nguSwfp%{e4< z*4dX-oP|`Mvr)mGE@rv5f5G_6*RR0Mdn?z2J?N=qKkJXwnF(g)vK@#x1|dK$!x+)c z4Yo9d39q|C4nH$ccotqW8Q!t4=8JoPPVPUfh`$t%R}+(3=HEX1I41X(ykYHnjK`Oa zdH(S5)TssL|8tQ&Po<3Lx)~&aFjn)to&1pRdf!`oFVjifnJc?#R<&_^oeAY&I18j= zHZuQ(h*rnfU+Y#@R22T+;%LPUGAQ79@qCfzwP>j|;y#|K8AxL3IWb^kIZo%#$R2HJ z@5s$bJ~RT95o<%jB!3cQopgxIz|>Z5EI4$c?5DtSc2DO|v4eYK=}B|Y{E@dujoK80 zG@&YUq8p=LA z@sG^$&-Fi&N%KFY%(EG`s?{skZe4yGfS)nE<02Ro6a+kACvR3@u>*tshL?zOcyJIg z3P&L10L@Sn%(Eu!oii^v-%Us5*-flIb%x*uYd;bLGv5kV&NPAzDS-#n#5X-NgYSa%=+R5S zJ23!+Qmo@Cu`E_PZe8iB9&_bM;di|?iw8M1Lia_~`(hIw)7*=3@bdTcWQ!hl;>&`Z z<9xdTibD@S46efZW6?QQq8Z7m9w=?mi!D@`mWVHZ#USyrhoPA`E+pGgkO_p|YYKpG za(sfnO8z))=jV+FnyR`V^bg&UK;~@lm=lT4kxQ%3a_>X~K|d(NI!eKz@?=kFnMV)Z z`nbg!9{Q|w-a;RA7FOTSOn_OD^Kv86e#>ilHfP|Rnq_Ko=K!Rd(xDPs?XROlPAC za!5b1>0UIVnnKa;eIxKF&5UXGWiHoaRe76{EY3Xx)2TtdcY0gFZIV}jp78K$E3I~G z0|Mu5CUZ~qwS1sZL*sb%e3rG{?esS*-?AC)-%;E#c6{sm5x*z#BOq%TyUkBArDgkR zig~Tm%g5tnD-BTcTibcPx2lb`2g(c!Z|e+(yTw6q9^{12=2&PL*+X`tNE&EOm`}dk6 zr}bdw%*Nj^-|eF9h14K3=wA==ORxY$Xr8Dt9PF6l!*O< z1>`P4p`Msz9TDfHW$4iYHt1(*B=F}n%YD$46!34TM=+h`(AFzkChk`QY7d5BjrD;4 zoA=#7m5=m^l_IzAU{n&z!5xZE>6M*z+{&+turc*p%do5_oC+`d@^aaehDbSc!MsXg z4(zAY6kWt(cXbgqX7jjdF)QXX4)!IPhP`KKP;1@8qqS z=9p|H^aWyyiRrR*?!2bfC4P&EApU~F^2R0WF{#>HbVekL2{J`3y$eAl5?uh4c#Ndw z2Z!gd`HT!gCj?8jGt9fg}+=jfB)9 zVJ)W)wKFIgNyR6zv@zW??ppMALppp&Q9guB;QO7aYc{u?bm^1|Jn!Ggn+jwfg9k67 z;4=}6?+R@*cV!T0F=pXb648`pSu#;Sx$cZ8G24hqAHvlR$g1*L)ovf2CqC_UH18Bu zC8*^_BKy5`5*OS#Zblo%a}HFT3g?25L9^d>lkUWAOI8y~E`RM=cLvF7ZL=V`vsSOS zJFucV-}+n}0*Ziu@w$L!rz`#KU%QR;qOFc|QQZcRw0%c})RF@XeqHWJ)vA9R*3jpv z-L)CzSwAh(xg>DW>xjh>gjhl(y_%p5?>4QSZ#`{Y0_ohT4Wgx^o_jMybv^XT=-vSf ze8BjSVd!a2%KL&jRbuBpysl-&t>D~8?H2jVRs&tbmkiNcGkhSPc79-|Cq>e7*c(;OG5LIm+}HmBE7 zXn(dzH5h2~a4#T>=Thx~aUjYZ)$2YRV$C`^-lm(DPn{B(f2JF(muRwa6Gf=1do_Jm z?r^Z9z@_qGhu<-9C!x*oNu-L3dkUuKVlDjf#Gq}BHarzWiU98sdO64ZnO$5q;DsHI z+<4Hv-U*uNC4iMRHP?k2VmX#9T$lK1`Hubm9lR+$eB#}sq)2k9$|NIrQ!@FfgYQ-7 ziB*(IWe}cgzO0t+w%=f~d03M5gRZ{cIKLwU_3XR?I!>rsSrj~=5>!!pj@O$>2wav? zuI6Ka%>8FH&WodGc;=}A#!}9&8S?~bFe(W#J;JCYTH#Yu<$;O!7F3Lgc<;BX6FzHK zc?plG(k;6;)HzKR9R_)Hf9;iJwa@`F!m7RAZY4wpN(KKQ=>J7HRS#APv5-v9ExumJ z~3sHhye2x;@w{Bw)kXk9s8b(1_5Lu0eyjuEJ=7L1F_cUkffH7o-nqv zI6cp1Qpv-gEyr_kF1ujE+aO+PHMLbXGL3IrlW%FBrPgj)r2Qqc(8awV=btnVyk%{5 z@%l4RJDCJTf*dnoCTWY$itAyvbW@%Y7J6iWrp_(@MtbhOHz~SaQ z3NKpbGnlvz+==emkZKNvP&{KJeRdFpO^+WFDSC0TnZ&?G?MjrVu7oMqPk(wFU}{o_ zIx5V&=s-Lf^m?Mn!>-n33wlz$AYaUBz8*23`6J@Bm9LWS)%;C&-()>q#jvry1MFsA zr5~nXj$yHMBD&A{ue9#Ow0CVUl^!>;yU9ZOp#lgwRqe(T?*bgwj`)6tFJ&Br`tBu^iY2eVrTn z@ZOK`$gN4M5)O{eBZI|&TgFb?vT}rpn;@z%qaKn} zmceBm%~OR%NK&hAfFY5^or0BayJ_jz**KiOlo_DUA7?9^zt|&|%_#}xl~U~EP^6|G zVrx!I>!WAMPQj^YlN$xZ_24*6o!M={1G~uj8OaO^qT2R~8PgI^W1Vr1vMjS%tb2_NrQ1UKqr{>-G5(bj{0*+$E{=r@LY^1Q8ykMr>+DgxVCM-#6Obuzp|V#raK3J&kgaF_jw+83HDx* z2x#lFVP+8+yODRJ*O^PH)m%Ss-8k)Ln%F3MeD8xQcWI5WaRtrUApALgHoF9e0P+Mf zkS;Kxzs=%$&_?X32d*VX@nt-dlv0f9O7(v?cH`~tu#k|X?ii_`1~zQZ-SnvE)Zc%9*&v!OjjyyG*NZMMrAq7xYepBeG#o z%QM+Zq{TcOtlkctcS0bz6WfEzt z%#`j$*oxk_u3$vg=LTh8U9Plmdk9W`mV0c84B|WY4Y*o>hQ-CT`oHX)*5QVsM6MN) zK){{|YBg*5p}eDen0NVL<8_|h)zo+#tHypBCnHg+kj1)3tag^{bYrb!}#D{IB1{ zS(cDuVDgahVWdCmog%#OK{u{{wn8?W^QCfmv{oo$7u@&UyC;F=J!W(d3Blt|$1&6< zc7Z|r|m9bPp0GI$~q;%}<45*YygHQkf?UYyXOTyy&n-yRJdo2_pkFAykO zj+h9rry1`aU(o4b9-wPa6ygJFFggg2WL+D^<-e0a^d3fhM??O87M5Aw^qMo^F;EpQ z1g(j<^pN~1&GS#Qs;0HNlJ{4Z8wbmaO$n$jFU~7hbQSN;e~J^f@;f4vKzAFMNJEqp z+pX6i&15%jU+P6*efiYVZY4r!7wh4JmiMaytI|&hCOU&^ZF7QcV;yv}h2AI=J1<^5 zX$H+L2R@`&1h@llHvzD*G7CIPcFvD^lvSnK?XM^{rc)5DBpPqL7*F9e5 z`s4)bDQnUH9P_E)68%0fEJ(IRBi}#9m&msGi}%6(>wwk-iVDx+)^EQ=9$FVM=YAzy z35fivq7XZ=yX!D3Q}8x1^5M?Kg%3S2aZh97WtZ__LD|Ebmgaa5*56g{7WVEaJ$faxwaavUtf52-oZlsW{3?c(Gq|NQ$%QF640h%)ZHtbcuGbb*c_Y3Hloqmc;4X? zrQqEzoO(1M#(NS?aAj!Pr!j)3a`lH0dV)pY@j7Q+ce{t|fBbkOcHB5W(-fkh`o#a7 z{n&Yx7>YdFdlk6W({E8rh7sv%(DrnXm|*aviK$PkJ3qF<{ngQL_oMSr*9j_V7CN$+2856d+S)x7YhS&d-OLH zatv!MYCdb1j~cdd^4LeeEgsNKQv=0k1Awp3WT(t~xzPA!4r25Qn|hKr70kRj$iQKA zWoF(-5CheX{|Q~F@tcPL$zfk3HIm!JJ5AS!jZj2MYPmLC>B_akXVTRZuWEF6*S@yt zMrtlq@p5<#9V+!Jc?=y{rh$`WU9S`9Ycs|478f@OtI3~M27NdA5BLwWNiTS~UCZ1+<>eE5?C`{Z+JSu+?qxuRG4>n!#u=)kId8+LN?@2KFJ3vpp) zJnm94uMMhv%q73c!`g9dbI^2Fh;O+9*@E}4sIZ;oUs2)OE32vf2wm#tnQEA@?>?<#UZDlu?I5;2vrPZ6L<9^kxkn;#?{iIf(X3)_6B# z?a!B~Z6Kv(&mVa%z8c2uQm_GV58&1Br>zksvBED(4Jl+cAG`>(>IxJc|`}G{efBg3E1ko|%4;pK1e~Q`V=jq8;zyO*L{I$p& z`19Lb_Wcbtu~yuOM+8XZ6;Rdag8_-kq*rU!r6_-fidvJ>AJ$Wm_$RdrNZ_uWKlnST zLcrkwMRYNEvzx>MM0fwD3wQ}|Et{CZP;KzE^&(bUgCx-PkN45NHJpDI>BG3j{BuM9 zt>okXQNdfk1vfDMzlJ3CcOnP8_<{HaVf^c_|GUZwE>z7&^hzChxgkw!u~L+7ZUBaR zkNO`Z5i1eHHhpP@5jzc-F8LyZN9VHLYTYMD=FyX4!kS3h5w|vHm$BlNQ>^7YAXDrI zTZ}DP8;q+G@6YrKk4xE9>ls5=&q?fc;gQ5~L1yI>T)6mMv=^#D%wHgXpmAeyI_U<^ z12o{nmu<1=UHF0fO=e#73}si+;mi8ImnN*OjF#K)5S^0Wm}{M3-Q=~54udZ2&BjNTt0tFn4UU(& zV+;hYSTFDWC?C{KXcf0_R|l%1jzs;QBMKfYp3iRQ;xNhEjNGOzRL*84HpwoZ{SS&h z*TW}=|0?M7Hz0LJTmMV9XVC=srozDBm+dGouKeXFL9sqo6GRfV7t)p<9)0;A%rW0u zw0E9tX$&fDh`8Q{gV?2-9CGJzhO*I`i) zvTWa-yI&J0nSL}&rqk%880$E@2;-?u%t`qrT4|;oSdyM*vRJF?@hP*|E9d%mEt=}5ou?(m(=cfE$(aeY3xf*D;L~yIRV6$5S?CLp|ibZ zV>wVRpQ>@0?R9q}ut zM9?k-%KQu244~rpcVzS4jcw^20;EC}IQ4rIf zSj>R6gk2XB2D{pzN7^N0#o9Y5SX)XvMUaP~1N71Y)^95G1=OK*Qd!IbtBk%oF^_d1 zIm$MyzIr9B6A&2{yy3F>8a0`GZr8|99uYbKs-HT>ZRv*rsm>KyiCtmzV% zyy=#P&d7d>Xv20a6?a_kQFv%tp;bIqg*QIMU%qbP=pQ_`?gB&woo?6>0~<|C5Q91q za!hQ+gEFYH$e(aUPMCNA&{-vDeLV;d&$IKUOZoe8+oa!h-Tp`1rgnb~G|Uzw$oExu zrx9%Auae9fm{m-$$NsRA$xHbdQ)W?v{p`|F{g*|oRAqwf za#~L8!-1vI7N6WKLV=WIpY-0IB1Ie#nvmJK{J|&Dj~+LqtEL|XL|R(2IZNHL)Q1WQ67-rTPR1Hb$L_o+v> z4_Sc0?Z4JrPCkht&wp(rQGo+CZbS?&(lwk-H$A0jvI}O~+k_`oXAG~xPZtfs-ryaQ zh&kHLaR+{~l`rGd(Ra{cMgKP?UvVpN?-tV;0z7wH(lTM~*LEq@=B=NpsICvdP>tQP zr6n(&dfm=dr?|9%9Hh=2I|KBjqKv)VJyVB_CuR1l42Ec}Jf}CgT#mJ2q8tyOURHU{Pb31X1?>r@0}dqZGP5ZRrUZJHQOZ%dgpHza}SRamoa1?Aq=z zHY?9rapk?T_N+*jPo!5F{s?p)tk5n_EqkUxam0CGiSvCYKmDnKHz2dtTo{RxxN#lX z6a}}B3_X3i#!r~GQIka>nB|SOGuYn~!^p0SEO@2nTzr_qDKb8PmB>EpC}w%$zgl%p zkonU=x$f01jKEsoDQPAJ^ zWo1ys*AZC{OJZrgy*+_$;y_Y(RI_T|X4-&^l8+RA@{HWKdXUSSM6zn|*6?MXMF`0G z(FL(z)IYdTz`Wy$WJ>TkV5llTw$4%%?Jim4j=;u~4!ofUZG8J~|z1c^v zUSZdHRh~Pi#%*lqb>8u2#+3`@X<4T~s+cJsc>}Zb=OCPMoy>tHllErnJyw1!baw-? zo4#gOQ`>U^+NvpW0&RDO*zpV7l4snYX$hV@I}WZZD5K(m)M;L)&w7a&iK>vD0tNKC zSdXtd!7k^gYX>dlaz<>`Gr~5t-DVB5)55IvJx<=gaqO*og){m&rCpm)BUKjmZO7xX zv)p=LD3-=nxy>$$H8k8H9qS$tALg1ydowL96?&|9zwKW%CQ?z8KXc2i>D3;_^v+@4 z`qaUEvZLj#w&8$9&U3!P6*JmLhh0o~mEpCoiom8LDR^C-zbzumhVKL18jo6KfP$Q!d5%=4Cg7n3~-d}6fI`g`?Rxr?F@O? zg2$Q6CzH#*+9^#98EzkJ2doe2ji7rBJz4ZrvxO(5@q|;1eq2HCQaY+?$pa+gZgUi@ zqXP0T$QPz3S)-_)|E#~$Hhz=%Ugu?94pU{x#xK895zuwQOb#l)s=+$fBeb}dGSfwGkw$=jTsqw+^HhGaDTZuiIePz~jWz{K!Qm zX)mjjGu)>~B(H%%6ZOKTquGH({tGVSA{ zfO-^X44lm2kNC!DX)BxF1RKVdHe-S_sKyrTbmTAPDkjF#mBt8>1_vnEIWRcQtZiw* zI9VHV`h6QN*uVI{%q{Boy>S}EJK~XpU>|ZcixjQu>mG7olRevjy44vsdi94@1&!mL z#n^XFWC5Ly>3{wVHqF0yZ)zC`ei^uOnNf1ptfBP%K(nJ@aT9^n?nr5o`kRAeng6E! z>{GI=(y;SxnX>M-$R4xxp;hhs!8&z)wh_v5N^LAK+Z}Slv3ur#8SSBKe6UAzE!<9* z!?e<&wy&;q`0?cWvy8W3D8hr7#&T`nYvMblehDCp7)sR zjSY{A&B7Jb;hp{Co^Q}hVZYzS<9S7u+LLuw)1SkM91soFV_>2#|IKTHvwA@Ih=P$A`OX;OtPNkXgY~ENwzjr z^><=ms>g4*ju+LO;Y#Q%9kn>|jpm3T2frVT1%2$7<}YkY9|Bd1R3%Lw zE*EWVMuhj@e2M&cDH0@jrrfZ9+VgGb&4S7{43 z_sm+*GC^9|pl7F|G$RS^_YR>lU+PxwSOfQ}UrP#3dlGW>DR0A{9!5HND=W1y_8x;=8-yF|9>cl^EtFONc#~iLq3h-G-Gk)720=wb$)R-R?~v z6|euyJ7W)MIWtS?_d%tnAC-H0c3*a@r|FeBEe{l|vO6wxkqU`^GszGUx{5AdcP&iv zwVH@Qq}EmUm$1G(tu6IVF)2f`5at(kuIl>_NO0P_d>F_9oJJMMzgOIq(|%!8-!{vB z&G?GP4g}br4<9N>OKP@co^z1>o}93tsTKCu%6^;4&88sj6Ediz!$^urUT{8iq@|df zD&ZaiT@;!02c)b2D$|S8c_;lS^i6Me22mP+*JY@;H74ubLC=CA;L>rLCwt@`QXao5 z_tN@K&sA{p0xtzSGG}?WA7G@E4Do|kA(5(2s7)x5`R|a5rawM1of063S;!!#i|guI z0M*Hp9_FyiPjj!lpXo*Aj+wu7t_=$YZQ0+srcQ$6_O6OHjC$9^B>W$pm+T}~Vt2T* z_w9=?p4Kb08wz~+RfnynOnRoOB$-@T_T>%z#o_G?<6x?nNaaOzq=z7RXfKV;9}$lp zS4?Od^_x<6ciZ~(%PTcBOpJnpX%`A?~q-cuszHUkF0G@ABP`3`qFH0k`!dzDMnie`ja?q>~9GS~=$zR(*NQo?!0IF=E_P)bh+G<=;#7bj-PPApb$CH*2KwbXa@vlm~obJW^aX!w=+O#-{hMjKhNj-jqFSqp;n*7I=_qBRmV-*E0LpXgi-6@L{ z4I-O<)PJjjw^aC9m;#T54x~*=w}L6vmrqUGa<_cbe*C%C r{83c*?%mz>@Q;Ckml%Kc1GIZg^_aXlWP4hGr literal 0 HcmV?d00001 diff --git a/ui/pages/__screenshots__/ValidatorsStability.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/stability/pages/validator-index/__screenshots__/ValidatorsStability.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/ValidatorsStability.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/stability/pages/validator-index/__screenshots__/ValidatorsStability.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/validators/stability/utils.ts b/client/features/chain-variants/stability/pages/validator-index/utils.ts similarity index 93% rename from ui/validators/stability/utils.ts rename to client/features/chain-variants/stability/pages/validator-index/utils.ts index b3454e426d..cffdb38ae5 100644 --- a/ui/validators/stability/utils.ts +++ b/client/features/chain-variants/stability/pages/validator-index/utils.ts @@ -3,7 +3,7 @@ import type { ValidatorsStabilitySortingValue, ValidatorsStabilitySortingField, -} from 'types/api/validators'; +} from 'client/features/chain-variants/stability/types/api'; import type { SelectOption } from 'toolkit/chakra/select'; diff --git a/client/features/chain-variants/stability/stubs/validators.ts b/client/features/chain-variants/stability/stubs/validators.ts new file mode 100644 index 0000000000..8915a097be --- /dev/null +++ b/client/features/chain-variants/stability/stubs/validators.ts @@ -0,0 +1,19 @@ +import type { + ValidatorStability, + ValidatorsStabilityCountersResponse, +} from 'client/features/chain-variants/stability/types/api'; + +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; + +export const VALIDATOR_STABILITY: ValidatorStability = { + address: ADDRESS_PARAMS, + blocks_validated_count: 25987, + state: 'active', +}; + +export const VALIDATORS_STABILITY_COUNTERS: ValidatorsStabilityCountersResponse = { + active_validators_count: '42', + active_validators_percentage: 7.14, + new_validators_count_24h: '11', + validators_count: '140', +}; diff --git a/client/features/chain-variants/stability/types/api.ts b/client/features/chain-variants/stability/types/api.ts index c70dea3a35..77fce6be8e 100644 --- a/client/features/chain-variants/stability/types/api.ts +++ b/client/features/chain-variants/stability/types/api.ts @@ -13,3 +13,40 @@ export interface TransactionStability { validator_fee: string; }; } + +export interface ValidatorStability { + address: AddressParam; + blocks_validated_count: number; + state: 'active' | 'probation' | 'inactive'; +} + +export interface ValidatorsStabilityResponse { + items: Array; + next_page_params: { + address_hash: string; + blocks_validated: string; + items_count: string; + state: ValidatorStability['state']; + } | null; +} + +export interface ValidatorsStabilityCountersResponse { + active_validators_count: string; + active_validators_percentage: number; + new_validators_count_24h: string; + validators_count: string; +} + +export interface ValidatorsStabilityFilters { + // address_hash: string | undefined; // right now API doesn't support filtering by address_hash + state_filter: ValidatorStability['state'] | undefined; +} + +export interface ValidatorsStabilitySorting { + sort: 'state' | 'blocks_validated'; + order: 'asc' | 'desc'; +} + +export type ValidatorsStabilitySortingField = ValidatorsStabilitySorting['sort']; + +export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }` | 'default'; diff --git a/ui/shared/entities/validator/ValidatorEntity.tsx b/client/features/chain-variants/zilliqa/components/ValidatorEntity.tsx similarity index 96% rename from ui/shared/entities/validator/ValidatorEntity.tsx rename to client/features/chain-variants/zilliqa/components/ValidatorEntity.tsx index d6f081ba40..984b23dd0d 100644 --- a/ui/shared/entities/validator/ValidatorEntity.tsx +++ b/client/features/chain-variants/zilliqa/components/ValidatorEntity.tsx @@ -6,8 +6,7 @@ import React from 'react'; import { route } from 'nextjs-routes'; import * as EntityBase from 'ui/shared/entities/base/components'; - -import { distributeEntityProps } from '../base/utils'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; type LinkProps = EntityBase.LinkBaseProps & Pick; diff --git a/mocks/validators/zilliqa.ts b/client/features/chain-variants/zilliqa/mocks/validators.ts similarity index 95% rename from mocks/validators/zilliqa.ts rename to client/features/chain-variants/zilliqa/mocks/validators.ts index 4f7a434a9a..499232950a 100644 --- a/mocks/validators/zilliqa.ts +++ b/client/features/chain-variants/zilliqa/mocks/validators.ts @@ -1,4 +1,4 @@ -import type { ValidatorZilliqa, ValidatorsZilliqaItem, ValidatorsZilliqaResponse } from 'types/api/validators'; +import type { ValidatorZilliqa, ValidatorsZilliqaItem, ValidatorsZilliqaResponse } from 'client/features/chain-variants/zilliqa/types/api'; export const validator1: ValidatorsZilliqaItem = { index: 420, diff --git a/ui/validators/zilliqa/ValidatorDetails.pw.tsx b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.pw.tsx similarity index 84% rename from ui/validators/zilliqa/ValidatorDetails.pw.tsx rename to client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.pw.tsx index 5e3c8c68d5..cf7a8b203f 100644 --- a/ui/validators/zilliqa/ValidatorDetails.pw.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as validatorsMock from 'mocks/validators/zilliqa'; +import * as validatorsMock from 'client/features/chain-variants/zilliqa/mocks/validators'; + import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/validators/zilliqa/ValidatorDetails.tsx b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.tsx similarity index 97% rename from ui/validators/zilliqa/ValidatorDetails.tsx rename to client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.tsx index 3a200627f0..b7e98c9b1a 100644 --- a/ui/validators/zilliqa/ValidatorDetails.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorDetails.tsx @@ -3,7 +3,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorZilliqa } from 'types/api/validators'; +import type { ValidatorZilliqa } from 'client/features/chain-variants/zilliqa/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/ui/pages/ValidatorZilliqa.tsx b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorZilliqa.tsx similarity index 85% rename from ui/pages/ValidatorZilliqa.tsx rename to client/features/chain-variants/zilliqa/pages/validator-details/ValidatorZilliqa.tsx index 8f910003a1..263a5f7ef7 100644 --- a/ui/pages/ValidatorZilliqa.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-details/ValidatorZilliqa.tsx @@ -6,14 +6,16 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import ValidatorEntity from 'client/features/chain-variants/zilliqa/components/ValidatorEntity'; +import { VALIDATOR_ZILLIQA } from 'client/features/chain-variants/zilliqa/stubs/validators'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { VALIDATOR_ZILLIQA } from 'stubs/validators'; import TextAd from 'ui/shared/ad/TextAd'; -import ValidatorEntity from 'ui/shared/entities/validator/ValidatorEntity'; import PageTitle from 'ui/shared/Page/PageTitle'; -import ValidatorDetails from 'ui/validators/zilliqa/ValidatorDetails'; + +import ValidatorDetails from './ValidatorDetails'; const ValidatorZilliqa = () => { const router = useRouter(); diff --git a/ui/validators/zilliqa/__screenshots__/ValidatorDetails.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/zilliqa/pages/validator-details/__screenshots__/ValidatorDetails.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/validators/zilliqa/__screenshots__/ValidatorDetails.pw.tsx_default_base-view-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/validator-details/__screenshots__/ValidatorDetails.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/validators/zilliqa/__screenshots__/ValidatorDetails.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/zilliqa/pages/validator-details/__screenshots__/ValidatorDetails.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/validators/zilliqa/__screenshots__/ValidatorDetails.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/validator-details/__screenshots__/ValidatorDetails.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/validators/zilliqa/ValidatorsList.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsList.tsx similarity index 85% rename from ui/validators/zilliqa/ValidatorsList.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsList.tsx index 37655425b0..d5aff8b719 100644 --- a/ui/validators/zilliqa/ValidatorsList.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsList.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { ValidatorsZilliqaItem } from 'types/api/validators'; +import type { ValidatorsZilliqaItem } from 'client/features/chain-variants/zilliqa/types/api'; import ValidatorsListItem from './ValidatorsListItem'; diff --git a/ui/validators/zilliqa/ValidatorsListItem.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsListItem.tsx similarity index 87% rename from ui/validators/zilliqa/ValidatorsListItem.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsListItem.tsx index f11eff90ad..904c1e6d4f 100644 --- a/ui/validators/zilliqa/ValidatorsListItem.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsListItem.tsx @@ -2,10 +2,11 @@ import React from 'react'; -import type { ValidatorsZilliqaItem } from 'types/api/validators'; +import type { ValidatorsZilliqaItem } from 'client/features/chain-variants/zilliqa/types/api'; + +import ValidatorEntity from 'client/features/chain-variants/zilliqa/components/ValidatorEntity'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import ValidatorEntity from 'ui/shared/entities/validator/ValidatorEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/validators/zilliqa/ValidatorsTable.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTable.tsx similarity index 92% rename from ui/validators/zilliqa/ValidatorsTable.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTable.tsx index 2e6e2649f5..2218f0f1cb 100644 --- a/ui/validators/zilliqa/ValidatorsTable.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { ValidatorsZilliqaItem } from 'types/api/validators'; +import type { ValidatorsZilliqaItem } from 'client/features/chain-variants/zilliqa/types/api'; import config from 'configs/app'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; diff --git a/ui/validators/zilliqa/ValidatorsTableItem.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTableItem.tsx similarity index 83% rename from ui/validators/zilliqa/ValidatorsTableItem.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTableItem.tsx index 800fc9cb70..cb19f28419 100644 --- a/ui/validators/zilliqa/ValidatorsTableItem.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsTableItem.tsx @@ -2,11 +2,12 @@ import React from 'react'; -import type { ValidatorsZilliqaItem } from 'types/api/validators'; +import type { ValidatorsZilliqaItem } from 'client/features/chain-variants/zilliqa/types/api'; + +import ValidatorEntity from 'client/features/chain-variants/zilliqa/components/ValidatorEntity'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import ValidatorEntity from 'ui/shared/entities/validator/ValidatorEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; interface Props { diff --git a/ui/pages/ValidatorsZilliqa.pw.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.pw.tsx similarity index 85% rename from ui/pages/ValidatorsZilliqa.pw.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.pw.tsx index d53d1f89d4..a6f46ef814 100644 --- a/ui/pages/ValidatorsZilliqa.pw.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as validatorsMock from 'mocks/validators/zilliqa'; +import * as validatorsMock from 'client/features/chain-variants/zilliqa/mocks/validators'; + import { test, expect } from 'playwright/lib'; import Validators from './ValidatorsZilliqa'; diff --git a/ui/pages/ValidatorsZilliqa.tsx b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.tsx similarity index 89% rename from ui/pages/ValidatorsZilliqa.tsx rename to client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.tsx index f964d2e497..c8526cc089 100644 --- a/ui/pages/ValidatorsZilliqa.tsx +++ b/client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa.tsx @@ -3,16 +3,18 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import { VALIDATORS_ZILLIQA_ITEM } from 'client/features/chain-variants/zilliqa/stubs/validators'; + import config from 'configs/app'; import { generateListStub } from 'stubs/utils'; -import { VALIDATORS_ZILLIQA_ITEM } from 'stubs/validators'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import ValidatorsList from 'ui/validators/zilliqa/ValidatorsList'; -import ValidatorsTable from 'ui/validators/zilliqa/ValidatorsTable'; + +import ValidatorsList from './ValidatorsList'; +import ValidatorsTable from './ValidatorsTable'; const ValidatorsZilliqa = () => { const { isError, isPlaceholderData, data, pagination } = useQueryWithPages({ diff --git a/ui/pages/__screenshots__/ValidatorsZilliqa.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/zilliqa/pages/validator-index/__screenshots__/ValidatorsZilliqa.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/ValidatorsZilliqa.pw.tsx_default_base-view-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/validator-index/__screenshots__/ValidatorsZilliqa.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/pages/__screenshots__/ValidatorsZilliqa.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/zilliqa/pages/validator-index/__screenshots__/ValidatorsZilliqa.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/ValidatorsZilliqa.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/validator-index/__screenshots__/ValidatorsZilliqa.pw.tsx_mobile_base-view-mobile-1.png diff --git a/client/features/chain-variants/zilliqa/stubs/validators.ts b/client/features/chain-variants/zilliqa/stubs/validators.ts new file mode 100644 index 0000000000..51c7e0d00e --- /dev/null +++ b/client/features/chain-variants/zilliqa/stubs/validators.ts @@ -0,0 +1,24 @@ +import type { + ValidatorsZilliqaItem, + ValidatorZilliqa, +} from 'client/features/chain-variants/zilliqa/types/api'; + +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; + +export const VALIDATORS_ZILLIQA_ITEM: ValidatorsZilliqaItem = { + index: 420, + bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100', + balance: '80000000000000000000000000', +}; + +export const VALIDATOR_ZILLIQA: ValidatorZilliqa = { + index: 420, + bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100', + balance: '1000000000000000000', + added_at_block_number: 1234567890, + control_address: ADDRESS_PARAMS, + peer_id: '1234567890', + reward_address: ADDRESS_PARAMS, + signing_address: ADDRESS_PARAMS, + stake_updated_at_block_number: 1234567890, +}; diff --git a/client/features/chain-variants/zilliqa/types/api.ts b/client/features/chain-variants/zilliqa/types/api.ts index 393e52f8c6..b38ee9d7ec 100644 --- a/client/features/chain-variants/zilliqa/types/api.ts +++ b/client/features/chain-variants/zilliqa/types/api.ts @@ -1,5 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { AddressParam } from 'client/slices/address/types/api'; + export interface AddressZilliqaParams { is_scilla_contract: boolean; } @@ -31,3 +33,26 @@ export interface ZilliqaBlockData { export interface BlockZilliqa { zilliqa?: ZilliqaBlockData; } + +export interface ValidatorsZilliqaItem { + index: number; + bls_public_key: string; + balance: string; +} + +export interface ValidatorsZilliqaResponse { + items: Array; + next_page_params: null; +} + +export interface ValidatorZilliqa { + added_at_block_number: number; + balance: string; + bls_public_key: string; + control_address: AddressParam; + index: number; + peer_id: string; + reward_address: AddressParam; + signing_address: AddressParam; + stake_updated_at_block_number: number; +} diff --git a/configs/app/features/validators.ts b/configs/app/features/validators.ts index b2239185eb..d76ba2dae1 100644 --- a/configs/app/features/validators.ts +++ b/configs/app/features/validators.ts @@ -1,11 +1,18 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import { VALIDATORS_CHAIN_TYPE } from 'types/client/validators'; -import type { ValidatorsChainType } from 'types/client/validators'; +import type { ArrayElement } from 'types/utils'; import { getEnvValue } from '../utils'; +export const VALIDATORS_CHAIN_TYPE = [ + 'stability', + 'blackfort', + 'zilliqa', +] as const; + +export type ValidatorsChainType = ArrayElement; + const chainType = ((): ValidatorsChainType | undefined => { const envValue = getEnvValue('NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE') as ValidatorsChainType | undefined; return envValue && VALIDATORS_CHAIN_TYPE.includes(envValue) ? envValue : undefined; diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 2674b9a63e..0075e4e29f 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -20,8 +20,8 @@ import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConf import { GAS_UNITS } from 'client/slices/gas/types/config'; import type { GasUnit } from 'client/slices/gas/types/config'; import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from 'client/features/tx-interpretation/common/types/config'; -import { VALIDATORS_CHAIN_TYPE } from 'types/client/validators'; -import type { ValidatorsChainType } from 'types/client/validators'; +import { VALIDATORS_CHAIN_TYPE } from 'configs/app/features/validators'; +import type { ValidatorsChainType } from 'configs/app/features/validators'; import type { WalletType } from 'types/client/wallets'; import { SUPPORTED_WALLETS } from 'types/client/wallets'; import type { TxExternalTxsConfig } from 'types/client/externalTxsConfig'; diff --git a/pages/validators/[id].tsx b/pages/validators/[id].tsx index 136ca041ed..e8df30143f 100644 --- a/pages/validators/[id].tsx +++ b/pages/validators/[id].tsx @@ -13,7 +13,7 @@ const validatorsFeature = config.features.validators; const ValidatorDetails = dynamic(() => { if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') { - return import('ui/pages/ValidatorZilliqa'); + return import('client/features/chain-variants/zilliqa/pages/validator-details/ValidatorZilliqa'); } throw new Error('Validators feature is not enabled.'); diff --git a/pages/validators/index.tsx b/pages/validators/index.tsx index 1e8323848b..d271e92baa 100644 --- a/pages/validators/index.tsx +++ b/pages/validators/index.tsx @@ -12,15 +12,15 @@ const validatorsFeature = config.features.validators; const Validators = dynamic(() => { if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability') { - return import('ui/pages/ValidatorsStability'); + return import('client/features/chain-variants/stability/pages/validator-index/ValidatorsStability'); } if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'blackfort') { - return import('ui/pages/ValidatorsBlackfort'); + return import('client/features/chain-variants/blackfort/pages/validator-index/ValidatorsBlackfort'); } if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') { - return import('ui/pages/ValidatorsZilliqa'); + return import('client/features/chain-variants/zilliqa/pages/validator-index/ValidatorsZilliqa'); } throw new Error('Validators feature is not enabled.'); diff --git a/stubs/validators.ts b/stubs/validators.ts deleted file mode 100644 index 0720177434..0000000000 --- a/stubs/validators.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { - ValidatorStability, - ValidatorsStabilityCountersResponse, - ValidatorBlackfort, - ValidatorsBlackfortCountersResponse, - ValidatorsZilliqaItem, - ValidatorZilliqa, -} from 'types/api/validators'; - -import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; - -export const VALIDATOR_STABILITY: ValidatorStability = { - address: ADDRESS_PARAMS, - blocks_validated_count: 25987, - state: 'active', -}; - -export const VALIDATORS_STABILITY_COUNTERS: ValidatorsStabilityCountersResponse = { - active_validators_count: '42', - active_validators_percentage: 7.14, - new_validators_count_24h: '11', - validators_count: '140', -}; - -export const VALIDATOR_BLACKFORT: ValidatorBlackfort = { - address: ADDRESS_PARAMS, - name: 'testnet-1', - commission: 10, - delegated_amount: '0', - self_bonded_amount: '10000', -}; - -export const VALIDATORS_BLACKFORT_COUNTERS: ValidatorsBlackfortCountersResponse = { - new_validators_count_24h: '11', - validators_count: '140', -}; - -export const VALIDATORS_ZILLIQA_ITEM: ValidatorsZilliqaItem = { - index: 420, - bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100', - balance: '80000000000000000000000000', -}; - -export const VALIDATOR_ZILLIQA: ValidatorZilliqa = { - index: 420, - bls_public_key: '0x95125dca41be848801f9bd75254f1faf1ae3194b1da53e9a5684ed7f67b729542482bc521924603b9703c33bf831a100', - balance: '1000000000000000000', - added_at_block_number: 1234567890, - control_address: ADDRESS_PARAMS, - peer_id: '1234567890', - reward_address: ADDRESS_PARAMS, - signing_address: ADDRESS_PARAMS, - stake_updated_at_block_number: 1234567890, -}; diff --git a/types/api/validators.ts b/types/api/validators.ts deleted file mode 100644 index 1f06d75eb0..0000000000 --- a/types/api/validators.ts +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { AddressParam } from 'client/slices/address/types/api'; - -// Stability - -export interface ValidatorStability { - address: AddressParam; - blocks_validated_count: number; - state: 'active' | 'probation' | 'inactive'; -} - -export interface ValidatorsStabilityResponse { - items: Array; - next_page_params: { - address_hash: string; - blocks_validated: string; - items_count: string; - state: ValidatorStability['state']; - } | null; -} - -export interface ValidatorsStabilityCountersResponse { - active_validators_count: string; - active_validators_percentage: number; - new_validators_count_24h: string; - validators_count: string; -} - -export interface ValidatorsStabilityFilters { - // address_hash: string | undefined; // right now API doesn't support filtering by address_hash - state_filter: ValidatorStability['state'] | undefined; -} - -export interface ValidatorsStabilitySorting { - sort: 'state' | 'blocks_validated'; - order: 'asc' | 'desc'; -} - -export type ValidatorsStabilitySortingField = ValidatorsStabilitySorting['sort']; - -export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }` | 'default'; - -// Blackfort - -export interface ValidatorBlackfort { - address: AddressParam; - name: string; - commission: number; - delegated_amount: string; - self_bonded_amount: string; -} - -export interface ValidatorsBlackfortResponse { - items: Array; - next_page_params: { - address_hash: string; - } | null; -} - -export interface ValidatorsBlackfortCountersResponse { - new_validators_count_24h: string; - validators_count: string; -} - -export interface ValidatorsBlackfortSorting { - sort: 'address_hash'; - order: 'asc' | 'desc'; -} - -export type ValidatorsBlackfortSortingField = ValidatorsBlackfortSorting['sort']; - -export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }` | 'default'; - -// Zilliqa -export interface ValidatorsZilliqaItem { - index: number; - bls_public_key: string; - balance: string; -} - -export interface ValidatorsZilliqaResponse { - items: Array; - next_page_params: null; -} - -export interface ValidatorZilliqa { - added_at_block_number: number; - balance: string; - bls_public_key: string; - control_address: AddressParam; - index: number; - peer_id: string; - reward_address: AddressParam; - signing_address: AddressParam; - stake_updated_at_block_number: number; -} diff --git a/types/client/validators.ts b/types/client/validators.ts deleted file mode 100644 index eb1a34a4b0..0000000000 --- a/types/client/validators.ts +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { ArrayElement } from 'types/utils'; - -export const VALIDATORS_CHAIN_TYPE = [ - 'stability', - 'blackfort', - 'zilliqa', -] as const; - -export type ValidatorsChainType = ArrayElement; diff --git a/ui/pages/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/ValidatorsBlackfort.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 47be4f812c80c97b4c44a4f4677dc36ac089adda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31967 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 diff --git a/ui/pages/__screenshots__/ValidatorsStability.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/ValidatorsStability.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index a71b8c29d2b5f5b26579586438ac3d5ba48aa4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33982 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

m 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 From c2d8327c2d541be8d8e03dd261728a86d6f62147 Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 12:35:28 +0200 Subject: [PATCH 19/85] =?UTF-8?q?track:=206-3=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3479?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 9aa0cbbcbb..ee492ba9c5 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -209,7 +209,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-2 · [x] Feature: `data-availability` · [#3453](https://github.com/blockscout/frontend/issues/3453) -### 6-3 · [ ] Feature: `multichain` +### 6-3 · [~] Features: `multichain` and `multichain-button` · [#3479](https://github.com/blockscout/frontend/issues/3479) **Scope:** Includes `lib/multichain/`, `lib/contexts/multichain.tsx`, `ui/snippets/networkMenu/` → `client/features/multichain/` ### 6-4 · [x] Feature: `name-services` (domains and clusters) · [#3458](https://github.com/blockscout/frontend/issues/3458) From 51f0675a849b507ea06ff28abab7efe271e27e9a Mon Sep 17 00:00:00 2001 From: tom goriunov Date: Thu, 21 May 2026 12:36:18 +0200 Subject: [PATCH 20/85] API page: PRO API tab (#3476) * API page: PRO API Tab Resolves #3408 * add script to detect pro api instance * add utm and fix icon color * let user redefine the pro api variable * [skip ci] review fixes --- Dockerfile | 2 + .../api-docs/components}/SwaggerUI.tsx | 2 +- .../api-docs/pages/index}/ApiDocs.tsx | 11 +- .../api-docs/pages/index}/EthRpcApi.tsx | 0 .../api-docs/pages/index}/GraphQL.tsx | 0 .../features/api-docs/pages/index/ProApi.tsx | 33 ++++ .../api-docs/pages/index}/RestApi.tsx | 4 +- .../features/api-docs/pages/index}/RpcApi.tsx | 0 .../features/api-docs/types/client.ts | 0 .../features/api-docs/types/config.ts | 1 + .../features/api-docs/utils/rest-api.spec.ts | 2 +- .../features/api-docs/utils/rest-api.ts | 2 +- configs/app/features/api-docs.ts | 11 +- deploy/scripts/entrypoint.sh | 3 + deploy/scripts/export_pro_api_flag.sh | 52 ++++++ deploy/scripts/make_envs_script.sh | 1 + deploy/tools/envs-validator/schema.ts | 1 + .../schemas/features/apiDocs.ts | 4 +- .../schemas/features/nameServices.ts | 2 - deploy/tools/envs-validator/test/.env.base | 1 + docs/ENVS.md | 3 +- pages/api-docs.tsx | 2 +- public/static/api-docs/pro_api_dark.svg | 149 ++++++++++++++++++ public/static/api-docs/pro_api_light.svg | 147 +++++++++++++++++ tools/scripts/dev.preset.sh | 2 +- 25 files changed, 416 insertions(+), 19 deletions(-) rename {ui/apiDocs => client/features/api-docs/components}/SwaggerUI.tsx (98%) rename {ui/pages => client/features/api-docs/pages/index}/ApiDocs.tsx (85%) rename {ui/apiDocs => client/features/api-docs/pages/index}/EthRpcApi.tsx (100%) rename {ui/apiDocs => client/features/api-docs/pages/index}/GraphQL.tsx (100%) create mode 100644 client/features/api-docs/pages/index/ProApi.tsx rename {ui/apiDocs => client/features/api-docs/pages/index}/RestApi.tsx (96%) rename {ui/apiDocs => client/features/api-docs/pages/index}/RpcApi.tsx (100%) rename ui/apiDocs/types.ts => client/features/api-docs/types/client.ts (100%) rename types/views/apiDocs.ts => client/features/api-docs/types/config.ts (94%) rename ui/apiDocs/utils.spec.ts => client/features/api-docs/utils/rest-api.spec.ts (98%) rename ui/apiDocs/utils.ts => client/features/api-docs/utils/rest-api.ts (98%) create mode 100755 deploy/scripts/export_pro_api_flag.sh create mode 100644 public/static/api-docs/pro_api_dark.svg create mode 100644 public/static/api-docs/pro_api_light.svg diff --git a/Dockerfile b/Dockerfile index c5ab384436..2cff64bde7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -119,6 +119,8 @@ COPY --chmod=755 ./deploy/scripts/make_envs_script.sh . COPY --chmod=755 ./deploy/scripts/download_assets.sh . ## OG image generator COPY ./deploy/scripts/og_image_generator.js . +## Pro API support flag +COPY --chmod=755 ./deploy/scripts/export_pro_api_flag.sh . ## Favicon generator COPY --chmod=755 ./deploy/scripts/favicon_generator.sh . COPY --from=builder /app/favicon-generator-bundle ./deploy/tools/favicon-generator diff --git a/ui/apiDocs/SwaggerUI.tsx b/client/features/api-docs/components/SwaggerUI.tsx similarity index 98% rename from ui/apiDocs/SwaggerUI.tsx rename to client/features/api-docs/components/SwaggerUI.tsx index 3ec0935d8e..a2b3afa159 100644 --- a/ui/apiDocs/SwaggerUI.tsx +++ b/client/features/api-docs/components/SwaggerUI.tsx @@ -11,7 +11,7 @@ import { Box, useToken } from '@chakra-ui/react'; import dynamic from 'next/dynamic'; import React from 'react'; -import type { SwaggerRequest } from './types'; +import type { SwaggerRequest } from '../types/client'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; diff --git a/ui/pages/ApiDocs.tsx b/client/features/api-docs/pages/index/ApiDocs.tsx similarity index 85% rename from ui/pages/ApiDocs.tsx rename to client/features/api-docs/pages/index/ApiDocs.tsx index 2a59cd20d6..aabe888517 100644 --- a/ui/pages/ApiDocs.tsx +++ b/client/features/api-docs/pages/index/ApiDocs.tsx @@ -7,18 +7,21 @@ import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import config from 'configs/app'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import EthRpcApi from 'ui/apiDocs/EthRpcApi'; -import GraphQL from 'ui/apiDocs/GraphQL'; -import RestApi from 'ui/apiDocs/RestApi'; -import RpcApi from 'ui/apiDocs/RpcApi'; import AlertWithExternalHtml from 'ui/shared/alerts/AlertWithExternalHtml'; import PageTitle from 'ui/shared/Page/PageTitle'; +import EthRpcApi from './EthRpcApi'; +import GraphQL from './GraphQL'; +import ProApi from './ProApi'; +import RestApi from './RestApi'; +import RpcApi from './RpcApi'; + const feature = config.features.apiDocs; const ApiDocs = () => { const tabs: Array = [ + { id: 'pro_api', title: 'Pro API', component: }, { id: 'rest_api', title: 'REST API', component: }, { id: 'eth_rpc_api', title: 'ETH RPC API', component: }, { id: 'rpc_api', title: 'RPC API endpoints', component: }, diff --git a/ui/apiDocs/EthRpcApi.tsx b/client/features/api-docs/pages/index/EthRpcApi.tsx similarity index 100% rename from ui/apiDocs/EthRpcApi.tsx rename to client/features/api-docs/pages/index/EthRpcApi.tsx diff --git a/ui/apiDocs/GraphQL.tsx b/client/features/api-docs/pages/index/GraphQL.tsx similarity index 100% rename from ui/apiDocs/GraphQL.tsx rename to client/features/api-docs/pages/index/GraphQL.tsx diff --git a/client/features/api-docs/pages/index/ProApi.tsx b/client/features/api-docs/pages/index/ProApi.tsx new file mode 100644 index 0000000000..fb541d5b1f --- /dev/null +++ b/client/features/api-docs/pages/index/ProApi.tsx @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import { Text, Center } from '@chakra-ui/react'; +import React from 'react'; + +import { Button } from 'toolkit/chakra/button'; +import { useColorModeValue } from 'toolkit/chakra/color-mode'; +import { Heading } from 'toolkit/chakra/heading'; +import { Image } from 'toolkit/chakra/image'; +import { Link } from 'toolkit/chakra/link'; + +const ProApi = () => { + + const imageSrc = useColorModeValue('/static/api-docs/pro_api_light.svg', '/static/api-docs/pro_api_dark.svg'); + + return ( +

+ Pro API + More chain coverage
lower API Costs
+ + Blockscout's Multichain API delivers unified access to 120+ EVM chains with a single key.
+ Get rich decoded data, scalability and speed at a fraction of the price. +
+ +
+ ); +}; + +export default React.memo(ProApi); diff --git a/ui/apiDocs/RestApi.tsx b/client/features/api-docs/pages/index/RestApi.tsx similarity index 96% rename from ui/apiDocs/RestApi.tsx rename to client/features/api-docs/pages/index/RestApi.tsx index 985b9ed68d..98329c8026 100644 --- a/ui/apiDocs/RestApi.tsx +++ b/client/features/api-docs/pages/index/RestApi.tsx @@ -11,8 +11,8 @@ import { AccordionItem, AccordionItemContent, AccordionItemTrigger, AccordionRoo import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import SwaggerUI from './SwaggerUI'; -import { REST_API_SECTIONS } from './utils'; +import SwaggerUI from '../../components/SwaggerUI'; +import { REST_API_SECTIONS } from '../../utils/rest-api'; const RestApi = () => { diff --git a/ui/apiDocs/RpcApi.tsx b/client/features/api-docs/pages/index/RpcApi.tsx similarity index 100% rename from ui/apiDocs/RpcApi.tsx rename to client/features/api-docs/pages/index/RpcApi.tsx diff --git a/ui/apiDocs/types.ts b/client/features/api-docs/types/client.ts similarity index 100% rename from ui/apiDocs/types.ts rename to client/features/api-docs/types/client.ts diff --git a/types/views/apiDocs.ts b/client/features/api-docs/types/config.ts similarity index 94% rename from types/views/apiDocs.ts rename to client/features/api-docs/types/config.ts index d508b9d949..33aa2269d8 100644 --- a/types/views/apiDocs.ts +++ b/client/features/api-docs/types/config.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout export const API_DOCS_TABS = [ + 'pro_api', 'rest_api', 'eth_rpc_api', 'rpc_api', diff --git a/ui/apiDocs/utils.spec.ts b/client/features/api-docs/utils/rest-api.spec.ts similarity index 98% rename from ui/apiDocs/utils.spec.ts rename to client/features/api-docs/utils/rest-api.spec.ts index 0c1a846dd3..a2de9f184e 100644 --- a/ui/apiDocs/utils.spec.ts +++ b/client/features/api-docs/utils/rest-api.spec.ts @@ -1,7 +1,7 @@ import type { ApiPropsFull } from 'configs/app/apis'; import { describe, test, expect } from 'vitest'; -import { coreApiRequestInterceptorFactory } from './utils'; +import { coreApiRequestInterceptorFactory } from './rest-api'; const api: ApiPropsFull = { endpoint: 'https://example.com', diff --git a/ui/apiDocs/utils.ts b/client/features/api-docs/utils/rest-api.ts similarity index 98% rename from ui/apiDocs/utils.ts rename to client/features/api-docs/utils/rest-api.ts index 4e071e7456..23db4e497d 100644 --- a/ui/apiDocs/utils.ts +++ b/client/features/api-docs/utils/rest-api.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { SwaggerRequest } from './types'; +import type { SwaggerRequest } from '../types/client'; import config from 'configs/app'; import type { ApiPropsBase, ApiPropsFull } from 'configs/app/apis'; diff --git a/configs/app/features/api-docs.ts b/configs/app/features/api-docs.ts index 2af7c3d01c..d334800e11 100644 --- a/configs/app/features/api-docs.ts +++ b/configs/app/features/api-docs.ts @@ -1,13 +1,18 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { ApiDocsTabId } from 'types/views/apiDocs'; -import { API_DOCS_TABS } from 'types/views/apiDocs'; +import type { ApiDocsTabId } from 'client/features/api-docs/types/config'; +import { API_DOCS_TABS } from 'client/features/api-docs/types/config'; import { getEnvValue, parseEnvJson } from '../utils'; const tabs = (() => { - const value = (parseEnvJson>(getEnvValue('NEXT_PUBLIC_API_DOCS_TABS')) || API_DOCS_TABS) + const DEFAULT_TABS = getEnvValue('NEXT_PUBLIC_PRO_API_SUPPORTED') === 'true' ? + API_DOCS_TABS : + API_DOCS_TABS.filter((tab) => tab !== 'pro_api'); + const value = ( + parseEnvJson>(getEnvValue('NEXT_PUBLIC_API_DOCS_TABS')) || DEFAULT_TABS + ) .filter((tab) => API_DOCS_TABS.includes(tab)); return value.length > 0 ? value : undefined; diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh index d30c68eb1a..d8f962cb70 100755 --- a/deploy/scripts/entrypoint.sh +++ b/deploy/scripts/entrypoint.sh @@ -65,6 +65,9 @@ echo # Generate OG image node --no-warnings ./og_image_generator.js +# Expose Pro API support flag for the current chain (if NEXT_PUBLIC_NETWORK_ID is set) +source ./export_pro_api_flag.sh + # Create envs.js file with run-time environment variables for the client app ./make_envs_script.sh diff --git a/deploy/scripts/export_pro_api_flag.sh b/deploy/scripts/export_pro_api_flag.sh new file mode 100755 index 0000000000..91f15efaf8 --- /dev/null +++ b/deploy/scripts/export_pro_api_flag.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Must be sourced so export applies to the caller shell (see entrypoint.sh, dev.preset.sh). + +SUPPORTED_CHAINS_URL='https://api.blockscout.com/api/json/config' + +check_pro_api_supported() { + local network_id="${NEXT_PUBLIC_NETWORK_ID// /}" + + if [ -z "$network_id" ]; then + echo "⚠️ NEXT_PUBLIC_NETWORK_ID is not set. Skipping Pro API support check." >&2 + return 1 + fi + + if ! command -v jq >/dev/null 2>&1; then + echo "⚠️ jq not found. Skipping Pro API support check." >&2 + return 1 + fi + + if ! command -v curl >/dev/null 2>&1; then + echo "⚠️ curl not found. Skipping Pro API support check." >&2 + return 1 + fi + + echo "⏳ Checking Pro API support for chain ${network_id}..." >&2 + + local response + if ! response=$(curl -f -sS --connect-timeout 10 --max-time 30 "$SUPPORTED_CHAINS_URL"); then + echo "⚠️ Failed to fetch Pro API config." >&2 + return 1 + fi + + if echo "$response" | jq -e --arg id "$network_id" '.chains[$id]' >/dev/null; then + echo "✅ Chain ${network_id} is supported by Blockscout PRO API." >&2 + return 0 + fi + + echo "ℹ️ Chain ${network_id} is not supported by Blockscout PRO API." >&2 + return 1 +} + +if [ "${BASH_SOURCE[0]}" = "${0}" ]; then + echo "This script must be sourced. Use: source ./export_pro_api_flag.sh" >&2 + exit 1 +fi + +if [ -n "${NEXT_PUBLIC_PRO_API_SUPPORTED}" ]; then + echo "ℹ️ NEXT_PUBLIC_PRO_API_SUPPORTED is already set to '${NEXT_PUBLIC_PRO_API_SUPPORTED}'. Skipping auto-detection." >&2 +elif check_pro_api_supported; then + export NEXT_PUBLIC_PRO_API_SUPPORTED=true +fi + +echo "" diff --git a/deploy/scripts/make_envs_script.sh b/deploy/scripts/make_envs_script.sh index b3548cb207..cda13bdc0e 100755 --- a/deploy/scripts/make_envs_script.sh +++ b/deploy/scripts/make_envs_script.sh @@ -37,3 +37,4 @@ done echo "}" >> $output_file; echo "✅ Done." +echo "" \ No newline at end of file diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 0075e4e29f..2cb8f7658b 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -139,6 +139,7 @@ const schema = yup NEXT_PUBLIC_HOT_CONTRACTS_ENABLED: yup.boolean(), // Misc + NEXT_PUBLIC_PRO_API_SUPPORTED: yup.boolean(), NEXT_PUBLIC_USE_NEXT_JS_PROXY: yup.boolean(), NEXT_PUBLIC_API_KEYS_ALERT_MESSAGE: yup.string(), NEXT_PUBLIC_API_DOCS_ALERT_MESSAGE: yup.string(), diff --git a/deploy/tools/envs-validator/schemas/features/apiDocs.ts b/deploy/tools/envs-validator/schemas/features/apiDocs.ts index c9649f28a4..eb82f71dce 100644 --- a/deploy/tools/envs-validator/schemas/features/apiDocs.ts +++ b/deploy/tools/envs-validator/schemas/features/apiDocs.ts @@ -1,7 +1,7 @@ import * as yup from 'yup'; import { replaceQuotes } from 'configs/app/utils'; -import type { ApiDocsTabId } from 'types/views/apiDocs'; -import { API_DOCS_TABS } from 'types/views/apiDocs'; +import type { ApiDocsTabId } from 'client/features/api-docs/types/config'; +import { API_DOCS_TABS } from 'client/features/api-docs/types/config'; export const apiDocsSchema = yup .object() diff --git a/deploy/tools/envs-validator/schemas/features/nameServices.ts b/deploy/tools/envs-validator/schemas/features/nameServices.ts index c63f0bf1d9..d63495a035 100644 --- a/deploy/tools/envs-validator/schemas/features/nameServices.ts +++ b/deploy/tools/envs-validator/schemas/features/nameServices.ts @@ -1,8 +1,6 @@ import { urlTest } from "../../utils"; import * as yup from 'yup'; import { replaceQuotes } from 'configs/app/utils'; -import type { ApiDocsTabId } from 'types/views/apiDocs'; -import { API_DOCS_TABS } from 'types/views/apiDocs'; export const nameServicesSchema = yup .object() diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index b1c103ee95..cf6a431bca 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -70,6 +70,7 @@ NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global NEXT_PUBLIC_STATS_API_HOST=https://example.com NEXT_PUBLIC_STATS_API_BASE_PATH=/ NEXT_PUBLIC_STATS_API_REFETCH_INTERVAL={'stats:counters': 10000, 'stats:pages_main': 10000} +NEXT_PUBLIC_PRO_API_SUPPORTED=true NEXT_PUBLIC_USE_NEXT_JS_PROXY=false NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16'] diff --git a/docs/ENVS.md b/docs/ENVS.md index e7a9fd5b7b..a75c3ead7d 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -399,6 +399,7 @@ Settings for meta tags, OG tags and SEO | NEXT_PUBLIC_FONT_FAMILY_HEADING | `FontFamily`, see full description [below](#font-family-configuration-properties) | Special typeface to use in page headings (`

`, `

`, etc.) | - | - | `{'name':'Montserrat','url':'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap'}` | v1.35.0+ | | NEXT_PUBLIC_FONT_FAMILY_BODY | `FontFamily`, see full description [below](#font-family-configuration-properties) | Main typeface to use in page content elements. | - | - | `{'name':'Raleway','url':'https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap'}` | v1.35.0+ | | NEXT_PUBLIC_MAX_CONTENT_WIDTH_ENABLED | `boolean` | Set to `true` to restrict the page content width on extra-large screens. | - | `true` | `false` | v1.34.1+ | +| NEXT_PUBLIC_PRO_API_SUPPORTED | `boolean` | Indicates whether the current chain is supported by Blockscout Pro API. When set to `true`, the `pro_api` tab is included in the API documentation by default. **This variable is automatically set during container startup**; manual configuration is typically not needed. | - | - | `true` | | #### Network explorer configuration properties @@ -610,7 +611,7 @@ Ads are enabled by default on all self-hosted instances. If you would like to di | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_API_DOCS_TABS | `Array` | Controls which tabs appear on the API documentation page. Possible values for `TabId` are `rest_api`, `eth_rpc_api`, `rpc_api`, and `graphql_api`. **Note** that this variable has a default value, so the feature is enabled by default. Pass an empty array to disable it. | - | `['rest_api','eth_rpc_api','rpc_api','graphql_api']` | `[]` | v2.3.x+ | +| NEXT_PUBLIC_API_DOCS_TABS | `Array` | Controls which tabs appear on the API documentation page. Possible values for `TabId` are `pro_api`, `rest_api`, `eth_rpc_api`, `rpc_api`, and `graphql_api`. **Note** that this variable has a default value, so the feature is enabled by default. Pass an empty array to disable it. The `pro_api` tab will be automatically added to the default value for chains supported in the Pro API. | - | `['rest_api','eth_rpc_api','rpc_api','graphql_api']` | `[]` | v2.3.x+ | | NEXT_PUBLIC_API_DOCS_ALERT_MESSAGE | `string` | Used for displaying custom alerts on the API documentation page. Could be a regular string or a HTML code. | - | - | `Hello world! 🤪` | v2.7.0+ |   diff --git a/pages/api-docs.tsx b/pages/api-docs.tsx index 33806c28c3..82998acefb 100644 --- a/pages/api-docs.tsx +++ b/pages/api-docs.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const ApiDocs = dynamic(() => import('ui/pages/ApiDocs'), { ssr: false }); +const ApiDocs = dynamic(() => import('client/features/api-docs/pages/index/ApiDocs'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/public/static/api-docs/pro_api_dark.svg b/public/static/api-docs/pro_api_dark.svg new file mode 100644 index 0000000000..16aa9f6dfa --- /dev/null +++ b/public/static/api-docs/pro_api_dark.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/api-docs/pro_api_light.svg b/public/static/api-docs/pro_api_light.svg new file mode 100644 index 0000000000..460e4eb039 --- /dev/null +++ b/public/static/api-docs/pro_api_light.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/scripts/dev.preset.sh b/tools/scripts/dev.preset.sh index e30af75546..9ed7c57b37 100755 --- a/tools/scripts/dev.preset.sh +++ b/tools/scripts/dev.preset.sh @@ -51,5 +51,5 @@ dotenv \ -v NEXT_PUBLIC_ICON_SPRITE_HASH="${NEXT_PUBLIC_ICON_SPRITE_HASH}" \ -e $config_file \ -e $secrets_file \ - -- bash -c './deploy/scripts/make_envs_script.sh && next dev -p $NEXT_PUBLIC_APP_PORT' | + -- bash -c 'source ./deploy/scripts/export_pro_api_flag.sh && ./deploy/scripts/make_envs_script.sh && next dev -p $NEXT_PUBLIC_APP_PORT' | pino-pretty From eadc1afef620cf7c53b2c4873eb4f060246d828a Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 13:06:30 +0200 Subject: [PATCH 21/85] =?UTF-8?q?track:=206-27=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3480?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- client/MIGRATION_TASKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index ee492ba9c5..21d1d63190 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -273,7 +273,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-26 · [ ] Feature: `bridged-tokens` -### 6-27 · [ ] Feature: `web3-wallet` +### 6-27 · [~] Features: `web3-wallet` and `connect-wallet` · [#3480](https://github.com/blockscout/frontend/issues/3480) **Scope:** Includes `blockchainInteraction.ts` config. ### 6-28 · [ ] Feature: `alternative-explorers` From 8cb639415636cd22b746950e52a477679e4445fb Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 14:49:15 +0200 Subject: [PATCH 22/85] =?UTF-8?q?[Migration=206-22]=20Feature=20`address-m?= =?UTF-8?q?etadata`=20=E2=80=94=20merge=20`public-tags-submission`=20(#347?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(migration-6-22): migrate address-metadata, merge public-tags-submission Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/e0d3eadb-c3ea-4c8d-9f77-cde244a30543 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> --- .../hooks/useAddressMetadataInfoQuery.ts | 4 +-- .../hooks/useAddressMetadataInitUpdate.ts | 2 +- .../pages/tag-search/TagSearch.pw.tsx | 6 ++-- .../pages/tag-search/TagSearch.tsx | 13 +++++---- .../pages/tag-search/TagSearchListItem.tsx | 4 +-- .../pages/tag-search/TagSearchTable.tsx | 8 +++--- .../pages/tag-search/TagSearchTableItem.tsx | 4 +-- ...arch.pw.tsx_default_base-view-mobile-1.png | Bin ...earch.pw.tsx_mobile_base-view-mobile-1.png | Bin .../pages/tag-submition/TagSubmition.tsx | 15 +++++----- .../tag-submition/TagSubmitionForm.pw.tsx | 4 +-- .../pages/tag-submition/TagSubmitionForm.tsx | 12 ++++---- .../tag-submition/TagSubmitionResult.pw.tsx | 8 +++--- .../tag-submition/TagSubmitionResult.tsx | 10 +++---- ...Form.pw.tsx_default_base-view-mobile-1.png | Bin ...nForm.pw.tsx_mobile_base-view-mobile-1.png | Bin ...fault_all-success-result-view-mobile-1.png | Bin ...fault_mobile-result-with-errors-view-1.png | Bin ....tsx_default_result-with-errors-view-1.png | Bin ...obile_all-success-result-view-mobile-1.png | Bin .../fields/TagSubmitionFieldAddresses.tsx | 4 +-- .../fields/TagSubmitionFieldTag.tsx | 12 ++++---- .../fields/TagSubmitionFieldTagIcon.tsx | 10 +++---- .../TagSubmitionFieldTagIconPreview.tsx | 4 +-- .../fields/TagSubmitionFieldTagType.tsx | 4 +-- .../fields/TagSubmitionFieldTags.tsx | 8 +++--- .../pages/tag-submition}/mocks.ts | 0 .../result/TagSubmitionResultSuccess.tsx | 4 +-- .../result/TagSubmitionResultWithErrors.tsx | 4 +-- .../pages/tag-submition}/types.ts | 0 .../pages/tag-submition}/utils.spec.ts | 0 .../pages/tag-submition}/utils.ts | 0 .../types/{view.ts => client.ts} | 0 ...seMetaPayload.ts => parse-meta-payload.ts} | 2 +- .../pages/details/info/AddressAlerts.tsx | 2 +- .../details/info/TokenNftMarketplaces.tsx | 2 +- configs/app/features/address-metadata.ts | 8 ++++-- configs/app/features/index.ts | 1 - .../app/features/public-tags-submission.ts | 26 ------------------ lib/hooks/useNavItems.tsx | 3 +- nextjs/getServerSideProps/guards.ts | 3 +- pages/accounts/label/[slug].tsx | 2 +- .../accounts/label/[slug].tsx | 2 +- pages/public-tags/submit.tsx | 4 +-- .../AccountActionsMenu/AccountActionsMenu.tsx | 3 +- .../AppActionButton/AppActionButton.pw.tsx | 2 +- ui/shared/AppActionButton/AppActionButton.tsx | 2 +- ui/shared/EntityTags/types.ts | 2 +- 48 files changed, 94 insertions(+), 110 deletions(-) rename ui/pages/AccountsLabelSearch.pw.tsx => client/features/address-metadata/pages/tag-search/TagSearch.pw.tsx (89%) rename ui/pages/AccountsLabelSearch.tsx => client/features/address-metadata/pages/tag-search/TagSearch.tsx (90%) rename ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx => client/features/address-metadata/pages/tag-search/TagSearchListItem.tsx (94%) rename ui/addressesLabelSearch/AddressesLabelSearchTable.tsx => client/features/address-metadata/pages/tag-search/TagSearchTable.tsx (81%) rename ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx => client/features/address-metadata/pages/tag-search/TagSearchTableItem.tsx (92%) rename ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_default_base-view-mobile-1.png => client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_default_base-view-mobile-1.png (100%) rename ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_mobile_base-view-mobile-1.png => client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_mobile_base-view-mobile-1.png (100%) rename ui/pages/PublicTagsSubmit.tsx => client/features/address-metadata/pages/tag-submition/TagSubmition.tsx (74%) rename ui/publicTags/submit/PublicTagsSubmitForm.pw.tsx => client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx (86%) rename ui/publicTags/submit/PublicTagsSubmitForm.tsx => client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx (93%) rename ui/publicTags/submit/PublicTagsSubmitResult.pw.tsx => client/features/address-metadata/pages/tag-submition/TagSubmitionResult.pw.tsx (61%) rename ui/publicTags/submit/PublicTagsSubmitResult.tsx => client/features/address-metadata/pages/tag-submition/TagSubmitionResult.tsx (87%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_default_base-view-mobile-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_default_base-view-mobile-1.png (100%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_mobile_base-view-mobile-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_mobile_base-view-mobile-1.png (100%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_all-success-result-view-mobile-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_all-success-result-view-mobile-1.png (100%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_mobile-result-with-errors-view-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_mobile-result-with-errors-view-1.png (100%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_result-with-errors-view-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_result-with-errors-view-1.png (100%) rename ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_mobile_all-success-result-view-mobile-1.png => client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_mobile_all-success-result-view-mobile-1.png (100%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldAddresses.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldAddresses.tsx (95%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldTag.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTag.tsx (91%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIcon.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIcon.tsx (73%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIconPreview.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIconPreview.tsx (78%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldTagType.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagType.tsx (88%) rename ui/publicTags/submit/fields/PublicTagsSubmitFieldTags.tsx => client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTags.tsx (87%) rename {ui/publicTags/submit => client/features/address-metadata/pages/tag-submition}/mocks.ts (100%) rename ui/publicTags/submit/result/PublicTagsSubmitResultSuccess.tsx => client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultSuccess.tsx (93%) rename ui/publicTags/submit/result/PublicTagsSubmitResultWithErrors.tsx => client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultWithErrors.tsx (96%) rename {ui/publicTags/submit => client/features/address-metadata/pages/tag-submition}/types.ts (100%) rename {ui/publicTags/submit => client/features/address-metadata/pages/tag-submition}/utils.spec.ts (100%) rename {ui/publicTags/submit => client/features/address-metadata/pages/tag-submition}/utils.ts (100%) rename client/features/address-metadata/types/{view.ts => client.ts} (100%) rename client/features/address-metadata/utils/{parseMetaPayload.ts => parse-meta-payload.ts} (97%) delete mode 100644 configs/app/features/public-tags-submission.ts diff --git a/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts index 069aaa47c4..55f95fe594 100644 --- a/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts +++ b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts @@ -1,13 +1,13 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; import useApiQuery from 'client/api/hooks/useApiQuery'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import parseMetaPayload from '../utils/parseMetaPayload'; +import parseMetaPayload from '../utils/parse-meta-payload'; export default function useAddressMetadataInfoQuery(addresses: Array, isEnabled = true) { diff --git a/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts b/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts index 3e08dfa028..cea2b43bc1 100644 --- a/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts +++ b/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts @@ -25,7 +25,7 @@ export default function useAddressMetadataInitUpdate({ address, counters, isEnab React.useEffect(() => { if ( feature.isEnabled && - feature.isAddressTagsUpdateEnabled && + feature.isTagsUpdateEnabled && address && isEnabled && counters?.transactions_count && Number(counters.transactions_count) > TXS_THRESHOLD diff --git a/ui/pages/AccountsLabelSearch.pw.tsx b/client/features/address-metadata/pages/tag-search/TagSearch.pw.tsx similarity index 89% rename from ui/pages/AccountsLabelSearch.pw.tsx rename to client/features/address-metadata/pages/tag-search/TagSearch.pw.tsx index a93c4706a4..24fe188f33 100644 --- a/ui/pages/AccountsLabelSearch.pw.tsx +++ b/client/features/address-metadata/pages/tag-search/TagSearch.pw.tsx @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + import React from 'react'; import type { AddressesMetadataSearchResult } from 'client/features/address-metadata/types/api'; @@ -6,7 +8,7 @@ import * as addressMocks from 'client/slices/address/mocks/address'; import { test, expect } from 'playwright/lib'; -import AccountsLabelSearch from './AccountsLabelSearch'; +import TagSearch from './TagSearch'; const addresses: AddressesMetadataSearchResult = { items: [ @@ -57,6 +59,6 @@ test('base view +@mobile', async({ render, mockTextAd, mockApiResponse }) => { }, }, ); - const component = await render(, { hooksConfig }); + const component = await render(, { hooksConfig }); await expect(component).toHaveScreenshot(); }); diff --git a/ui/pages/AccountsLabelSearch.tsx b/client/features/address-metadata/pages/tag-search/TagSearch.tsx similarity index 90% rename from ui/pages/AccountsLabelSearch.tsx rename to client/features/address-metadata/pages/tag-search/TagSearch.tsx index 245eadbe90..4dc70b4f7b 100644 --- a/ui/pages/AccountsLabelSearch.tsx +++ b/client/features/address-metadata/pages/tag-search/TagSearch.tsx @@ -12,8 +12,6 @@ import getQueryParamString from 'client/shared/router/get-query-param-string'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressesLabelSearchListItem from 'ui/addressesLabelSearch/AddressesLabelSearchListItem'; -import AddressesLabelSearchTable from 'ui/addressesLabelSearch/AddressesLabelSearchTable'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import EntityTag from 'ui/shared/EntityTags/EntityTag'; @@ -21,7 +19,10 @@ import PageTitle from 'ui/shared/Page/PageTitle'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText'; -const AccountsLabelSearch = () => { +import TagSearchListItem from './TagSearchListItem'; +import TagSearchTable from './TagSearchTable'; + +const TagSearch = () => { const router = useRouter(); const slug = getQueryParamString(router.query.slug); @@ -48,7 +49,7 @@ const AccountsLabelSearch = () => { const content = data?.items ? ( <> - { { data.items.map((item, index) => { return ( - { ); }; -export default AccountsLabelSearch; +export default TagSearch; diff --git a/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx b/client/features/address-metadata/pages/tag-search/TagSearchListItem.tsx similarity index 94% rename from ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx rename to client/features/address-metadata/pages/tag-search/TagSearchListItem.tsx index afb0070d1e..7105957067 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx +++ b/client/features/address-metadata/pages/tag-search/TagSearchListItem.tsx @@ -18,7 +18,7 @@ type Props = { isLoading?: boolean; }; -const AddressesLabelSearchListItem = ({ +const TagSearchListItem = ({ item, isLoading, }: Props) => { @@ -51,4 +51,4 @@ const AddressesLabelSearchListItem = ({ ); }; -export default React.memo(AddressesLabelSearchListItem); +export default React.memo(TagSearchListItem); diff --git a/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx b/client/features/address-metadata/pages/tag-search/TagSearchTable.tsx similarity index 81% rename from ui/addressesLabelSearch/AddressesLabelSearchTable.tsx rename to client/features/address-metadata/pages/tag-search/TagSearchTable.tsx index 6475d192b5..be9d720057 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx +++ b/client/features/address-metadata/pages/tag-search/TagSearchTable.tsx @@ -8,7 +8,7 @@ import { currencyUnits } from 'client/shared/chain/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import AddressesLabelSearchTableItem from './AddressesLabelSearchTableItem'; +import TagSearchTableItem from './TagSearchTableItem'; interface Props { items: Array; @@ -16,7 +16,7 @@ interface Props { isLoading?: boolean; } -const AddressesLabelSearchTable = ({ items, top, isLoading }: Props) => { +const TagSearchTable = ({ items, top, isLoading }: Props) => { return ( @@ -28,7 +28,7 @@ const AddressesLabelSearchTable = ({ items, top, isLoading }: Props) => { { items.map((item, index) => ( - { ); }; -export default AddressesLabelSearchTable; +export default TagSearchTable; diff --git a/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx b/client/features/address-metadata/pages/tag-search/TagSearchTableItem.tsx similarity index 92% rename from ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx rename to client/features/address-metadata/pages/tag-search/TagSearchTableItem.tsx index 03a509fbc2..1719695684 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx +++ b/client/features/address-metadata/pages/tag-search/TagSearchTableItem.tsx @@ -15,7 +15,7 @@ type Props = { isLoading?: boolean; }; -const AddressesLabelSearchTableItem = ({ +const TagSearchTableItem = ({ item, isLoading, }: Props) => { @@ -47,4 +47,4 @@ const AddressesLabelSearchTableItem = ({ ); }; -export default React.memo(AddressesLabelSearchTableItem); +export default React.memo(TagSearchTableItem); diff --git a/ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_default_base-view-mobile-1.png b/client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_default_base-view-mobile-1.png rename to client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_mobile_base-view-mobile-1.png b/client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/AccountsLabelSearch.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/address-metadata/pages/tag-search/__screenshots__/TagSearch.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/pages/PublicTagsSubmit.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmition.tsx similarity index 74% rename from ui/pages/PublicTagsSubmit.tsx rename to client/features/address-metadata/pages/tag-submition/TagSubmition.tsx index 4b4d9a0276..96cc850bfa 100644 --- a/ui/pages/PublicTagsSubmit.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmition.tsx @@ -2,21 +2,22 @@ import React from 'react'; -import type { FormSubmitResult } from 'ui/publicTags/submit/types'; +import type { FormSubmitResult } from './types'; import useApiQuery from 'client/api/hooks/useApiQuery'; import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; -import PublicTagsSubmitForm from 'ui/publicTags/submit/PublicTagsSubmitForm'; -import PublicTagsSubmitResult from 'ui/publicTags/submit/PublicTagsSubmitResult'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import PageTitle from 'ui/shared/Page/PageTitle'; +import TagSubmitionForm from './TagSubmitionForm'; +import TagSubmitionResult from './TagSubmitionResult'; + type Screen = 'form' | 'result' | 'initializing' | 'error'; -const PublicTagsSubmit = () => { +const TagSubmition = () => { const [ screen, setScreen ] = React.useState('initializing'); const [ submitResult, setSubmitResult ] = React.useState(); @@ -42,9 +43,9 @@ const PublicTagsSubmit = () => { case 'error': return ; case 'form': - return ; + return ; case 'result': - return ; + return ; default: return null; } @@ -58,4 +59,4 @@ const PublicTagsSubmit = () => { ); }; -export default PublicTagsSubmit; +export default TagSubmition; diff --git a/ui/publicTags/submit/PublicTagsSubmitForm.pw.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx similarity index 86% rename from ui/publicTags/submit/PublicTagsSubmitForm.pw.tsx rename to client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx index 41f8c28073..390d549435 100644 --- a/ui/publicTags/submit/PublicTagsSubmitForm.pw.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx @@ -6,13 +6,13 @@ import { publicTagTypes as configMock } from 'mocks/metadata/publicTagTypes'; import { expect, test } from 'playwright/lib'; import * as mocks from './mocks'; -import PublicTagsSubmitForm from './PublicTagsSubmitForm'; +import TagSubmitionForm from './TagSubmitionForm'; const onSubmitResult = () => {}; test('base view +@mobile', async({ render }) => { const component = await render( - , + , ); await component.getByLabel(/Smart contract \/ Address/i).fill(mocks.address1); diff --git a/ui/publicTags/submit/PublicTagsSubmitForm.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx similarity index 93% rename from ui/publicTags/submit/PublicTagsSubmitForm.tsx rename to client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx index 95a42b5073..dccfcdb4d4 100644 --- a/ui/publicTags/submit/PublicTagsSubmitForm.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx @@ -26,8 +26,8 @@ import { Hint } from 'toolkit/components/Hint/Hint'; import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; -import PublicTagsSubmitFieldAddresses from './fields/PublicTagsSubmitFieldAddresses'; -import PublicTagsSubmitFieldTags from './fields/PublicTagsSubmitFieldTags'; +import TagSubmitionFieldAddresses from './fields/TagSubmitionFieldAddresses'; +import TagSubmitionFieldTags from './fields/TagSubmitionFieldTags'; import { convertFormDataToRequestsBody, getFormDefaultValues } from './utils'; interface Props { @@ -36,7 +36,7 @@ interface Props { onSubmitResult: (result: FormSubmitResult) => void; } -const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => { +const TagSubmitionForm = ({ config, userInfo, onSubmitResult }: Props) => { const isMobile = useIsMobile(); const router = useRouter(); const apiFetch = useApiFetch(); @@ -128,8 +128,8 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => { - - + + name="description" @@ -168,4 +168,4 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => { ); }; -export default React.memo(PublicTagsSubmitForm); +export default React.memo(TagSubmitionForm); diff --git a/ui/publicTags/submit/PublicTagsSubmitResult.pw.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionResult.pw.tsx similarity index 61% rename from ui/publicTags/submit/PublicTagsSubmitResult.pw.tsx rename to client/features/address-metadata/pages/tag-submition/TagSubmitionResult.pw.tsx index fc853b7278..77d1bb02e6 100644 --- a/ui/publicTags/submit/PublicTagsSubmitResult.pw.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionResult.pw.tsx @@ -3,15 +3,15 @@ import React from 'react'; import { expect, test, devices } from 'playwright/lib'; import * as mocks from './mocks'; -import PublicTagsSubmitResult from './PublicTagsSubmitResult'; +import TagSubmitionResult from './TagSubmitionResult'; test('all success result view +@mobile', async({ render }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot(); }); test('result with errors view', async({ render }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot(); }); @@ -19,7 +19,7 @@ test.describe('mobile', () => { test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test('result with errors view', async({ render }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot(); }); }); diff --git a/ui/publicTags/submit/PublicTagsSubmitResult.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionResult.tsx similarity index 87% rename from ui/publicTags/submit/PublicTagsSubmitResult.tsx rename to client/features/address-metadata/pages/tag-submition/TagSubmitionResult.tsx index 7ed40be7ce..0edbfcb02e 100644 --- a/ui/publicTags/submit/PublicTagsSubmitResult.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionResult.tsx @@ -14,15 +14,15 @@ import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; import { makePrettyLink } from 'toolkit/utils/url'; -import PublicTagsSubmitResultSuccess from './result/PublicTagsSubmitResultSuccess'; -import PublicTagsSubmitResultWithErrors from './result/PublicTagsSubmitResultWithErrors'; +import TagSubmitionResultSuccess from './result/TagSubmitionResultSuccess'; +import TagSubmitionResultWithErrors from './result/TagSubmitionResultWithErrors'; import { groupSubmitResult } from './utils'; interface Props { data: FormSubmitResult | undefined; } -const PublicTagsSubmitResult = ({ data }: Props) => { +const TagSubmitionResult = ({ data }: Props) => { const groupedData = React.useMemo(() => groupSubmitResult(data), [ data ]); if (!groupedData) { @@ -69,7 +69,7 @@ const PublicTagsSubmitResult = ({ data }: Props) => { Public tags/labels - { hasErrors ? : } + { hasErrors ? : } { hasErrors && ( @@ -87,4 +87,4 @@ const PublicTagsSubmitResult = ({ data }: Props) => { ); }; -export default React.memo(PublicTagsSubmitResult); +export default React.memo(TagSubmitionResult); diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_default_base-view-mobile-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_default_base-view-mobile-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_mobile_base-view-mobile-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitForm.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionForm.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_all-success-result-view-mobile-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_all-success-result-view-mobile-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_all-success-result-view-mobile-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_all-success-result-view-mobile-1.png diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_mobile-result-with-errors-view-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_mobile-result-with-errors-view-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_mobile-result-with-errors-view-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_mobile-result-with-errors-view-1.png diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_result-with-errors-view-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_result-with-errors-view-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_default_result-with-errors-view-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_default_result-with-errors-view-1.png diff --git a/ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_mobile_all-success-result-view-mobile-1.png b/client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_mobile_all-success-result-view-mobile-1.png similarity index 100% rename from ui/publicTags/submit/__screenshots__/PublicTagsSubmitResult.pw.tsx_mobile_all-success-result-view-mobile-1.png rename to client/features/address-metadata/pages/tag-submition/__screenshots__/TagSubmitionResult.pw.tsx_mobile_all-success-result-view-mobile-1.png diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldAddresses.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldAddresses.tsx similarity index 95% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldAddresses.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldAddresses.tsx index 9aef6f6da9..e9284e9017 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldAddresses.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldAddresses.tsx @@ -12,7 +12,7 @@ import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddre const LIMIT = 20; -const PublicTagsSubmitFieldAddresses = () => { +const TagSubmitionFieldAddresses = () => { const { control, formState } = useFormContext(); const { fields, insert, remove } = useFieldArray({ name: 'addresses', @@ -70,4 +70,4 @@ const PublicTagsSubmitFieldAddresses = () => { ); }; -export default React.memo(PublicTagsSubmitFieldAddresses); +export default React.memo(TagSubmitionFieldAddresses); diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTag.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTag.tsx similarity index 91% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldTag.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTag.tsx index 4f695e3531..501088a9be 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTag.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTag.tsx @@ -17,8 +17,8 @@ import { FormFieldUrl } from 'toolkit/components/forms/fields/FormFieldUrl'; import { colorValidator } from 'toolkit/components/forms/validators/color'; import EntityTag from 'ui/shared/EntityTags/EntityTag'; -import PublicTagsSubmitFieldTagIcon from './PublicTagsSubmitFieldTagIcon'; -import PublicTagsSubmitFieldTagType from './PublicTagsSubmitFieldTagType'; +import TagSubmitionFieldTagIcon from './TagSubmitionFieldTagIcon'; +import TagSubmitionFieldTagType from './TagSubmitionFieldTagType'; const CIRCLE_BG_COLOR_DEFAULT = { bgColor: { _light: 'gray.100', _dark: 'gray.700' }, @@ -35,7 +35,7 @@ interface Props { onRemoveClick?: (index: number) => void; } -const PublicTagsSubmitFieldTag = ({ index, isDisabled, errors, onAddClick, onRemoveClick, tagTypes, field }: Props) => { +const TagSubmitionFieldTag = ({ index, isDisabled, errors, onAddClick, onRemoveClick, tagTypes, field }: Props) => { const isMobile = useIsMobile(); const bgColorDefault = { _light: 'blackAlpha.50', _dark: 'whiteAlpha.100' }; const bgColorError = { _light: 'red.50', _dark: 'red.900' }; @@ -65,7 +65,7 @@ const PublicTagsSubmitFieldTag = ({ index, isDisabled, errors, onAddClick, onRem /> - + @@ -84,7 +84,7 @@ const PublicTagsSubmitFieldTag = ({ index, isDisabled, errors, onAddClick, onRem sampleDefaultBgColor={ CIRCLE_BG_COLOR_DEFAULT.textColor } /> - + @@ -147,4 +147,4 @@ const PublicTagsSubmitFieldTag = ({ index, isDisabled, errors, onAddClick, onRem ); }; -export default PublicTagsSubmitFieldTag; +export default TagSubmitionFieldTag; diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIcon.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIcon.tsx similarity index 73% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIcon.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIcon.tsx index d61edb0f0a..e73f8437fa 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIcon.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIcon.tsx @@ -10,13 +10,13 @@ import { FormFieldImagePreview } from 'toolkit/components/forms/fields/image/For import { useImageField } from 'toolkit/components/forms/fields/image/useImageField'; import IconSvg from 'ui/shared/IconSvg'; -import PublicTagsSubmitFieldTagIconPreview from './PublicTagsSubmitFieldTagIconPreview'; +import TagSubmitionFieldTagIconPreview from './TagSubmitionFieldTagIconPreview'; interface Props { index: number; } -const PublicTagsSubmitFieldTagIcon = ({ index }: Props) => { +const TagSubmitionFieldTagIcon = ({ index }: Props) => { const imageField = useImageField({ name: `tags.${ index }.iconUrl`, isRequired: false }); @@ -27,15 +27,15 @@ const PublicTagsSubmitFieldTagIcon = ({ index }: Props) => { placeholder="Label icon URL" { ...imageField.input } /> - + } boxSize="30px" /> - + ); }; -export default React.memo(PublicTagsSubmitFieldTagIcon); +export default React.memo(TagSubmitionFieldTagIcon); diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIconPreview.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIconPreview.tsx similarity index 78% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIconPreview.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIconPreview.tsx index 5aa34f75ec..0f7e705b5a 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagIconPreview.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagIconPreview.tsx @@ -9,7 +9,7 @@ interface Props { children: React.ReactElement; } -const PublicTagsSubmitFieldTagIconPreview = ({ url, isInvalid, children }: Props) => { +const TagSubmitionFieldTagIconPreview = ({ url, isInvalid, children }: Props) => { const borderColorActive = isInvalid ? 'error' : 'input.border.filled'; return ( @@ -26,4 +26,4 @@ const PublicTagsSubmitFieldTagIconPreview = ({ url, isInvalid, children }: Props ); }; -export default React.memo(PublicTagsSubmitFieldTagIconPreview); +export default React.memo(TagSubmitionFieldTagIconPreview); diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagType.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagType.tsx similarity index 88% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldTagType.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagType.tsx index ec8e171d06..7ccba02adb 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTagType.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTagType.tsx @@ -14,7 +14,7 @@ interface Props { tagTypes: Array | undefined; } -const PublicTagsSubmitFieldTagType = ({ index, tagTypes }: Props) => { +const TagSubmitionFieldTagType = ({ index, tagTypes }: Props) => { const collection = React.useMemo(() => { const items = tagTypes?.map((type) => ({ @@ -35,4 +35,4 @@ const PublicTagsSubmitFieldTagType = ({ index, tagTypes }: Props) => { ); }; -export default React.memo(PublicTagsSubmitFieldTagType); +export default React.memo(TagSubmitionFieldTagType); diff --git a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTags.tsx b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTags.tsx similarity index 87% rename from ui/publicTags/submit/fields/PublicTagsSubmitFieldTags.tsx rename to client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTags.tsx index c55f1a8327..33a8daa002 100644 --- a/ui/publicTags/submit/fields/PublicTagsSubmitFieldTags.tsx +++ b/client/features/address-metadata/pages/tag-submition/fields/TagSubmitionFieldTags.tsx @@ -6,7 +6,7 @@ import { useFieldArray, useFormContext } from 'react-hook-form'; import type { FormFields } from '../types'; import type { PublicTagType } from 'client/features/address-metadata/types/api'; -import PublicTagsSubmitFieldTag from './PublicTagsSubmitFieldTag'; +import TagSubmitionFieldTag from './TagSubmitionFieldTag'; const LIMIT = 5; @@ -14,7 +14,7 @@ interface Props { tagTypes: Array | undefined; } -const PublicTagsSubmitFieldTags = ({ tagTypes }: Props) => { +const TagSubmitionFieldTags = ({ tagTypes }: Props) => { const { control, formState, watch } = useFormContext(); const { fields, insert, remove } = useFieldArray({ name: 'tags', @@ -45,7 +45,7 @@ const PublicTagsSubmitFieldTags = ({ tagTypes }: Props) => { const errors = formState.errors?.tags?.[ index ]; return ( - { ); }; -export default React.memo(PublicTagsSubmitFieldTags); +export default React.memo(TagSubmitionFieldTags); diff --git a/ui/publicTags/submit/mocks.ts b/client/features/address-metadata/pages/tag-submition/mocks.ts similarity index 100% rename from ui/publicTags/submit/mocks.ts rename to client/features/address-metadata/pages/tag-submition/mocks.ts diff --git a/ui/publicTags/submit/result/PublicTagsSubmitResultSuccess.tsx b/client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultSuccess.tsx similarity index 93% rename from ui/publicTags/submit/result/PublicTagsSubmitResultSuccess.tsx rename to client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultSuccess.tsx index 27b654e6bc..07d1bde8dd 100644 --- a/ui/publicTags/submit/result/PublicTagsSubmitResultSuccess.tsx +++ b/client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultSuccess.tsx @@ -13,7 +13,7 @@ interface Props { data: FormSubmitResultGrouped; } -const PublicTagsSubmitResultSuccess = ({ data }: Props) => { +const TagSubmitionResultSuccess = ({ data }: Props) => { return ( @@ -55,4 +55,4 @@ const PublicTagsSubmitResultSuccess = ({ data }: Props) => { ); }; -export default React.memo(PublicTagsSubmitResultSuccess); +export default React.memo(TagSubmitionResultSuccess); diff --git a/ui/publicTags/submit/result/PublicTagsSubmitResultWithErrors.tsx b/client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultWithErrors.tsx similarity index 96% rename from ui/publicTags/submit/result/PublicTagsSubmitResultWithErrors.tsx rename to client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultWithErrors.tsx index 73de0d1170..35230c67be 100644 --- a/ui/publicTags/submit/result/PublicTagsSubmitResultWithErrors.tsx +++ b/client/features/address-metadata/pages/tag-submition/result/TagSubmitionResultWithErrors.tsx @@ -19,7 +19,7 @@ interface Props { data: FormSubmitResultGrouped; } -const PublicTagsSubmitResultWithErrors = ({ data }: Props) => { +const TagSubmitionResultWithErrors = ({ data }: Props) => { const isMobile = useIsMobile(); const bgColorSuccess = { _light: 'green.50', _dark: 'green.800' }; const bgColorError = { _light: 'red.50', _dark: 'red.800' }; @@ -98,4 +98,4 @@ const PublicTagsSubmitResultWithErrors = ({ data }: Props) => { ); }; -export default React.memo(PublicTagsSubmitResultWithErrors); +export default React.memo(TagSubmitionResultWithErrors); diff --git a/ui/publicTags/submit/types.ts b/client/features/address-metadata/pages/tag-submition/types.ts similarity index 100% rename from ui/publicTags/submit/types.ts rename to client/features/address-metadata/pages/tag-submition/types.ts diff --git a/ui/publicTags/submit/utils.spec.ts b/client/features/address-metadata/pages/tag-submition/utils.spec.ts similarity index 100% rename from ui/publicTags/submit/utils.spec.ts rename to client/features/address-metadata/pages/tag-submition/utils.spec.ts diff --git a/ui/publicTags/submit/utils.ts b/client/features/address-metadata/pages/tag-submition/utils.ts similarity index 100% rename from ui/publicTags/submit/utils.ts rename to client/features/address-metadata/pages/tag-submition/utils.ts diff --git a/client/features/address-metadata/types/view.ts b/client/features/address-metadata/types/client.ts similarity index 100% rename from client/features/address-metadata/types/view.ts rename to client/features/address-metadata/types/client.ts diff --git a/client/features/address-metadata/utils/parseMetaPayload.ts b/client/features/address-metadata/utils/parse-meta-payload.ts similarity index 97% rename from client/features/address-metadata/utils/parseMetaPayload.ts rename to client/features/address-metadata/utils/parse-meta-payload.ts index d27e95d04c..8fb6ed8f1c 100644 --- a/client/features/address-metadata/utils/parseMetaPayload.ts +++ b/client/features/address-metadata/utils/parse-meta-payload.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { AddressMetadataTag } from 'client/features/address-metadata/types/api'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; type MetaParsed = NonNullable; diff --git a/client/slices/address/pages/details/info/AddressAlerts.tsx b/client/slices/address/pages/details/info/AddressAlerts.tsx index 1d9888f8ff..93a5c5ec02 100644 --- a/client/slices/address/pages/details/info/AddressAlerts.tsx +++ b/client/slices/address/pages/details/info/AddressAlerts.tsx @@ -3,7 +3,7 @@ import { Flex, chakra } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; import type { AlertProps } from 'toolkit/chakra/alert'; import { Alert } from 'toolkit/chakra/alert'; diff --git a/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx b/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx index ef1884d8bb..890c713466 100644 --- a/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx +++ b/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx @@ -3,7 +3,7 @@ import { HStack } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; import config from 'configs/app'; import { Image } from 'toolkit/chakra/image'; diff --git a/configs/app/features/address-metadata.ts b/configs/app/features/address-metadata.ts index 8496c31f2f..0deb06d958 100644 --- a/configs/app/features/address-metadata.ts +++ b/configs/app/features/address-metadata.ts @@ -3,16 +3,20 @@ import type { Feature } from './types'; import apis from '../apis'; +import app from '../app'; +import services from '../services'; import { getEnvValue } from '../utils'; const title = 'Address metadata'; -const config: Feature<{ isAddressTagsUpdateEnabled: boolean }> = (() => { +const config: Feature<{ isTagsUpdateEnabled: boolean; isTagSubmitionEnabled: boolean }> = (() => { if (apis.metadata) { + const isTagSubmitionEnabled = !app.isPrivateMode && Boolean(services.reCaptchaV2.siteKey) && Boolean(apis.admin); return Object.freeze({ title, isEnabled: true, - isAddressTagsUpdateEnabled: getEnvValue('NEXT_PUBLIC_METADATA_ADDRESS_TAGS_UPDATE_ENABLED') !== 'false', + isTagsUpdateEnabled: getEnvValue('NEXT_PUBLIC_METADATA_ADDRESS_TAGS_UPDATE_ENABLED') !== 'false', + isTagSubmitionEnabled, }); } diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index 5b9a95834f..576c4896e3 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -35,7 +35,6 @@ export { default as multichain } from './multichain'; export { default as multichainButton } from './multichain-button'; export { default as nameServices } from './name-services'; export { default as dexPools } from './dex-pools'; -export { default as publicTagsSubmission } from './public-tags-submission'; export { default as rewards } from './rewards'; export { default as rollbar } from './rollbar'; export { default as rollup } from './rollup'; diff --git a/configs/app/features/public-tags-submission.ts b/configs/app/features/public-tags-submission.ts deleted file mode 100644 index f3b0f46231..0000000000 --- a/configs/app/features/public-tags-submission.ts +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { Feature } from './types'; - -import apis from '../apis'; -import app from '../app'; -import services from '../services'; -import addressMetadata from './address-metadata'; - -const title = 'Public tag submission'; - -const config: Feature<{}> = (() => { - if (!app.isPrivateMode && services.reCaptchaV2.siteKey && addressMetadata.isEnabled && apis.admin) { - return Object.freeze({ - title, - isEnabled: true, - }); - } - - return Object.freeze({ - title, - isEnabled: false, - }); -})(); - -export default config; diff --git a/lib/hooks/useNavItems.tsx b/lib/hooks/useNavItems.tsx index b3612a7d01..ee4efa73f7 100644 --- a/lib/hooks/useNavItems.tsx +++ b/lib/hooks/useNavItems.tsx @@ -3,6 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; +import { getFeaturePayload } from 'configs/app/features/types'; import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/navigation'; import { layerLabels } from 'client/features/rollup/common/utils/layer'; @@ -335,7 +336,7 @@ export default function useNavItems(): ReturnType { nextRoute: { pathname: '/contract-verification' as const }, isActive: pathname.startsWith('/contract-verification'), }, - config.features.publicTagsSubmission.isEnabled && { + getFeaturePayload(config.features.addressMetadata)?.isTagSubmitionEnabled && { text: 'Submit public tag', nextRoute: { pathname: '/public-tags/submit' as const }, isActive: pathname.startsWith('/public-tags/submit'), diff --git a/nextjs/getServerSideProps/guards.ts b/nextjs/getServerSideProps/guards.ts index e28afa7ebd..a7f859430f 100644 --- a/nextjs/getServerSideProps/guards.ts +++ b/nextjs/getServerSideProps/guards.ts @@ -3,6 +3,7 @@ import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next'; import type { RollupType } from 'client/features/rollup/common/types/config'; +import { getFeaturePayload } from 'configs/app/features/types'; import type { Route } from 'nextjs-routes'; import type { Props } from 'nextjs/getServerSideProps/handlers'; @@ -194,7 +195,7 @@ export const dev: Guard = (chainConfig: typeof config) => async() => { }; export const publicTagsSubmit: Guard = (chainConfig: typeof config) => async() => { - if (!chainConfig.features.publicTagsSubmission.isEnabled) { + if (!getFeaturePayload(chainConfig.features.addressMetadata)?.isTagSubmitionEnabled) { return { notFound: true, }; diff --git a/pages/accounts/label/[slug].tsx b/pages/accounts/label/[slug].tsx index bb6e90914e..d5e8c82362 100644 --- a/pages/accounts/label/[slug].tsx +++ b/pages/accounts/label/[slug].tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const AccountsLabelSearch = dynamic(() => import('ui/pages/AccountsLabelSearch'), { ssr: false }); +const AccountsLabelSearch = dynamic(() => import('client/features/address-metadata/pages/tag-search/TagSearch'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx index ee426501c6..62021d50d8 100644 --- a/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx +++ b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx @@ -8,7 +8,7 @@ import PageNextJs from 'nextjs/PageNextJs'; import { MultichainProvider } from 'lib/contexts/multichain'; -const AccountsLabelSearch = dynamic(() => import('ui/pages/AccountsLabelSearch'), { ssr: false }); +const AccountsLabelSearch = dynamic(() => import('client/features/address-metadata/pages/tag-search/TagSearch'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/pages/public-tags/submit.tsx b/pages/public-tags/submit.tsx index 558759fc89..0f87e7c589 100644 --- a/pages/public-tags/submit.tsx +++ b/pages/public-tags/submit.tsx @@ -5,12 +5,12 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import PublicTagsSubmit from 'ui/pages/PublicTagsSubmit'; +import TagSubmition from 'client/features/address-metadata/pages/tag-submition/TagSubmition'; const Page: NextPage = () => { return ( - + ); }; diff --git a/ui/shared/AccountActionsMenu/AccountActionsMenu.tsx b/ui/shared/AccountActionsMenu/AccountActionsMenu.tsx index 4757ed6b4e..fbd5cbde71 100644 --- a/ui/shared/AccountActionsMenu/AccountActionsMenu.tsx +++ b/ui/shared/AccountActionsMenu/AccountActionsMenu.tsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import type { ItemProps } from './types'; +import { getFeaturePayload } from 'configs/app/features/types'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import getQueryParamString from 'client/shared/router/get-query-param-string'; @@ -53,7 +54,7 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr }, { render: (props: ItemProps) => , - enabled: config.features.account.isEnabled && !isTxPage && config.features.publicTagsSubmission.isEnabled, + enabled: config.features.account.isEnabled && !isTxPage && Boolean(getFeaturePayload(config.features.addressMetadata)?.isTagSubmitionEnabled), }, ].filter(({ enabled }) => enabled); diff --git a/ui/shared/AppActionButton/AppActionButton.pw.tsx b/ui/shared/AppActionButton/AppActionButton.pw.tsx index ad4c37321f..ca52b3156d 100644 --- a/ui/shared/AppActionButton/AppActionButton.pw.tsx +++ b/ui/shared/AppActionButton/AppActionButton.pw.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; import * as actionButtonMetadataMock from 'mocks/metadata/appActionButton'; import { test, expect } from 'playwright/lib'; diff --git a/ui/shared/AppActionButton/AppActionButton.tsx b/ui/shared/AppActionButton/AppActionButton.tsx index c5cba26db4..eee11f9c92 100644 --- a/ui/shared/AppActionButton/AppActionButton.tsx +++ b/ui/shared/AppActionButton/AppActionButton.tsx @@ -3,7 +3,7 @@ import { Text, chakra } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; import { route } from 'nextjs-routes'; diff --git a/ui/shared/EntityTags/types.ts b/ui/shared/EntityTags/types.ts index 42dc343563..40d2f6e6e7 100644 --- a/ui/shared/EntityTags/types.ts +++ b/ui/shared/EntityTags/types.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { AddressMetadataTagType } from 'client/features/address-metadata/types/api'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; export type EntityTagType = AddressMetadataTagType | 'custom' | 'watchlist' | 'private_tag'; From a1773a415f93bf8f69c55426334539107e31d19c Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 14:53:38 +0200 Subject: [PATCH 23/85] update glossary --- docs/GLOSSARY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index 067f2bb054..9c4a5d307a 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -48,6 +48,7 @@ Optional product areas and their backing services. Each corresponds to a folder | **Address Metadata** | The feature that provides address labels, metadata enrichment, and label-based address search. Backs the metadata panel on address detail pages and the Label Search page (`/accounts/label/[slug]`). Feature folder: `client/features/address-metadata/`. Config-gated via `NEXT_PUBLIC_METADATA_SERVICE_API_HOST`. Note: **Public Tags** is one sub-feature of Address Metadata, not a synonym for the whole. | | **BENS (Blockscout Name Service)** | Blockscout's own address naming service, chain-agnostic and operated by Blockscout. Distinct from ENS (which is Ethereum-specific). BENS domain lookups and **Clusters** are co-located under the `name-services/` sub-structure: `client/features/name-services/domains/` for BENS/ENS and `client/features/name-services/clusters/` for Clusters. | | **Clusters** | An address identity and grouping service. Aggregates multiple addresses under a named cluster (individual, protocol, organization). Exposes a directory, leaderboard, and per-address lookup. Backed by the Clusters API (`NEXT_PUBLIC_CLUSTERS_API_HOST`). Co-located with **BENS** under `client/features/name-services/clusters/`. | +| **Connect Wallet** | Blockchain interaction feature — enables writing to contracts, signing transactions, and connecting a wallet to the explorer. Supports two connector types: Reown (WalletConnect) and Dynamic. Previously named `blockchain-interaction` in the config; the correct config key is `connectWallet` (file: `configs/app/features/connect-wallet.ts`). Lives under `client/features/connect-wallet/`. | | **Data Availability** | The config-gated *feature* that surfaces **Blob** data posted to Ethereum (EIP-4844). `data-availability` is the name used for the feature folder and config flag — the underlying entity is still called a **Blob**. Config-gated via `NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED`. | | **Fault Proof System** | Optimism's mechanism for proving the correctness of L2 state transitions on L1 via **Dispute Games**. Enabled by the `NEXT_PUBLIC_FAULT_PROOF_ENABLED` feature flag. | | **Flashblocks** | MegaETH's sub-second block streaming mechanism. Delivers real-time pre-confirmation block data via WebSocket. Specific to the MegaETH chain variant. | @@ -66,3 +67,4 @@ Optional product areas and their backing services. Each corresponds to a folder | **Validators** | The set of active block validators on chains that expose this concept (e.g. zkSync Era, Celo). Displayed in a dedicated list. Not present on standard PoW/PoS EVM chains without explicit support. Config-gated via `NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE`. | | **Visualize** | A service that converts Solidity source code into UML diagrams (class and storage layout). Accessed from contract detail pages. Backed by `NEXT_PUBLIC_VISUALIZE_API_HOST`. | | **Watchlist** | An **Account** feature that lets authenticated users track a set of addresses and receive notifications for their activity. | +| **Web3 Wallet** | Feature that lets users add tokens and networks directly from the explorer to their browser wallet (MetaMask, Coinbase Wallet, Rabby, OKX, Trust, TokenPocket). Surfaces as "Add token to wallet" buttons on token pages and an "Add network" button in the top bar and footer. Distinct from **Connect Wallet** (which handles signing/writing). | From 05d7f1187d26a3abce81c84bde8f381c2ee03fca Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 18:40:48 +0200 Subject: [PATCH 24/85] [Migration 6-3] Features `multichain` and `multichain-button` (#3481) * feat: migrate multichain and multichain-button features to client/ (6-3) Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/3a77ef5d-f314-4986-aa6e-dfe4b057ff38 Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * [skip ci] fixes --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> Co-authored-by: tom --- client/api/hooks/useApiQuery.ts | 2 +- client/api/services/multichain-aggregator.ts | 2 +- .../hooks/useAddressMetadataInfoQuery.ts | 3 ++- .../components/AddressAdvancedFilterLink.tsx | 5 +++-- .../advanced-filter/components/ItemByColumn.tsx | 2 +- .../components/filters/TypeFilter.tsx | 3 ++- .../pages/index/AdvancedFilter.tsx | 2 +- .../pages/token/TokenAdvancedFilterLink.tsx | 3 ++- client/features/advanced-filter/utils/lib.ts | 2 +- .../features/chain-stats/hooks/useChainStats.tsx | 2 +- .../chain-stats/mocks/cross-chain-txs-paths.ts | 2 +- .../pages/details/ChainStatsDetails.pw.tsx | 2 +- .../pages/details/ChainStatsDetailsLineChart.tsx | 7 ++++--- .../pages/index/ChainStatsSections.tsx | 2 +- .../features/csv-export/components/CsvExport.tsx | 5 +++-- .../dialog/CsvExportDialogDescription.tsx | 2 +- .../flashblocks/components/FlashblockEntity.tsx | 3 ++- .../pages/essential-dapp/revoke/Revoke.pw.tsx | 3 ++- .../revoke/components/Content.pw.tsx | 2 +- .../pages/address/AddressMultichainButton.tsx | 2 +- .../features/multichain-button/types/client.ts | 0 .../multichain/components/ChainSelect.tsx | 0 .../components/ClusterChainsPopover.tsx | 0 .../multichain/components/ListCounterText.tsx | 0 .../multichain/components/NativeTokenIcon.tsx | 2 +- .../features/multichain/context.tsx | 5 +++-- .../multichain/hooks/useFetchParentChainApi.ts | 0 .../multichain/hooks}/useRoutedChainSelect.ts | 2 +- .../multichain/hooks/useSearchMultichain.ts | 2 +- .../features/multichain/mocks}/address.ts | 0 .../features/multichain/mocks}/chains.ts | 2 +- .../features/multichain/mocks}/metrics.ts | 0 .../features/multichain/mocks}/portfolio.ts | 0 client/features/multichain/mocks/search.ts | 2 +- .../features/multichain/mocks}/stats.ts | 0 .../features/multichain/mocks}/tokens.ts | 2 +- .../pages}/address/MultichainAddress.pw.tsx | 9 +++++---- .../pages}/address/MultichainAddress.tsx | 6 +++--- .../MultichainAddressCoinBalanceHistory.tsx | 9 +++++---- .../pages}/address/MultichainAddressContract.tsx | 9 +++++---- .../address/MultichainAddressInternalTxs.tsx | 6 +++--- .../pages}/address/MultichainAddressLogs.tsx | 6 +++--- .../address/MultichainAddressPortfolio.pw.tsx | 5 +++-- .../pages}/address/MultichainAddressPortfolio.tsx | 7 ++++--- .../MultichainAddressTokenTransfers.pw.tsx | 5 +++-- .../address/MultichainAddressTokenTransfers.tsx | 8 ++++---- .../pages}/address/MultichainAddressTxs.pw.tsx | 5 +++-- .../pages}/address/MultichainAddressTxs.tsx | 8 ++++---- ...ltichainAddress.pw.tsx_default_base-view-1.png | Bin ...sPortfolio.pw.tsx_default_nfts-base-view-1.png | Bin ...lio.pw.tsx_default_nfts-mobile-base-view-1.png | Bin ...pw.tsx_default_local-transfers-base-view-1.png | Bin ...default_local-transfers-mobile-base-view-1.png | Bin ...ssTxs.pw.tsx_default_local-txs-base-view-1.png | Bin ...w.tsx_default_local-txs-mobile-base-view-1.png | Bin .../pages/address/get-available-chain-ids.ts | 0 .../header/MultichainAddressEnsDomains.tsx | 0 .../portfolio/MultichainAddressPortfolioCard.tsx | 2 +- .../portfolio/MultichainAddressPortfolioCards.tsx | 2 +- .../MultichainAddressPortfolioNetWorth.tsx | 0 .../MultichainAddressPortfolioTokens.pw.tsx | 9 +++++---- .../MultichainAddressPortfolioTokens.tsx | 3 ++- .../portfolio/MultichainAddressTokensListItem.tsx | 0 .../portfolio/MultichainAddressTokensTable.tsx | 0 .../MultichainAddressTokensTableItem.tsx | 0 ...-color-mode_many-chains-mobile-dark-mode-1.png | Bin ...-color-mode_many-chains-mobile-dark-mode-2.png | Bin ...tsx_default_many-chains-mobile-dark-mode-1.png | Bin ...tsx_default_many-chains-mobile-dark-mode-2.png | Bin ...olioTokens.pw.tsx_default_zero-net-worth-1.png | Bin ....tsx_mobile_many-chains-mobile-dark-mode-1.png | Bin ....tsx_mobile_many-chains-mobile-dark-mode-2.png | Bin .../multichain/pages}/address/portfolio/utils.ts | 0 .../pages}/address/useChainSelectErc20.ts | 0 .../pages/addresses}/MultichainAccounts.tsx | 5 +++-- .../multichain/pages}/block/MultichainBlock.tsx | 3 ++- .../multichain/pages}/blocks/MultichainBlocks.tsx | 3 ++- .../pages}/blocks/MultichainBlocksContent.tsx | 3 ++- .../contracts}/MultichainVerifiedContracts.tsx | 5 +++-- .../pages}/ecosystems/MultichainEcosystems.pw.tsx | 5 +++-- .../pages}/ecosystems/MultichainEcosystems.tsx | 5 +++-- .../ecosystems/MultichainEcosystemsListItem.tsx | 2 +- .../ecosystems/MultichainEcosystemsTable.tsx | 5 +++-- .../ecosystems/MultichainEcosystemsTableItem.tsx | 2 +- ...osystems.pw.tsx_default_base-view-mobile-1.png | Bin ...cosystems.pw.tsx_mobile_base-view-mobile-1.png | Bin .../multichain/pages}/ecosystems/utils.ts | 2 +- .../multichain/pages}/home/ChainIndicators.tsx | 7 ++++--- .../multichain/pages}/home/ChainWidget.tsx | 2 +- .../features/multichain/pages}/home/LatestTxs.tsx | 7 ++++--- .../multichain/pages}/home/LatestTxsLocal.tsx | 3 ++- .../multichain/pages}/home/MultichainHome.pw.tsx | 7 ++++--- .../multichain/pages}/home/MultichainHome.tsx | 3 ++- .../features/multichain/pages}/home/Stats.tsx | 3 ++- ...rk-color-mode_base-view-mobile-dark-mode-1.png | Bin ...w.tsx_default_base-view-mobile-dark-mode-1.png | Bin ...pw.tsx_mobile_base-view-mobile-dark-mode-1.png | Bin .../multichain/pages}/home/useChartDataQuery.ts | 4 ++-- .../pages/internal-txs}/MultichainInternalTxs.tsx | 5 +++-- .../pages/search-results/SearchResults.pw.tsx | 2 +- .../pages/search-results/SearchResults.tsx | 5 +++-- .../items/SearchResultItemAddress.tsx | 2 +- .../items/SearchResultItemBlock.tsx | 2 +- .../items/SearchResultItemBlockNumber.tsx | 2 +- .../items/SearchResultItemToken.tsx | 5 +++-- .../search-results/items/SearchResultItemTx.tsx | 2 +- .../multichain/pages}/stats/MultichainStats.tsx | 7 +++---- .../token-instance}/MultichainTokenInstance.tsx | 3 ++- .../token-transfers}/MultichainTokenTransfers.tsx | 5 +++-- .../MultichainTokenTransfersLocal.tsx | 3 ++- .../multichain/pages}/token/MultichainToken.tsx | 3 ++- .../multichain/pages}/tokens/MultichainTokens.tsx | 5 +++-- .../multichain/pages}/tx/MultichainTx.tsx | 3 ++- .../multichain/pages}/txs/MultichainTxs.tsx | 7 ++++--- .../multichain/pages}/txs/MultichainTxsLocal.tsx | 3 ++- .../pages/user-ops}/MultichainUserOps.tsx | 4 ++-- .../features/multichain/stubs.ts | 2 +- .../features/multichain/types/client.ts | 12 ++++++++++++ .../features/multichain/utils}/contract.ts | 0 .../utils/get-chain-id-from-slug-or-id.ts | 0 .../utils/get-chain-value-from-query.ts | 0 .../domains/components/SearchBarSuggestDomain.tsx | 2 +- .../common/components/TxInterpretation.tsx | 2 +- .../user-ops/components/entity/UserOpEntity.tsx | 3 ++- .../user-ops/pages/details/UserOpSubHeading.tsx | 2 +- .../user-ops/pages/index/UserOpsListItem.tsx | 4 ++-- .../user-ops/pages/index/UserOpsTable.tsx | 3 ++- .../user-ops/pages/index/UserOpsTableItem.tsx | 2 +- client/shared/web3/useAddChain.tsx | 2 +- client/shared/web3/useProvider.tsx | 3 ++- client/shared/web3/useSwitchChain.tsx | 3 ++- .../address/hooks/useAddressCountersQuery.ts | 2 +- .../coin-balance/AddressCoinBalanceHistory.tsx | 3 ++- .../coin-balance/AddressCoinBalanceListItem.tsx | 2 +- .../coin-balance/AddressCoinBalanceTableItem.tsx | 2 +- .../details/info/token-select/TokenSelect.tsx | 3 ++- .../AddressTokenTransfersLocal.tsx | 2 +- .../useAddressTokenTransfersSocket.ts | 3 ++- .../block/components/entity/BlockEntity.tsx | 3 ++- .../pages/countdown-details/BlockCountdown.tsx | 3 ++- .../create-google-calendar-link.ts | 3 ++- .../countdown-details/create-ics-file-blob.ts | 3 ++- .../pages/countdown-index/BlockCountdownIndex.tsx | 3 ++- client/slices/block/pages/details/Block.tsx | 2 +- .../slices/block/pages/details/BlockDetails.tsx | 2 +- client/slices/block/pages/index/BlocksContent.tsx | 3 ++- client/slices/block/pages/index/BlocksList.tsx | 2 +- .../slices/block/pages/index/BlocksListItem.tsx | 2 +- client/slices/block/pages/index/BlocksTable.tsx | 2 +- .../slices/block/pages/index/BlocksTableItem.tsx | 2 +- .../contract/pages/details/code/ContractCode.tsx | 3 ++- .../code/ContractDetailsVerificationButton.tsx | 3 ++- .../pages/details/code/ContractSourceCode.tsx | 3 ++- .../details/code/info/ContractDetailsInfo.tsx | 2 +- .../details/methods/useCallMethodPublicClient.ts | 2 +- .../details/methods/useCallMethodWalletClient.ts | 2 +- .../contract/pages/details/useContractTabs.tsx | 2 +- .../pages/index/VerifiedContractsCounters.tsx | 3 ++- .../pages/index/VerifiedContractsTable.tsx | 3 ++- .../pages/index/VerifiedContractsTableItem.tsx | 2 +- client/slices/gas/components/GasPrice.tsx | 3 ++- .../internal-tx/components/InternalTxsList.tsx | 2 +- .../components/InternalTxsListItem.tsx | 2 +- .../internal-tx/components/InternalTxsTable.tsx | 3 ++- .../components/InternalTxsTableItem.tsx | 2 +- client/slices/log/components/LogItem.tsx | 2 +- .../SearchBarSuggest/SearchBarSuggestAddress.tsx | 5 +++-- .../SearchBarSuggest/SearchBarSuggestBlock.tsx | 2 +- .../SearchBarSuggest/SearchBarSuggestToken.tsx | 5 +++-- .../SearchBarSuggest/SearchBarSuggestTx.tsx | 2 +- .../search-bar/SearchBarSuggest/types.ts | 2 +- client/slices/search/types/client.ts | 2 +- .../components/TokenTransferFilter.tsx | 2 +- .../components/list/TokenTransferList.tsx | 2 +- .../components/list/TokenTransferListItem.tsx | 2 +- .../components/list/TokenTransferTable.tsx | 3 ++- .../components/list/TokenTransferTableItem.tsx | 2 +- .../pages/index/TokenTransfersListItem.tsx | 2 +- .../pages/index/TokenTransfersTable.tsx | 2 +- .../pages/index/TokenTransfersTableItem.tsx | 2 +- .../pages/token/TokenTransferList.tsx | 2 +- .../pages/token/TokenTransferListItem.tsx | 2 +- .../pages/token/TokenTransferTable.tsx | 3 ++- .../pages/token/TokenTransferTableItem.tsx | 2 +- .../token-transfer/pages/tx/TxTokenTransfer.tsx | 2 +- .../slices/token/components/TokenTypeFilter.tsx | 2 +- .../token/components/entity/TokenEntity.pw.tsx | 3 ++- .../token/components/entity/TokenEntity.tsx | 3 ++- .../token/pages/address/AddressCollections.tsx | 3 ++- client/slices/token/pages/address/AddressNFTs.tsx | 3 ++- client/slices/token/pages/address/NFTItem.tsx | 2 +- .../slices/token/pages/address/useFetchTokens.ts | 3 ++- .../slices/token/pages/details/TokenPageTitle.tsx | 2 +- .../token/pages/details/info/TokenDetails.tsx | 3 ++- .../details/inventory/TokenInventoryItem.tsx | 3 ++- .../slices/token/pages/index/TokensListItem.tsx | 2 +- client/slices/token/pages/index/TokensTable.tsx | 2 +- .../slices/token/pages/index/TokensTableItem.tsx | 2 +- .../pages/instance/TokenInstancePageTitle.tsx | 2 +- client/slices/token/utils/list-utils.ts | 2 +- client/slices/token/utils/token-types.ts | 2 +- .../tx/components/TxAdditionalInfoContent.tsx | 2 +- client/slices/tx/components/entity/TxEntity.tsx | 3 ++- client/slices/tx/hooks/useTxsSocketTypeAddress.ts | 3 ++- client/slices/tx/pages/details/TxSubHeading.tsx | 2 +- .../info/parts/TxDetailsTokenTransfers.tsx | 3 ++- client/slices/tx/pages/index/list/TxsList.tsx | 3 +-- client/slices/tx/pages/index/list/TxsListItem.tsx | 2 +- client/slices/tx/pages/index/list/TxsTable.tsx | 2 +- .../slices/tx/pages/index/list/TxsTableItem.tsx | 2 +- client/slices/tx/pages/index/list/TxsTabs.tsx | 2 +- .../index/list/socket/TxsSocketNoticeTypeAll.tsx | 3 ++- client/slices/tx/pages/index/stats/TxsStats.tsx | 3 ++- configs/app/features/multichain-button.ts | 2 +- configs/essential-dapps-chains/config.nodejs.ts | 2 +- configs/multichain/config.nodejs.ts | 2 +- configs/multichain/index.ts | 2 +- .../schemas/features/multichainButton.ts | 2 +- deploy/tools/multichain-config-generator/index.ts | 2 +- global.d.ts | 2 +- nextjs/csp/policies/multichain.ts | 2 +- pages/accounts/index.tsx | 2 +- pages/address/[hash]/index.tsx | 3 ++- pages/blocks.tsx | 2 +- .../[chain_slug_or_id]/accounts/label/[slug].tsx | 2 +- .../[chain_slug_or_id]/advanced-filter/index.tsx | 3 +-- .../[chain_slug_or_id]/block/[height_or_hash].tsx | 2 +- .../block/countdown/[height].tsx | 2 +- .../[chain_slug_or_id]/block/countdown/index.tsx | 2 +- pages/chain/[chain_slug_or_id]/op/[hash].tsx | 2 +- .../[chain_slug_or_id]/token/[hash]/index.tsx | 2 +- .../token/[hash]/instance/[id].tsx | 2 +- .../chain/[chain_slug_or_id]/tx/[hash]/index.tsx | 2 +- .../[chain_slug_or_id]/visualize/sol2uml.tsx | 3 ++- pages/ecosystems.tsx | 2 +- pages/index.tsx | 3 ++- pages/internal-txs.tsx | 2 +- pages/ops.tsx | 2 +- pages/stats/[id].tsx | 3 ++- pages/stats/index.tsx | 2 +- pages/token-transfers.tsx | 2 +- pages/tokens.tsx | 2 +- pages/txs/index.tsx | 2 +- pages/verified-contracts.tsx | 2 +- .../fixtures/mockEssentialDappsChainsConfig.ts | 2 +- playwright/fixtures/mockMultichainConfig.ts | 2 +- .../components/charts/components/ChartMenu.tsx | 3 ++- types/externalChains.ts | 2 +- types/multichain.ts | 14 -------------- ui/pages/Sol2Uml.tsx | 3 ++- ui/shared/EntityTags/EntityTag.tsx | 3 ++- ui/shared/EntityTags/utils.ts | 2 +- ui/shared/entities/base/utils.ts | 2 +- ui/shared/entities/nft/NftEntity.tsx | 3 ++- ui/shared/pagination/useQueryWithPages.ts | 5 +++-- ui/shared/reCaptcha/ReCaptcha.tsx | 3 ++- 256 files changed, 376 insertions(+), 292 deletions(-) rename types/client/multichainProviderConfig.ts => client/features/multichain-button/types/client.ts (100%) rename {ui => client/features}/multichain/components/ChainSelect.tsx (100%) rename {ui => client/features}/multichain/components/ClusterChainsPopover.tsx (100%) rename {ui => client/features}/multichain/components/ListCounterText.tsx (100%) rename {ui => client/features}/multichain/components/NativeTokenIcon.tsx (94%) rename lib/contexts/multichain.tsx => client/features/multichain/context.tsx (92%) rename ui/multichain/home/useFetchParentChainApi.tsx => client/features/multichain/hooks/useFetchParentChainApi.ts (100%) rename {lib/multichain => client/features/multichain/hooks}/useRoutedChainSelect.ts (93%) rename {mocks/multichain => client/features/multichain/mocks}/address.ts (100%) rename {mocks/multichain => client/features/multichain/mocks}/chains.ts (97%) rename {mocks/multichain => client/features/multichain/mocks}/metrics.ts (100%) rename {mocks/multichain => client/features/multichain/mocks}/portfolio.ts (100%) rename {mocks/multichain => client/features/multichain/mocks}/stats.ts (100%) rename {mocks/multichain => client/features/multichain/mocks}/tokens.ts (97%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddress.pw.tsx (87%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddress.tsx (96%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressCoinBalanceHistory.tsx (84%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressContract.tsx (88%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressInternalTxs.tsx (94%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressLogs.tsx (93%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressPortfolio.pw.tsx (92%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressPortfolio.tsx (96%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressTokenTransfers.pw.tsx (93%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressTokenTransfers.tsx (96%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressTxs.pw.tsx (93%) rename {ui/multichain => client/features/multichain/pages}/address/MultichainAddressTxs.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png (100%) rename ui/multichain/address/getAvailableChainIds.ts => client/features/multichain/pages/address/get-available-chain-ids.ts (100%) rename {ui/multichain => client/features/multichain/pages}/address/header/MultichainAddressEnsDomains.tsx (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressPortfolioCard.tsx (97%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressPortfolioCards.tsx (97%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressPortfolioNetWorth.tsx (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx (91%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressPortfolioTokens.tsx (99%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressTokensListItem.tsx (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressTokensTable.tsx (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/MultichainAddressTokensTableItem.tsx (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png (100%) rename {ui/multichain => client/features/multichain/pages}/address/portfolio/utils.ts (100%) rename {ui/multichain => client/features/multichain/pages}/address/useChainSelectErc20.ts (100%) rename {ui/multichain/accounts => client/features/multichain/pages/addresses}/MultichainAccounts.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/block/MultichainBlock.tsx (90%) rename {ui/multichain => client/features/multichain/pages}/blocks/MultichainBlocks.tsx (98%) rename {ui/multichain => client/features/multichain/pages}/blocks/MultichainBlocksContent.tsx (93%) rename {ui/multichain/verifiedContracts => client/features/multichain/pages/contracts}/MultichainVerifiedContracts.tsx (96%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/MultichainEcosystems.pw.tsx (85%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/MultichainEcosystems.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/MultichainEcosystemsListItem.tsx (98%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/MultichainEcosystemsTable.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/MultichainEcosystemsTableItem.tsx (98%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/ecosystems/utils.ts (95%) rename {ui/multichain => client/features/multichain/pages}/home/ChainIndicators.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/home/ChainWidget.tsx (98%) rename {ui/multichain => client/features/multichain/pages}/home/LatestTxs.tsx (86%) rename {ui/multichain => client/features/multichain/pages}/home/LatestTxsLocal.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/home/MultichainHome.pw.tsx (88%) rename {ui/multichain => client/features/multichain/pages}/home/MultichainHome.tsx (95%) rename {ui/multichain => client/features/multichain/pages}/home/Stats.tsx (97%) rename {ui/multichain => client/features/multichain/pages}/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png (100%) rename {ui/multichain => client/features/multichain/pages}/home/useChartDataQuery.ts (94%) rename {ui/multichain/internalTxs => client/features/multichain/pages/internal-txs}/MultichainInternalTxs.tsx (94%) rename {ui/multichain => client/features/multichain/pages}/stats/MultichainStats.tsx (91%) rename {ui/multichain/tokenInstance => client/features/multichain/pages/token-instance}/MultichainTokenInstance.tsx (92%) rename {ui/multichain/tokenTransfers => client/features/multichain/pages/token-transfers}/MultichainTokenTransfers.tsx (96%) rename {ui/multichain/tokenTransfers => client/features/multichain/pages/token-transfers}/MultichainTokenTransfersLocal.tsx (97%) rename {ui/multichain => client/features/multichain/pages}/token/MultichainToken.tsx (92%) rename {ui/multichain => client/features/multichain/pages}/tokens/MultichainTokens.tsx (97%) rename {ui/multichain => client/features/multichain/pages}/tx/MultichainTx.tsx (90%) rename {ui/multichain => client/features/multichain/pages}/txs/MultichainTxs.tsx (89%) rename {ui/multichain => client/features/multichain/pages}/txs/MultichainTxsLocal.tsx (94%) rename {ui/multichain/userOps => client/features/multichain/pages/user-ops}/MultichainUserOps.tsx (95%) rename stubs/multichain.ts => client/features/multichain/stubs.ts (97%) rename types/client/multichainAggregator.ts => client/features/multichain/types/client.ts (92%) rename {lib/multichain => client/features/multichain/utils}/contract.ts (100%) rename lib/multichain/getChainIdFromSlugOrId.ts => client/features/multichain/utils/get-chain-id-from-slug-or-id.ts (100%) rename lib/multichain/getChainValueFromQuery.ts => client/features/multichain/utils/get-chain-value-from-query.ts (100%) delete mode 100644 types/multichain.ts diff --git a/client/api/hooks/useApiQuery.ts b/client/api/hooks/useApiQuery.ts index 24ea047fd0..b9476bf159 100644 --- a/client/api/hooks/useApiQuery.ts +++ b/client/api/hooks/useApiQuery.ts @@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import type { ExternalChainExtended } from 'types/externalChains'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; import type { ResourceError, ResourceName, ResourcePathParams, ResourcePayload } from '../resources'; import useApiFetch from './useApiFetch'; diff --git a/client/api/services/multichain-aggregator.ts b/client/api/services/multichain-aggregator.ts index 284aa9a718..e6b79602d8 100644 --- a/client/api/services/multichain-aggregator.ts +++ b/client/api/services/multichain-aggregator.ts @@ -2,7 +2,7 @@ import type { ApiResource } from '../types'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { AddressTokensFilter, AddressTokensResponse, TokensResponse } from 'types/client/multichainAggregator'; +import type { AddressTokensFilter, AddressTokensResponse, TokensResponse } from 'client/features/multichain/types/client'; export const MULTICHAIN_AGGREGATOR_API_RESOURCES = { address: { diff --git a/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts index 55f95fe594..46e9b9bf5e 100644 --- a/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts +++ b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts @@ -4,8 +4,9 @@ import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from ' import useApiQuery from 'client/api/hooks/useApiQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import parseMetaPayload from '../utils/parse-meta-payload'; diff --git a/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx b/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx index 86e81957dd..ef1fdd16f0 100644 --- a/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx +++ b/client/features/advanced-filter/components/AddressAdvancedFilterLink.tsx @@ -3,14 +3,15 @@ import { pickBy } from 'es-toolkit'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressFromToFilter } from 'client/slices/address/types/api'; import type { TokenType } from 'client/slices/token/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; + +import { useMultichainContext } from 'client/features/multichain/context'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { getAdvancedFilterTypes } from '../utils/lib'; import AdvancedFilterLink from './AdvancedFilterLink'; diff --git a/client/features/advanced-filter/components/ItemByColumn.tsx b/client/features/advanced-filter/components/ItemByColumn.tsx index 0eabd403f9..3f4f4d2af8 100644 --- a/client/features/advanced-filter/components/ItemByColumn.tsx +++ b/client/features/advanced-filter/components/ItemByColumn.tsx @@ -5,8 +5,8 @@ import React from 'react'; import type { AdvancedFilterResponseItem } from '../types/api'; import type { ColumnsIds } from '../types/client'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { isConfidentialTokenType } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; diff --git a/client/features/advanced-filter/components/filters/TypeFilter.tsx b/client/features/advanced-filter/components/filters/TypeFilter.tsx index 300a36d380..c0850da19b 100644 --- a/client/features/advanced-filter/components/filters/TypeFilter.tsx +++ b/client/features/advanced-filter/components/filters/TypeFilter.tsx @@ -6,7 +6,8 @@ import React from 'react'; import type { AdvancedFilterParams, AdvancedFilterType } from '../../types/api'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; diff --git a/client/features/advanced-filter/pages/index/AdvancedFilter.tsx b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx index bbeb0d5ef3..6f1f3b0ef7 100644 --- a/client/features/advanced-filter/pages/index/AdvancedFilter.tsx +++ b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx @@ -20,13 +20,13 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import { useMultichainContext } from 'client/features/multichain/context'; import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from-query'; -import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; diff --git a/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx b/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx index 94344f423d..afc1d9acb9 100644 --- a/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx +++ b/client/features/advanced-filter/pages/token/TokenAdvancedFilterLink.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type { TokenInfo } from 'client/slices/token/types/api'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import type { LinkProps } from 'toolkit/chakra/link'; import AdvancedFilterLink from '../../components/AdvancedFilterLink'; diff --git a/client/features/advanced-filter/utils/lib.ts b/client/features/advanced-filter/utils/lib.ts index 927faa8131..bd4c4b00ee 100644 --- a/client/features/advanced-filter/utils/lib.ts +++ b/client/features/advanced-filter/utils/lib.ts @@ -3,8 +3,8 @@ import { castArray } from 'es-toolkit/compat'; import type { AdvancedFilterAge, AdvancedFilterParams } from '../types/api'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { getTokenTypes } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import dayjs from 'lib/date/dayjs'; import { HOUR, DAY, MONTH } from 'toolkit/utils/consts'; diff --git a/client/features/chain-stats/hooks/useChainStats.tsx b/client/features/chain-stats/hooks/useChainStats.tsx index 3b593705e3..5a95fe2bda 100644 --- a/client/features/chain-stats/hooks/useChainStats.tsx +++ b/client/features/chain-stats/hooks/useChainStats.tsx @@ -5,7 +5,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import type { ChainStatsChart, ChainStatsPayload, ChainStatsSection, StatsIntervalIds } from '../types/client'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/client/features/chain-stats/mocks/cross-chain-txs-paths.ts b/client/features/chain-stats/mocks/cross-chain-txs-paths.ts index ad446ae490..ac4142cb59 100644 --- a/client/features/chain-stats/mocks/cross-chain-txs-paths.ts +++ b/client/features/chain-stats/mocks/cross-chain-txs-paths.ts @@ -1,6 +1,6 @@ import type { GetMessagePathsResponse } from '@blockscout/interchain-indexer-types'; -import { chainA, chainB, chainC, chainD } from 'mocks/multichain/chains'; +import { chainA, chainB, chainC, chainD } from 'client/features/multichain/mocks/chains'; export const incomingMessagesPaths: GetMessagePathsResponse = { items: [ diff --git a/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx b/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx index 6032dfaa92..afedd6c008 100644 --- a/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx +++ b/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx @@ -3,8 +3,8 @@ import React from 'react'; import { ChartResolution } from 'toolkit/components/charts/types'; import { CROSS_CHAIN_TXS_CHARTS } from 'client/features/cross-chain-txs/utils/chain-stats'; +import * as chainsMock from 'client/features/multichain/mocks/chains'; -import * as chainsMock from 'mocks/multichain/chains'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx index 4555bc03e0..9d02907927 100644 --- a/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx +++ b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx @@ -8,10 +8,12 @@ import type { LineChartInfo } from '@blockscout/stats-types'; import type { LineChartItem } from 'toolkit/components/charts/line/types'; import { CHART_RESOLUTION_LABELS, type ChartResolution } from 'toolkit/components/charts/types'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { useMultichainContext } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import type { OnValueChangeHandler, SelectOption } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select'; import { Skeleton } from 'toolkit/chakra/skeleton'; @@ -19,7 +21,6 @@ import { ChartResetZoomButton } from 'toolkit/components/charts/components/Chart import { LineChartContent } from 'toolkit/components/charts/line/LineChartContent'; import LineChartMenu from 'toolkit/components/charts/line/parts/LineChartMenu'; import { useLineChartZoom } from 'toolkit/components/charts/line/utils/useLineChartZoom'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import { useChartsConfig } from 'ui/shared/chart/config'; import ChartIntervalSelect from '../../components/ChartIntervalSelect'; diff --git a/client/features/chain-stats/pages/index/ChainStatsSections.tsx b/client/features/chain-stats/pages/index/ChainStatsSections.tsx index d972d96924..d249fc80d3 100644 --- a/client/features/chain-stats/pages/index/ChainStatsSections.tsx +++ b/client/features/chain-stats/pages/index/ChainStatsSections.tsx @@ -11,9 +11,9 @@ import GasInfoTooltip from 'client/slices/gas/components/GasInfoTooltip'; import ChartWidgetContainerCrossChain from 'client/features/cross-chain-txs/components/ChartWidgetContainerCrossChain'; import { CROSS_CHAIN_TXS_CHARTS } from 'client/features/cross-chain-txs/utils/chain-stats'; +import { useMultichainContext } from 'client/features/multichain/context'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { EmptyState } from 'toolkit/chakra/empty-state'; import { Heading } from 'toolkit/chakra/heading'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/csv-export/components/CsvExport.tsx b/client/features/csv-export/components/CsvExport.tsx index 3f7bf17e55..79e30e0e7f 100644 --- a/client/features/csv-export/components/CsvExport.tsx +++ b/client/features/csv-export/components/CsvExport.tsx @@ -8,8 +8,8 @@ import React from 'react'; import type { CsvExportDownloadResponse } from '../types/api'; import type { CsvExportType } from '../types/client'; import type { FormFields } from './dialog/types'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { NextJsQueryParam } from 'client/shared/router/types'; -import type { ClusterChainConfig } from 'types/multichain'; import buildUrl from 'client/api/build-url'; import useApiFetch from 'client/api/hooks/useApiFetch'; @@ -17,13 +17,14 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import isNeedProxy from 'client/api/is-need-proxy'; import type { ResourceName, ResourcePathParams } from 'client/api/resources'; +import { useMultichainContext } from 'client/features/multichain/context'; + import getErrorMessage from 'client/shared/errors/get-error-message'; import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; import { IconButton } from 'toolkit/chakra/icon-button'; import { toaster } from 'toolkit/chakra/toaster'; diff --git a/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx b/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx index eba5add3d4..9e6299cc51 100644 --- a/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx +++ b/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx @@ -4,7 +4,7 @@ import { chakra, Flex, Text } from '@chakra-ui/react'; import React from 'react'; import type { CsvExportType } from '../../types/client'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import shortenString from 'client/shared/text/shorten-string'; diff --git a/client/features/flashblocks/components/FlashblockEntity.tsx b/client/features/flashblocks/components/FlashblockEntity.tsx index d42df774ea..949df75969 100644 --- a/client/features/flashblocks/components/FlashblockEntity.tsx +++ b/client/features/flashblocks/components/FlashblockEntity.tsx @@ -5,7 +5,8 @@ import React from 'react'; import { route } from 'nextjs/routes'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as EntityBase from 'ui/shared/entities/base/components'; import { distributeEntityProps } from 'ui/shared/entities/base/utils'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx b/client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx index 8fc5ece56e..f7d7d11226 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/Revoke.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; + import { test, expect } from 'playwright/lib'; import Revoke from './Revoke'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx index d608de7a44..a8b4c7b6cd 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx @@ -3,8 +3,8 @@ import React from 'react'; import { hash as addressHash } from 'client/slices/address/mocks/address'; import * as revokeMock from 'client/features/marketplace/mocks/revoke-dapp'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; -import * as chainDataMock from 'mocks/multichain/chains'; import type { TestFnArgs } from 'playwright/lib'; import { test, expect } from 'playwright/lib'; diff --git a/client/features/multichain-button/pages/address/AddressMultichainButton.tsx b/client/features/multichain-button/pages/address/AddressMultichainButton.tsx index eead8ed99b..5bd780c1bc 100644 --- a/client/features/multichain-button/pages/address/AddressMultichainButton.tsx +++ b/client/features/multichain-button/pages/address/AddressMultichainButton.tsx @@ -3,7 +3,7 @@ import { upperFirst } from 'es-toolkit'; import React from 'react'; -import type { MultichainProviderConfigParsed } from 'types/client/multichainProviderConfig'; +import type { MultichainProviderConfigParsed } from 'client/features/multichain-button/types/client'; import { route } from 'nextjs-routes'; diff --git a/types/client/multichainProviderConfig.ts b/client/features/multichain-button/types/client.ts similarity index 100% rename from types/client/multichainProviderConfig.ts rename to client/features/multichain-button/types/client.ts diff --git a/ui/multichain/components/ChainSelect.tsx b/client/features/multichain/components/ChainSelect.tsx similarity index 100% rename from ui/multichain/components/ChainSelect.tsx rename to client/features/multichain/components/ChainSelect.tsx diff --git a/ui/multichain/components/ClusterChainsPopover.tsx b/client/features/multichain/components/ClusterChainsPopover.tsx similarity index 100% rename from ui/multichain/components/ClusterChainsPopover.tsx rename to client/features/multichain/components/ClusterChainsPopover.tsx diff --git a/ui/multichain/components/ListCounterText.tsx b/client/features/multichain/components/ListCounterText.tsx similarity index 100% rename from ui/multichain/components/ListCounterText.tsx rename to client/features/multichain/components/ListCounterText.tsx diff --git a/ui/multichain/components/NativeTokenIcon.tsx b/client/features/multichain/components/NativeTokenIcon.tsx similarity index 94% rename from ui/multichain/components/NativeTokenIcon.tsx rename to client/features/multichain/components/NativeTokenIcon.tsx index 91da366b61..deedd0f97c 100644 --- a/ui/multichain/components/NativeTokenIcon.tsx +++ b/client/features/multichain/components/NativeTokenIcon.tsx @@ -11,7 +11,7 @@ import TokenLogoPlaceholder from 'client/slices/token/components/icon/TokenIconP import { Image } from 'toolkit/chakra/image'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import useFetchParentChainApi from '../home/useFetchParentChainApi'; +import useFetchParentChainApi from '../hooks/useFetchParentChainApi'; interface Props { isLoading?: boolean; diff --git a/lib/contexts/multichain.tsx b/client/features/multichain/context.tsx similarity index 92% rename from lib/contexts/multichain.tsx rename to client/features/multichain/context.tsx index b027318884..a2894ad391 100644 --- a/lib/contexts/multichain.tsx +++ b/client/features/multichain/context.tsx @@ -3,12 +3,13 @@ import { useRouter } from 'next/router'; import React from 'react'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import getChainIdFromSlugOrId from 'lib/multichain/getChainIdFromSlugOrId'; + +import getChainIdFromSlugOrId from './utils/get-chain-id-from-slug-or-id'; interface MultichainProviderProps { children: React.ReactNode; diff --git a/ui/multichain/home/useFetchParentChainApi.tsx b/client/features/multichain/hooks/useFetchParentChainApi.ts similarity index 100% rename from ui/multichain/home/useFetchParentChainApi.tsx rename to client/features/multichain/hooks/useFetchParentChainApi.ts diff --git a/lib/multichain/useRoutedChainSelect.ts b/client/features/multichain/hooks/useRoutedChainSelect.ts similarity index 93% rename from lib/multichain/useRoutedChainSelect.ts rename to client/features/multichain/hooks/useRoutedChainSelect.ts index 264d3676a2..4b1c4744c6 100644 --- a/lib/multichain/useRoutedChainSelect.ts +++ b/client/features/multichain/hooks/useRoutedChainSelect.ts @@ -4,7 +4,7 @@ import { pickBy } from 'es-toolkit'; import { useRouter } from 'next/router'; import React from 'react'; -import getChainValueFromQuery from './getChainValueFromQuery'; +import getChainValueFromQuery from 'client/features/multichain/utils/get-chain-value-from-query'; interface Props { persistedParams?: Array; diff --git a/client/features/multichain/hooks/useSearchMultichain.ts b/client/features/multichain/hooks/useSearchMultichain.ts index a86b345cc6..3b88cff855 100644 --- a/client/features/multichain/hooks/useSearchMultichain.ts +++ b/client/features/multichain/hooks/useSearchMultichain.ts @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type * as bens from '@blockscout/bens-types'; +import type { QuickSearchResultBlock, QuickSearchResultToken } from 'client/features/multichain/types/client'; import type { QuickSearchResultItem } from 'client/slices/search/types/client'; -import type { QuickSearchResultBlock, QuickSearchResultToken } from 'types/client/multichainAggregator'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/mocks/multichain/address.ts b/client/features/multichain/mocks/address.ts similarity index 100% rename from mocks/multichain/address.ts rename to client/features/multichain/mocks/address.ts diff --git a/mocks/multichain/chains.ts b/client/features/multichain/mocks/chains.ts similarity index 97% rename from mocks/multichain/chains.ts rename to client/features/multichain/mocks/chains.ts index b1a5dd3c49..a04248b9ed 100644 --- a/mocks/multichain/chains.ts +++ b/client/features/multichain/mocks/chains.ts @@ -1,4 +1,4 @@ -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; export const chainA = { slug: 'op-mainnet', diff --git a/mocks/multichain/metrics.ts b/client/features/multichain/mocks/metrics.ts similarity index 100% rename from mocks/multichain/metrics.ts rename to client/features/multichain/mocks/metrics.ts diff --git a/mocks/multichain/portfolio.ts b/client/features/multichain/mocks/portfolio.ts similarity index 100% rename from mocks/multichain/portfolio.ts rename to client/features/multichain/mocks/portfolio.ts diff --git a/client/features/multichain/mocks/search.ts b/client/features/multichain/mocks/search.ts index 96b351d88a..d3f851b3a2 100644 --- a/client/features/multichain/mocks/search.ts +++ b/client/features/multichain/mocks/search.ts @@ -1,6 +1,6 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; -import { chainA } from 'mocks/multichain/chains'; +import { chainA } from 'client/features/multichain/mocks/chains'; export const searchAddressesA: multichain.GetAddressResponse = { hash: '0x0000000002C5fE54822a1eD058AE2F937Fd42769', diff --git a/mocks/multichain/stats.ts b/client/features/multichain/mocks/stats.ts similarity index 100% rename from mocks/multichain/stats.ts rename to client/features/multichain/mocks/stats.ts diff --git a/mocks/multichain/tokens.ts b/client/features/multichain/mocks/tokens.ts similarity index 97% rename from mocks/multichain/tokens.ts rename to client/features/multichain/mocks/tokens.ts index d51ca4d039..dc8ae28a24 100644 --- a/mocks/multichain/tokens.ts +++ b/client/features/multichain/mocks/tokens.ts @@ -1,4 +1,4 @@ -import type { AddressTokenItem } from 'types/client/multichainAggregator'; +import type { AddressTokenItem } from 'client/features/multichain/types/client'; import { erc7984Token } from 'client/features/fhe-operations/mocks/token'; diff --git a/ui/multichain/address/MultichainAddress.pw.tsx b/client/features/multichain/pages/address/MultichainAddress.pw.tsx similarity index 87% rename from ui/multichain/address/MultichainAddress.pw.tsx rename to client/features/multichain/pages/address/MultichainAddress.pw.tsx index 25e4a6fa84..7b723d11e6 100644 --- a/ui/multichain/address/MultichainAddress.pw.tsx +++ b/client/features/multichain/pages/address/MultichainAddress.pw.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import * as addressMock from 'mocks/multichain/address'; -import * as chainDataMock from 'mocks/multichain/chains'; -import * as portfolioMock from 'mocks/multichain/portfolio'; -import * as tokensMock from 'mocks/multichain/tokens'; +import * as addressMock from 'client/features/multichain/mocks/address'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; +import * as portfolioMock from 'client/features/multichain/mocks/portfolio'; +import * as tokensMock from 'client/features/multichain/mocks/tokens'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/MultichainAddress.tsx b/client/features/multichain/pages/address/MultichainAddress.tsx similarity index 96% rename from ui/multichain/address/MultichainAddress.tsx rename to client/features/multichain/pages/address/MultichainAddress.tsx index 8eea9cdd29..9a4c5df753 100644 --- a/ui/multichain/address/MultichainAddress.tsx +++ b/client/features/multichain/pages/address/MultichainAddress.tsx @@ -13,16 +13,16 @@ import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCod import getCheckedSummedAddress from 'client/slices/address/utils/get-checked-summed-address'; import { CONTRACT_TAB_IDS } from 'client/slices/contract/utils/tabs'; +import ClusterChainsPopover from 'client/features/multichain/components/ClusterChainsPopover'; +import { ADDRESS } from 'client/features/multichain/stubs'; +import * as contract from 'client/features/multichain/utils/contract'; import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import * as contract from 'lib/multichain/contract'; -import { ADDRESS } from 'stubs/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ClusterChainsPopover from 'ui/multichain/components/ClusterChainsPopover'; import TextAd from 'ui/shared/ad/TextAd'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx b/client/features/multichain/pages/address/MultichainAddressCoinBalanceHistory.tsx similarity index 84% rename from ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx rename to client/features/multichain/pages/address/MultichainAddressCoinBalanceHistory.tsx index 12710827cb..7bc8bb87d0 100644 --- a/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx +++ b/client/features/multichain/pages/address/MultichainAddressCoinBalanceHistory.tsx @@ -9,12 +9,13 @@ import { SocketProvider } from 'client/api/socket/context'; import AddressCoinBalance from 'client/slices/address/pages/details/coin-balance/AddressCoinBalance'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; const QUERY_PRESERVED_PARAMS = [ 'tab', 'hash' ]; diff --git a/ui/multichain/address/MultichainAddressContract.tsx b/client/features/multichain/pages/address/MultichainAddressContract.tsx similarity index 88% rename from ui/multichain/address/MultichainAddressContract.tsx rename to client/features/multichain/pages/address/MultichainAddressContract.tsx index d46e75cf9e..f60bcf2301 100644 --- a/ui/multichain/address/MultichainAddressContract.tsx +++ b/client/features/multichain/pages/address/MultichainAddressContract.tsx @@ -11,14 +11,15 @@ import { SocketProvider } from 'client/api/socket/context'; import { ADDRESS_INFO } from 'client/slices/address/stubs/address'; import Contract from 'client/slices/contract/pages/details/Contract'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; const LEFT_SLOT_PROPS = { mr: 6, diff --git a/ui/multichain/address/MultichainAddressInternalTxs.tsx b/client/features/multichain/pages/address/MultichainAddressInternalTxs.tsx similarity index 94% rename from ui/multichain/address/MultichainAddressInternalTxs.tsx rename to client/features/multichain/pages/address/MultichainAddressInternalTxs.tsx index 8c2d7b5e16..246ea8f268 100644 --- a/ui/multichain/address/MultichainAddressInternalTxs.tsx +++ b/client/features/multichain/pages/address/MultichainAddressInternalTxs.tsx @@ -11,15 +11,15 @@ import InternalTxsList from 'client/slices/internal-tx/components/InternalTxsLis import InternalTxsTable from 'client/slices/internal-tx/components/InternalTxsTable'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; interface Props { addressData: multichain.GetAddressResponse | undefined; diff --git a/ui/multichain/address/MultichainAddressLogs.tsx b/client/features/multichain/pages/address/MultichainAddressLogs.tsx similarity index 93% rename from ui/multichain/address/MultichainAddressLogs.tsx rename to client/features/multichain/pages/address/MultichainAddressLogs.tsx index ef373eb725..cd6ffe8c98 100644 --- a/ui/multichain/address/MultichainAddressLogs.tsx +++ b/client/features/multichain/pages/address/MultichainAddressLogs.tsx @@ -9,20 +9,20 @@ import LogItem from 'client/slices/log/components/LogItem'; import { LOG } from 'client/slices/log/stubs/log'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { generateListStub } from 'stubs/utils'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; interface Props { addressData: multichain.GetAddressResponse | undefined; diff --git a/ui/multichain/address/MultichainAddressPortfolio.pw.tsx b/client/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx similarity index 92% rename from ui/multichain/address/MultichainAddressPortfolio.pw.tsx rename to client/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx index b9b9b13a2f..82c6ef9fed 100644 --- a/ui/multichain/address/MultichainAddressPortfolio.pw.tsx +++ b/client/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx @@ -4,8 +4,9 @@ import React from 'react'; import * as tokensMock from 'client/slices/token/mocks/address-tokens'; -import * as addressMock from 'mocks/multichain/address'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as addressMock from 'client/features/multichain/mocks/address'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/MultichainAddressPortfolio.tsx b/client/features/multichain/pages/address/MultichainAddressPortfolio.tsx similarity index 96% rename from ui/multichain/address/MultichainAddressPortfolio.tsx rename to client/features/multichain/pages/address/MultichainAddressPortfolio.tsx index d1b3227aca..9b65c98d16 100644 --- a/ui/multichain/address/MultichainAddressPortfolio.tsx +++ b/client/features/multichain/pages/address/MultichainAddressPortfolio.tsx @@ -13,15 +13,16 @@ import AddressNFTs from 'client/slices/token/pages/address/AddressNFTs'; import AddressNftTypeFilter from 'client/slices/token/pages/address/AddressNftTypeFilter'; import useAddressNftQuery from 'client/slices/token/pages/address/useAddressNftQuery'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { MultichainProvider } from 'lib/contexts/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import Pagination from 'ui/shared/pagination/Pagination'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; import MultichainAddressPortfolioTokens from './portfolio/MultichainAddressPortfolioTokens'; export const ADDRESS_OP_PORTFOLIO_TAB_IDS = [ 'portfolio_tokens' as const, 'portfolio_nfts' as const ]; diff --git a/ui/multichain/address/MultichainAddressTokenTransfers.pw.tsx b/client/features/multichain/pages/address/MultichainAddressTokenTransfers.pw.tsx similarity index 93% rename from ui/multichain/address/MultichainAddressTokenTransfers.pw.tsx rename to client/features/multichain/pages/address/MultichainAddressTokenTransfers.pw.tsx index b0b9a68750..94d7ae5a57 100644 --- a/ui/multichain/address/MultichainAddressTokenTransfers.pw.tsx +++ b/client/features/multichain/pages/address/MultichainAddressTokenTransfers.pw.tsx @@ -5,8 +5,9 @@ import React from 'react'; import * as countersMock from 'client/slices/address/mocks/counters'; import * as tokenTransferMock from 'client/slices/token-transfer/mocks'; -import * as addressMock from 'mocks/multichain/address'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as addressMock from 'client/features/multichain/mocks/address'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/MultichainAddressTokenTransfers.tsx b/client/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx similarity index 96% rename from ui/multichain/address/MultichainAddressTokenTransfers.tsx rename to client/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx index beddc0d305..f3401d5021 100644 --- a/ui/multichain/address/MultichainAddressTokenTransfers.tsx +++ b/client/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx @@ -18,19 +18,19 @@ import { getTokenFilterValue } from 'client/slices/token/utils/list-utils'; import AddressAdvancedFilterLink from 'client/features/advanced-filter/components/AddressAdvancedFilterLink'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import ListCounterText from 'client/features/multichain/components/ListCounterText'; +import { MultichainProvider } from 'client/features/multichain/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import Pagination from 'ui/shared/pagination/Pagination'; -import ListCounterText from '../components/ListCounterText'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; export const ADDRESS_MULTICHAIN_TOKEN_TRANSFERS_TAB_IDS = [ 'token_transfers_cross_chain' as const, 'token_transfers_local' as const ]; const TABS_RIGHT_SLOT_PROPS = { diff --git a/ui/multichain/address/MultichainAddressTxs.pw.tsx b/client/features/multichain/pages/address/MultichainAddressTxs.pw.tsx similarity index 93% rename from ui/multichain/address/MultichainAddressTxs.pw.tsx rename to client/features/multichain/pages/address/MultichainAddressTxs.pw.tsx index 86f603ae53..1d568d87e3 100644 --- a/ui/multichain/address/MultichainAddressTxs.pw.tsx +++ b/client/features/multichain/pages/address/MultichainAddressTxs.pw.tsx @@ -5,8 +5,9 @@ import React from 'react'; import * as countersMock from 'client/slices/address/mocks/counters'; import * as txMock from 'client/slices/tx/mocks/tx'; -import * as addressMock from 'mocks/multichain/address'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as addressMock from 'client/features/multichain/mocks/address'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/MultichainAddressTxs.tsx b/client/features/multichain/pages/address/MultichainAddressTxs.tsx similarity index 95% rename from ui/multichain/address/MultichainAddressTxs.tsx rename to client/features/multichain/pages/address/MultichainAddressTxs.tsx index 1e3582ea46..066a8e4fa1 100644 --- a/ui/multichain/address/MultichainAddressTxs.tsx +++ b/client/features/multichain/pages/address/MultichainAddressTxs.tsx @@ -16,20 +16,20 @@ import useAddressTxsQuery from 'client/slices/address/pages/details/txs/useAddre import TxsWithApiSorting from 'client/slices/tx/pages/index/list/TxsWithApiSorting'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import ListCounterText from 'client/features/multichain/components/ListCounterText'; +import { MultichainProvider } from 'client/features/multichain/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; -import ListCounterText from '../components/ListCounterText'; -import getAvailableChainIds from './getAvailableChainIds'; +import getAvailableChainIds from './get-available-chain-ids'; export const ADDRESS_MULTICHAIN_TXS_TAB_IDS = [ 'txs_cross_chain' as const, 'txs_local' as const ]; const TAB_LIST_PROPS = { diff --git a/ui/multichain/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png diff --git a/ui/multichain/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png b/client/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png similarity index 100% rename from ui/multichain/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png rename to client/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png diff --git a/ui/multichain/address/getAvailableChainIds.ts b/client/features/multichain/pages/address/get-available-chain-ids.ts similarity index 100% rename from ui/multichain/address/getAvailableChainIds.ts rename to client/features/multichain/pages/address/get-available-chain-ids.ts diff --git a/ui/multichain/address/header/MultichainAddressEnsDomains.tsx b/client/features/multichain/pages/address/header/MultichainAddressEnsDomains.tsx similarity index 100% rename from ui/multichain/address/header/MultichainAddressEnsDomains.tsx rename to client/features/multichain/pages/address/header/MultichainAddressEnsDomains.tsx diff --git a/ui/multichain/address/portfolio/MultichainAddressPortfolioCard.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx similarity index 97% rename from ui/multichain/address/portfolio/MultichainAddressPortfolioCard.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx index 8d77f51ff7..afd2a84c7a 100644 --- a/ui/multichain/address/portfolio/MultichainAddressPortfolioCard.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx @@ -5,7 +5,7 @@ import type BigNumber from 'bignumber.js'; import { clamp } from 'es-toolkit'; import React from 'react'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; diff --git a/ui/multichain/address/portfolio/MultichainAddressPortfolioCards.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCards.tsx similarity index 97% rename from ui/multichain/address/portfolio/MultichainAddressPortfolioCards.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCards.tsx index 91a4997b73..2b88e9a1d6 100644 --- a/ui/multichain/address/portfolio/MultichainAddressPortfolioCards.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCards.tsx @@ -6,7 +6,7 @@ import { clamp } from 'es-toolkit'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import useIsMobile from 'client/shared/hooks/useIsMobile'; diff --git a/ui/multichain/address/portfolio/MultichainAddressPortfolioNetWorth.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx similarity index 100% rename from ui/multichain/address/portfolio/MultichainAddressPortfolioNetWorth.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx diff --git a/ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx similarity index 91% rename from ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx index c30a0e45f0..e1ba28e7df 100644 --- a/ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.pw.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import * as addressMock from 'mocks/multichain/address'; -import * as chainDataMock from 'mocks/multichain/chains'; -import * as portfolioMock from 'mocks/multichain/portfolio'; -import * as tokensMock from 'mocks/multichain/tokens'; +import * as addressMock from 'client/features/multichain/mocks/address'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; +import * as portfolioMock from 'client/features/multichain/mocks/portfolio'; +import * as tokensMock from 'client/features/multichain/mocks/tokens'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx similarity index 99% rename from ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx index 2c11cbeb92..1c9353e7c5 100644 --- a/ui/multichain/address/portfolio/MultichainAddressPortfolioTokens.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx @@ -14,12 +14,13 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { calculateUsdValue } from 'client/slices/token/pages/address/utils'; +import { ADDRESS_PORTFOLIO, TOKEN } from 'client/features/multichain/stubs'; + import useDebounce from 'client/shared/hooks/useDebounce'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import * as cookies from 'client/shared/storage/cookies'; import multichainConfig from 'configs/multichain'; -import { ADDRESS_PORTFOLIO, TOKEN } from 'stubs/multichain'; import { generateListStub } from 'stubs/utils'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import { ZERO } from 'toolkit/utils/consts'; diff --git a/ui/multichain/address/portfolio/MultichainAddressTokensListItem.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx similarity index 100% rename from ui/multichain/address/portfolio/MultichainAddressTokensListItem.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx diff --git a/ui/multichain/address/portfolio/MultichainAddressTokensTable.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTable.tsx similarity index 100% rename from ui/multichain/address/portfolio/MultichainAddressTokensTable.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressTokensTable.tsx diff --git a/ui/multichain/address/portfolio/MultichainAddressTokensTableItem.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx similarity index 100% rename from ui/multichain/address/portfolio/MultichainAddressTokensTableItem.tsx rename to client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png diff --git a/ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png b/client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png similarity index 100% rename from ui/multichain/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png rename to client/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png diff --git a/ui/multichain/address/portfolio/utils.ts b/client/features/multichain/pages/address/portfolio/utils.ts similarity index 100% rename from ui/multichain/address/portfolio/utils.ts rename to client/features/multichain/pages/address/portfolio/utils.ts diff --git a/ui/multichain/address/useChainSelectErc20.ts b/client/features/multichain/pages/address/useChainSelectErc20.ts similarity index 100% rename from ui/multichain/address/useChainSelectErc20.ts rename to client/features/multichain/pages/address/useChainSelectErc20.ts diff --git a/ui/multichain/accounts/MultichainAccounts.tsx b/client/features/multichain/pages/addresses/MultichainAccounts.tsx similarity index 95% rename from ui/multichain/accounts/MultichainAccounts.tsx rename to client/features/multichain/pages/addresses/MultichainAccounts.tsx index f9a9b87dde..e5f02e9707 100644 --- a/ui/multichain/accounts/MultichainAccounts.tsx +++ b/client/features/multichain/pages/addresses/MultichainAccounts.tsx @@ -8,11 +8,12 @@ import AddressesListItem from 'client/slices/address/pages/index/AddressesListIt import AddressesTable from 'client/slices/address/pages/index/AddressesTable'; import { TOP_ADDRESS } from 'client/slices/address/stubs/address'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getItemIndex from 'client/shared/lists/get-item-index'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { generateListStub } from 'stubs/utils'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/block/MultichainBlock.tsx b/client/features/multichain/pages/block/MultichainBlock.tsx similarity index 90% rename from ui/multichain/block/MultichainBlock.tsx rename to client/features/multichain/pages/block/MultichainBlock.tsx index f731496cec..a39595f987 100644 --- a/ui/multichain/block/MultichainBlock.tsx +++ b/client/features/multichain/pages/block/MultichainBlock.tsx @@ -5,10 +5,11 @@ import React from 'react'; import Block from 'client/slices/block/pages/details/Block'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; const MultichainBlock = () => { const router = useRouter(); diff --git a/ui/multichain/blocks/MultichainBlocks.tsx b/client/features/multichain/pages/blocks/MultichainBlocks.tsx similarity index 98% rename from ui/multichain/blocks/MultichainBlocks.tsx rename to client/features/multichain/pages/blocks/MultichainBlocks.tsx index f5dbfe0e5e..527ca0fc15 100644 --- a/ui/multichain/blocks/MultichainBlocks.tsx +++ b/client/features/multichain/pages/blocks/MultichainBlocks.tsx @@ -10,6 +10,8 @@ import { route } from 'nextjs/routes'; import { BLOCK } from 'client/slices/block/stubs/block'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; @@ -17,7 +19,6 @@ import multichainConfig from 'configs/multichain'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import IconSvg from 'ui/shared/IconSvg'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/ui/multichain/blocks/MultichainBlocksContent.tsx b/client/features/multichain/pages/blocks/MultichainBlocksContent.tsx similarity index 93% rename from ui/multichain/blocks/MultichainBlocksContent.tsx rename to client/features/multichain/pages/blocks/MultichainBlocksContent.tsx index 0c9a5f9e3c..6e108e578f 100644 --- a/ui/multichain/blocks/MultichainBlocksContent.tsx +++ b/client/features/multichain/pages/blocks/MultichainBlocksContent.tsx @@ -8,8 +8,9 @@ import { SocketProvider } from 'client/api/socket/context'; import type { Props as BlocksContentProps } from 'client/slices/block/pages/index/BlocksContent'; import BlocksContent from 'client/slices/block/pages/index/BlocksContent'; +import { MultichainProvider } from 'client/features/multichain/context'; + import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; interface Props extends BlocksContentProps { diff --git a/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx b/client/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx similarity index 96% rename from ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx rename to client/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx index 763ed868fe..f2ed9a5b99 100644 --- a/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx +++ b/client/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx @@ -10,11 +10,12 @@ import VerifiedContractsFilter from 'client/slices/contract/pages/index/Verified import VerifiedContractsList from 'client/slices/contract/pages/index/VerifiedContractsList'; import VerifiedContractsTable from 'client/slices/contract/pages/index/VerifiedContractsTable'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/ecosystems/MultichainEcosystems.pw.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx similarity index 85% rename from ui/multichain/ecosystems/MultichainEcosystems.pw.tsx rename to client/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx index 8a3c196c15..960f829d24 100644 --- a/ui/multichain/ecosystems/MultichainEcosystems.pw.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import * as chainDataMock from 'mocks/multichain/chains'; -import * as statsMock from 'mocks/multichain/stats'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; +import * as statsMock from 'client/features/multichain/mocks/stats'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/ui/multichain/ecosystems/MultichainEcosystems.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystems.tsx similarity index 95% rename from ui/multichain/ecosystems/MultichainEcosystems.tsx rename to client/features/multichain/pages/ecosystems/MultichainEcosystems.tsx index ebd10ab179..6e14f52dc8 100644 --- a/ui/multichain/ecosystems/MultichainEcosystems.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystems.tsx @@ -4,12 +4,13 @@ import { Box, createListCollection } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { ChainMetricsSorting, ChainMetricsSortingField, ChainMetricsSortingValue } from 'types/client/multichainAggregator'; +import type { ChainMetricsSorting, ChainMetricsSortingField, ChainMetricsSortingValue } from 'client/features/multichain/types/client'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { CHAIN_METRICS } from 'client/features/multichain/stubs'; + import multichainConfig from 'configs/multichain'; -import { CHAIN_METRICS } from 'stubs/multichain'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx similarity index 98% rename from ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx rename to client/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx index 6423551ef4..39b73da261 100644 --- a/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx @@ -4,7 +4,7 @@ import { chakra, Grid, HStack, Stat } from '@chakra-ui/react'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ChainSnippetList from 'ui/shared/externalChains/ChainSnippetList'; diff --git a/ui/multichain/ecosystems/MultichainEcosystemsTable.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTable.tsx similarity index 95% rename from ui/multichain/ecosystems/MultichainEcosystemsTable.tsx rename to client/features/multichain/pages/ecosystems/MultichainEcosystemsTable.tsx index 4936b522cc..700324f658 100644 --- a/ui/multichain/ecosystems/MultichainEcosystemsTable.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTable.tsx @@ -4,10 +4,11 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ChainMetricsSortingField, ChainMetricsSortingValue } from 'types/client/multichainAggregator'; +import type { ChainMetricsSortingField, ChainMetricsSortingValue } from 'client/features/multichain/types/client'; + +import { MultichainProvider } from 'client/features/multichain/context'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import getNextSortValue from 'ui/shared/sort/getNextSortValue'; diff --git a/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx similarity index 98% rename from ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx rename to client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx index aae773a9fa..97126febde 100644 --- a/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx @@ -4,7 +4,7 @@ import { Box, HStack, Stat } from '@chakra-ui/react'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import useAddChainClick from 'client/shared/web3/useAddChainClick'; import useProvider from 'client/shared/web3/useProvider'; diff --git a/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png b/client/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png rename to client/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png b/client/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/multichain/ecosystems/utils.ts b/client/features/multichain/pages/ecosystems/utils.ts similarity index 95% rename from ui/multichain/ecosystems/utils.ts rename to client/features/multichain/pages/ecosystems/utils.ts index ab0f37c45d..dd8a79b7a4 100644 --- a/ui/multichain/ecosystems/utils.ts +++ b/client/features/multichain/pages/ecosystems/utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ChainMetricsSortingField, ChainMetricsSortingValue } from 'types/client/multichainAggregator'; +import type { ChainMetricsSortingField, ChainMetricsSortingValue } from 'client/features/multichain/types/client'; import type { SelectOption } from 'toolkit/chakra/select'; diff --git a/ui/multichain/home/ChainIndicators.tsx b/client/features/multichain/pages/home/ChainIndicators.tsx similarity index 95% rename from ui/multichain/home/ChainIndicators.tsx rename to client/features/multichain/pages/home/ChainIndicators.tsx index fb5f5e032d..a9dcb9b8ef 100644 --- a/ui/multichain/home/ChainIndicators.tsx +++ b/client/features/multichain/pages/home/ChainIndicators.tsx @@ -13,12 +13,13 @@ import ChainIndicatorsContainer from 'client/slices/home/pages/index/charts/Chai import ChainIndicatorsList from 'client/slices/home/pages/index/charts/ChainIndicatorsList'; import { isIndicatorEnabled, sortIndicators } from 'client/slices/home/utils/indicators'; -import { HOMEPAGE_STATS } from 'stubs/multichain'; +import NativeTokenIcon from 'client/features/multichain/components/NativeTokenIcon'; +import { HOMEPAGE_STATS } from 'client/features/multichain/stubs'; + import IconSvg from 'ui/shared/IconSvg'; -import NativeTokenIcon from '../components/NativeTokenIcon'; +import useFetchParentChainApi from '../../hooks/useFetchParentChainApi'; import useChartDataQuery from './useChartDataQuery'; -import useFetchParentChainApi from './useFetchParentChainApi'; const ChainIndicators = () => { const statsQuery = useApiQuery('multichainStats:pages_main', { diff --git a/ui/multichain/home/ChainWidget.tsx b/client/features/multichain/pages/home/ChainWidget.tsx similarity index 98% rename from ui/multichain/home/ChainWidget.tsx rename to client/features/multichain/pages/home/ChainWidget.tsx index c9ba092bc2..0edd845925 100644 --- a/ui/multichain/home/ChainWidget.tsx +++ b/client/features/multichain/pages/home/ChainWidget.tsx @@ -4,7 +4,7 @@ import { Box, HStack, VStack } from '@chakra-ui/react'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import useAddChainClick from 'client/shared/web3/useAddChainClick'; diff --git a/ui/multichain/home/LatestTxs.tsx b/client/features/multichain/pages/home/LatestTxs.tsx similarity index 86% rename from ui/multichain/home/LatestTxs.tsx rename to client/features/multichain/pages/home/LatestTxs.tsx index 2198afb48d..ee1feadd9f 100644 --- a/ui/multichain/home/LatestTxs.tsx +++ b/client/features/multichain/pages/home/LatestTxs.tsx @@ -3,13 +3,14 @@ import { useRouter } from 'next/router'; import React from 'react'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import LatestTxsLocal from './LatestTxsLocal'; diff --git a/ui/multichain/home/LatestTxsLocal.tsx b/client/features/multichain/pages/home/LatestTxsLocal.tsx similarity index 95% rename from ui/multichain/home/LatestTxsLocal.tsx rename to client/features/multichain/pages/home/LatestTxsLocal.tsx index d655264a77..2d56a07222 100644 --- a/ui/multichain/home/LatestTxsLocal.tsx +++ b/client/features/multichain/pages/home/LatestTxsLocal.tsx @@ -14,7 +14,8 @@ import { SocketProvider } from 'client/api/socket/context'; import TxsContent from 'client/slices/tx/pages/index/list/TxsContent'; import { TX } from 'client/slices/tx/stubs/tx'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { Link } from 'toolkit/chakra/link'; const PAGINATION_PARAMS: PaginationParams = { diff --git a/ui/multichain/home/MultichainHome.pw.tsx b/client/features/multichain/pages/home/MultichainHome.pw.tsx similarity index 88% rename from ui/multichain/home/MultichainHome.pw.tsx rename to client/features/multichain/pages/home/MultichainHome.pw.tsx index 336125d475..03971f3f50 100644 --- a/ui/multichain/home/MultichainHome.pw.tsx +++ b/client/features/multichain/pages/home/MultichainHome.pw.tsx @@ -3,9 +3,10 @@ import React from 'react'; import * as chainStatsMock from 'client/slices/home/mocks/stats'; import * as txMock from 'client/slices/tx/mocks/tx'; -import * as chainDataMock from 'mocks/multichain/chains'; -import * as metricsMock from 'mocks/multichain/metrics'; -import * as statsMock from 'mocks/multichain/stats'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; +import * as metricsMock from 'client/features/multichain/mocks/metrics'; +import * as statsMock from 'client/features/multichain/mocks/stats'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/home/MultichainHome.tsx b/client/features/multichain/pages/home/MultichainHome.tsx similarity index 95% rename from ui/multichain/home/MultichainHome.tsx rename to client/features/multichain/pages/home/MultichainHome.tsx index 0f3807efbd..af28910e88 100644 --- a/ui/multichain/home/MultichainHome.tsx +++ b/client/features/multichain/pages/home/MultichainHome.tsx @@ -9,8 +9,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import HeroBanner from 'client/slices/home/pages/index/HeroBanner'; +import { MultichainProvider } from 'client/features/multichain/context'; + import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import ChainWidget from './ChainWidget'; diff --git a/ui/multichain/home/Stats.tsx b/client/features/multichain/pages/home/Stats.tsx similarity index 97% rename from ui/multichain/home/Stats.tsx rename to client/features/multichain/pages/home/Stats.tsx index 8266184972..2950e628cb 100644 --- a/ui/multichain/home/Stats.tsx +++ b/client/features/multichain/pages/home/Stats.tsx @@ -8,7 +8,8 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import type { HomeStatsWidgetItem } from 'client/slices/home/utils/stats'; import { sortHomeStatsItems, isHomeStatsItemEnabled } from 'client/slices/home/utils/stats'; -import { HOMEPAGE_STATS } from 'stubs/multichain'; +import { HOMEPAGE_STATS } from 'client/features/multichain/stubs'; + import StatsWidget from 'ui/shared/stats/StatsWidget'; import ChainIndicators from './ChainIndicators'; diff --git a/ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png rename to client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png diff --git a/ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png b/client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png rename to client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png diff --git a/ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/multichain/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png rename to client/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png diff --git a/ui/multichain/home/useChartDataQuery.ts b/client/features/multichain/pages/home/useChartDataQuery.ts similarity index 94% rename from ui/multichain/home/useChartDataQuery.ts rename to client/features/multichain/pages/home/useChartDataQuery.ts index c174b76f26..f85b7c052c 100644 --- a/ui/multichain/home/useChartDataQuery.ts +++ b/client/features/multichain/pages/home/useChartDataQuery.ts @@ -8,9 +8,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { getChartData } from 'client/slices/home/utils/chart'; -import { HOMEPAGE_STATS } from 'stubs/multichain'; +import { HOMEPAGE_STATS } from 'client/features/multichain/stubs'; -import useFetchParentChainApi from './useFetchParentChainApi'; +import useFetchParentChainApi from '../../hooks/useFetchParentChainApi'; interface Props { indicatorId: string; diff --git a/ui/multichain/internalTxs/MultichainInternalTxs.tsx b/client/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx similarity index 94% rename from ui/multichain/internalTxs/MultichainInternalTxs.tsx rename to client/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx index 5b60829b54..3a22e354b1 100644 --- a/ui/multichain/internalTxs/MultichainInternalTxs.tsx +++ b/client/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx @@ -7,11 +7,12 @@ import InternalTxsList from 'client/slices/internal-tx/components/InternalTxsLis import InternalTxsTable from 'client/slices/internal-tx/components/InternalTxsTable'; import useInternalTxsQuery from 'client/slices/internal-tx/hooks/useInternalTxsQuery'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/client/features/multichain/pages/search-results/SearchResults.pw.tsx b/client/features/multichain/pages/search-results/SearchResults.pw.tsx index 8eb5bb4221..cd49f44e9c 100644 --- a/client/features/multichain/pages/search-results/SearchResults.pw.tsx +++ b/client/features/multichain/pages/search-results/SearchResults.pw.tsx @@ -2,9 +2,9 @@ import React from 'react'; import type { GetAddressResponse } from '@blockscout/multichain-aggregator-types'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; import * as searchMock from 'client/features/multichain/mocks/search'; -import * as chainDataMock from 'mocks/multichain/chains'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/client/features/multichain/pages/search-results/SearchResults.tsx b/client/features/multichain/pages/search-results/SearchResults.tsx index 184a206afd..80774092c2 100644 --- a/client/features/multichain/pages/search-results/SearchResults.tsx +++ b/client/features/multichain/pages/search-results/SearchResults.tsx @@ -5,13 +5,14 @@ import React from 'react'; import SearchResultsInput from 'client/slices/search/pages/search-results/SearchResultsInput'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import * as Layout from 'ui/shared/layout/components'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemAddress.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemAddress.tsx index a5f904e2ae..8dd35fbe38 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemAddress.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemAddress.tsx @@ -9,7 +9,7 @@ import { route } from 'nextjs/routes'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import * as contract from 'lib/multichain/contract'; +import * as contract from 'client/features/multichain/utils/contract'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemBlock.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemBlock.tsx index 09b0f45d99..0552e3a56d 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemBlock.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemBlock.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { route } from 'nextjs/routes'; diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemBlockNumber.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemBlockNumber.tsx index d782b1df9e..1583b9830b 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemBlockNumber.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemBlockNumber.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { route } from 'nextjs/routes'; diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemToken.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemToken.tsx index a3916e2091..9416b49c78 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemToken.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemToken.tsx @@ -5,16 +5,17 @@ import { mapValues } from 'es-toolkit'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenType } from 'client/slices/token/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import * as contract from 'client/features/multichain/utils/contract'; + import shortenString from 'client/shared/text/shorten-string'; -import * as contract from 'lib/multichain/contract'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/features/multichain/pages/search-results/items/SearchResultItemTx.tsx b/client/features/multichain/pages/search-results/items/SearchResultItemTx.tsx index fbfb6904fb..5cd3101548 100644 --- a/client/features/multichain/pages/search-results/items/SearchResultItemTx.tsx +++ b/client/features/multichain/pages/search-results/items/SearchResultItemTx.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { route } from 'nextjs/routes'; diff --git a/ui/multichain/stats/MultichainStats.tsx b/client/features/multichain/pages/stats/MultichainStats.tsx similarity index 91% rename from ui/multichain/stats/MultichainStats.tsx rename to client/features/multichain/pages/stats/MultichainStats.tsx index a3a3be3a01..ebc3db776e 100644 --- a/ui/multichain/stats/MultichainStats.tsx +++ b/client/features/multichain/pages/stats/MultichainStats.tsx @@ -7,16 +7,15 @@ import useChainStats from 'client/features/chain-stats/hooks/useChainStats'; import ChainStatsCounters from 'client/features/chain-stats/pages/index/ChainStatsCounters'; import ChainStatsFilters from 'client/features/chain-stats/pages/index/ChainStatsFilters'; import ChainStatsSections from 'client/features/chain-stats/pages/index/ChainStatsSections'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import PageTitle from 'ui/shared/Page/PageTitle'; -import ChainSelect from '../components/ChainSelect'; - const MultichainStats = () => { useEtherscanRedirects(); diff --git a/ui/multichain/tokenInstance/MultichainTokenInstance.tsx b/client/features/multichain/pages/token-instance/MultichainTokenInstance.tsx similarity index 92% rename from ui/multichain/tokenInstance/MultichainTokenInstance.tsx rename to client/features/multichain/pages/token-instance/MultichainTokenInstance.tsx index 29ff0c7aba..461452811b 100644 --- a/ui/multichain/tokenInstance/MultichainTokenInstance.tsx +++ b/client/features/multichain/pages/token-instance/MultichainTokenInstance.tsx @@ -8,10 +8,11 @@ import { SocketProvider } from 'client/api/socket/context'; import TokenInstance from 'client/slices/token/pages/instance/TokenInstance'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; const MultichainTokenInstance = () => { const router = useRouter(); diff --git a/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx b/client/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx similarity index 96% rename from ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx rename to client/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx index a18449e9e0..c58e982b25 100644 --- a/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx +++ b/client/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx @@ -10,14 +10,15 @@ import useTokenTransfersQuery from 'client/slices/token-transfer/hooks/useTokenT import TokenTypeFilter from 'client/slices/token/components/TokenTypeFilter'; import { getTokenFilterValue } from 'client/slices/token/utils/list-utils'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx b/client/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx similarity index 97% rename from ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx rename to client/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx index 8756e90593..4d0f0f07a0 100644 --- a/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx +++ b/client/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx @@ -9,9 +9,10 @@ import TokenTransfersListItem from 'client/slices/token-transfer/pages/index/Tok import TokenTransfersTable from 'client/slices/token-transfer/pages/index/TokenTransfersTable'; import TokenTypeFilter from 'client/slices/token/components/TokenTypeFilter'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PopoverFilter from 'ui/shared/filters/PopoverFilter'; diff --git a/ui/multichain/token/MultichainToken.tsx b/client/features/multichain/pages/token/MultichainToken.tsx similarity index 92% rename from ui/multichain/token/MultichainToken.tsx rename to client/features/multichain/pages/token/MultichainToken.tsx index 38315a709f..a767b4d904 100644 --- a/ui/multichain/token/MultichainToken.tsx +++ b/client/features/multichain/pages/token/MultichainToken.tsx @@ -8,10 +8,11 @@ import { SocketProvider } from 'client/api/socket/context'; import Token from 'client/slices/token/pages/details/Token'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; const MultichainToken = () => { const router = useRouter(); diff --git a/ui/multichain/tokens/MultichainTokens.tsx b/client/features/multichain/pages/tokens/MultichainTokens.tsx similarity index 97% rename from ui/multichain/tokens/MultichainTokens.tsx rename to client/features/multichain/pages/tokens/MultichainTokens.tsx index 2b3afcc9fa..0ea5334753 100644 --- a/ui/multichain/tokens/MultichainTokens.tsx +++ b/client/features/multichain/pages/tokens/MultichainTokens.tsx @@ -10,15 +10,16 @@ import TokenTypeFilter from 'client/slices/token/components/TokenTypeFilter'; import Tokens from 'client/slices/token/pages/index/TokensList'; import { getTokenFilterValue } from 'client/slices/token/utils/list-utils'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { TOKEN } from 'client/features/multichain/stubs'; + import useDebounce from 'client/shared/hooks/useDebounce'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { TOKEN } from 'stubs/multichain'; import { generateListStub } from 'stubs/utils'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/tx/MultichainTx.tsx b/client/features/multichain/pages/tx/MultichainTx.tsx similarity index 90% rename from ui/multichain/tx/MultichainTx.tsx rename to client/features/multichain/pages/tx/MultichainTx.tsx index f1136893da..4df8cd9f75 100644 --- a/ui/multichain/tx/MultichainTx.tsx +++ b/client/features/multichain/pages/tx/MultichainTx.tsx @@ -5,10 +5,11 @@ import React from 'react'; import Transaction from 'client/slices/tx/pages/details/Transaction'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; const MultichainTx = () => { const router = useRouter(); diff --git a/ui/multichain/txs/MultichainTxs.tsx b/client/features/multichain/pages/txs/MultichainTxs.tsx similarity index 89% rename from ui/multichain/txs/MultichainTxs.tsx rename to client/features/multichain/pages/txs/MultichainTxs.tsx index f4727cebae..0cce995985 100644 --- a/ui/multichain/txs/MultichainTxs.tsx +++ b/client/features/multichain/pages/txs/MultichainTxs.tsx @@ -5,13 +5,14 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; +import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { MultichainProvider } from 'lib/contexts/multichain'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import PageTitle from 'ui/shared/Page/PageTitle'; import MultichainTxsLocal, { MULTICHAIN_TXS_LOCAL_TAB_IDS } from './MultichainTxsLocal'; diff --git a/ui/multichain/txs/MultichainTxsLocal.tsx b/client/features/multichain/pages/txs/MultichainTxsLocal.tsx similarity index 94% rename from ui/multichain/txs/MultichainTxsLocal.tsx rename to client/features/multichain/pages/txs/MultichainTxsLocal.tsx index a781c8cc89..93a7161198 100644 --- a/ui/multichain/txs/MultichainTxsLocal.tsx +++ b/client/features/multichain/pages/txs/MultichainTxsLocal.tsx @@ -9,7 +9,8 @@ import { SocketProvider } from 'client/api/socket/context'; import TxsTabs, { getTabId } from 'client/slices/tx/pages/index/list/TxsTabs'; import TxsStats from 'client/slices/tx/pages/index/stats/TxsStats'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; const PARENT_TAB = 'txs_local'; diff --git a/ui/multichain/userOps/MultichainUserOps.tsx b/client/features/multichain/pages/user-ops/MultichainUserOps.tsx similarity index 95% rename from ui/multichain/userOps/MultichainUserOps.tsx rename to client/features/multichain/pages/user-ops/MultichainUserOps.tsx index 9b85ba6d9e..984fb90208 100644 --- a/ui/multichain/userOps/MultichainUserOps.tsx +++ b/client/features/multichain/pages/user-ops/MultichainUserOps.tsx @@ -3,14 +3,14 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import ChainSelect from 'client/features/multichain/components/ChainSelect'; +import { MultichainProvider } from 'client/features/multichain/context'; import UserOpsListItem from 'client/features/user-ops/pages/index/UserOpsListItem'; import UserOpsTable from 'client/features/user-ops/pages/index/UserOpsTable'; import { USER_OPS_ITEM } from 'client/features/user-ops/stubs'; import multichainConfig from 'configs/multichain'; -import { MultichainProvider } from 'lib/contexts/multichain'; import { generateListStub } from 'stubs/utils'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/stubs/multichain.ts b/client/features/multichain/stubs.ts similarity index 97% rename from stubs/multichain.ts rename to client/features/multichain/stubs.ts index a7732c040e..c5cc6402c8 100644 --- a/stubs/multichain.ts +++ b/client/features/multichain/stubs.ts @@ -1,6 +1,6 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import type * as stats from '@blockscout/stats-types'; -import type { AddressTokenItem } from 'types/client/multichainAggregator'; +import type { AddressTokenItem } from 'client/features/multichain/types/client'; import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; diff --git a/types/client/multichainAggregator.ts b/client/features/multichain/types/client.ts similarity index 92% rename from types/client/multichainAggregator.ts rename to client/features/multichain/types/client.ts index ff46766af7..27ff0297c2 100644 --- a/types/client/multichainAggregator.ts +++ b/client/features/multichain/types/client.ts @@ -3,6 +3,18 @@ import type * as bens from '@blockscout/bens-types'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { TokenType } from 'client/slices/token/types/api'; +import type { ExternalChain } from 'types/externalChains'; + +import type config from 'configs/app'; + +export interface ClusterChainConfig extends ExternalChain { + slug: string; + app_config: typeof config; +} + +export interface MultichainConfig { + chains: Array; +} // ts-proto generates the wrong token types for AggregatedTokenInfo // moreover, the default values of the fields (= undefined) cannot be stripped off from the generated types diff --git a/lib/multichain/contract.ts b/client/features/multichain/utils/contract.ts similarity index 100% rename from lib/multichain/contract.ts rename to client/features/multichain/utils/contract.ts diff --git a/lib/multichain/getChainIdFromSlugOrId.ts b/client/features/multichain/utils/get-chain-id-from-slug-or-id.ts similarity index 100% rename from lib/multichain/getChainIdFromSlugOrId.ts rename to client/features/multichain/utils/get-chain-id-from-slug-or-id.ts diff --git a/lib/multichain/getChainValueFromQuery.ts b/client/features/multichain/utils/get-chain-value-from-query.ts similarity index 100% rename from lib/multichain/getChainValueFromQuery.ts rename to client/features/multichain/utils/get-chain-value-from-query.ts diff --git a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx index 6d3baf1008..61cbaabd5f 100644 --- a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx +++ b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx @@ -3,9 +3,9 @@ import { Grid, Text, Flex } from '@chakra-ui/react'; import React from 'react'; +import type * as multichain from 'client/features/multichain/types/client'; import type { SearchResultDomain } from 'client/features/name-services/domains/types/api'; import type { ItemsProps } from 'client/slices/search/components/search-bar/SearchBarSuggest/types'; -import type * as multichain from 'types/client/multichainAggregator'; import { toBech32Address } from 'client/slices/address/utils/bech32'; diff --git a/client/features/tx-interpretation/common/components/TxInterpretation.tsx b/client/features/tx-interpretation/common/components/TxInterpretation.tsx index 21f2a76c4c..67ef7d7672 100644 --- a/client/features/tx-interpretation/common/components/TxInterpretation.tsx +++ b/client/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -5,13 +5,13 @@ import { Box, chakra } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TxInterpretationSummary, TxInterpretationVariable, TxInterpretationVariableString, } from 'client/features/tx-interpretation/common/types/api'; import type { AddressParam } from 'client/slices/address/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; diff --git a/client/features/user-ops/components/entity/UserOpEntity.tsx b/client/features/user-ops/components/entity/UserOpEntity.tsx index 32907f18ea..1205ccf9ab 100644 --- a/client/features/user-ops/components/entity/UserOpEntity.tsx +++ b/client/features/user-ops/components/entity/UserOpEntity.tsx @@ -5,7 +5,8 @@ import React from 'react'; import { route } from 'nextjs/routes'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as EntityBase from 'ui/shared/entities/base/components'; import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; diff --git a/client/features/user-ops/pages/details/UserOpSubHeading.tsx b/client/features/user-ops/pages/details/UserOpSubHeading.tsx index 29073bfccc..2b729cc12c 100644 --- a/client/features/user-ops/pages/details/UserOpSubHeading.tsx +++ b/client/features/user-ops/pages/details/UserOpSubHeading.tsx @@ -8,13 +8,13 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; import UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import config from 'configs/app'; // import type { ResourceError } from 'client/api/resources'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { TX_ACTIONS_BLOCK_ID } from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; diff --git a/client/features/user-ops/pages/index/UserOpsListItem.tsx b/client/features/user-ops/pages/index/UserOpsListItem.tsx index f0d0a6fc35..4dbd264d64 100644 --- a/client/features/user-ops/pages/index/UserOpsListItem.tsx +++ b/client/features/user-ops/pages/index/UserOpsListItem.tsx @@ -2,18 +2,18 @@ import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { UserOpsItem } from 'client/features/user-ops/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressStringOrParam from 'client/slices/address/components/entity/AddressStringOrParam'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import { useMultichainContext } from 'client/features/multichain/context'; import UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import UserOpStatus from 'client/features/user-ops/components/UserOpStatus'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/client/features/user-ops/pages/index/UserOpsTable.tsx b/client/features/user-ops/pages/index/UserOpsTable.tsx index 2891e00d64..68e1cb11b9 100644 --- a/client/features/user-ops/pages/index/UserOpsTable.tsx +++ b/client/features/user-ops/pages/index/UserOpsTable.tsx @@ -6,8 +6,9 @@ import type { UserOpsItem } from 'client/features/user-ops/types/api'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/client/features/user-ops/pages/index/UserOpsTableItem.tsx b/client/features/user-ops/pages/index/UserOpsTableItem.tsx index 2b886400d4..1088485bc2 100644 --- a/client/features/user-ops/pages/index/UserOpsTableItem.tsx +++ b/client/features/user-ops/pages/index/UserOpsTableItem.tsx @@ -2,8 +2,8 @@ import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { UserOpsItem } from 'client/features/user-ops/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressStringOrParam from 'client/slices/address/components/entity/AddressStringOrParam'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/shared/web3/useAddChain.tsx b/client/shared/web3/useAddChain.tsx index ca3e922897..55c45f70dc 100644 --- a/client/shared/web3/useAddChain.tsx +++ b/client/shared/web3/useAddChain.tsx @@ -3,10 +3,10 @@ import React from 'react'; import type { AddEthereumChainParameter } from 'viem'; +import { useMultichainContext } from 'client/features/multichain/context'; import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { SECOND } from 'toolkit/utils/consts'; import useProvider from './useProvider'; diff --git a/client/shared/web3/useProvider.tsx b/client/shared/web3/useProvider.tsx index b867bea654..f972e9cbc7 100644 --- a/client/shared/web3/useProvider.tsx +++ b/client/shared/web3/useProvider.tsx @@ -4,8 +4,9 @@ import { useQuery } from '@tanstack/react-query'; import { getFeaturePayload } from 'configs/app/features/types'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import detectWallet from './detect-wallet'; import useDetectWalletEip6963 from './useDetectWalletEip6963'; diff --git a/client/shared/web3/useSwitchChain.tsx b/client/shared/web3/useSwitchChain.tsx index 0d140ada0e..e38779d63d 100644 --- a/client/shared/web3/useSwitchChain.tsx +++ b/client/shared/web3/useSwitchChain.tsx @@ -2,8 +2,9 @@ import React from 'react'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import useProvider from './useProvider'; import { getHexadecimalChainId } from './utils'; diff --git a/client/slices/address/hooks/useAddressCountersQuery.ts b/client/slices/address/hooks/useAddressCountersQuery.ts index 43690e182c..8a40aef9a1 100644 --- a/client/slices/address/hooks/useAddressCountersQuery.ts +++ b/client/slices/address/hooks/useAddressCountersQuery.ts @@ -3,8 +3,8 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressCounters } from 'client/slices/address/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import useApiQuery from 'client/api/hooks/useApiQuery'; import type { ResourceError } from 'client/api/resources'; diff --git a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx index c0c67924bb..3aabfcb0e6 100644 --- a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx @@ -9,9 +9,10 @@ import type { PaginationParams } from 'ui/shared/pagination/types'; import type { ResourceError } from 'client/api/resources'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { currencyUnits } from 'client/shared/chain/units'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx index 262666713f..6cb6cc0912 100644 --- a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx @@ -4,8 +4,8 @@ import { Stat, Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressCoinBalanceHistoryItem } from 'client/slices/address/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx index e166e8686e..0f7a510781 100644 --- a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx @@ -4,8 +4,8 @@ import { Stat } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressCoinBalanceHistoryItem } from 'client/slices/address/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/slices/address/pages/details/info/token-select/TokenSelect.tsx b/client/slices/address/pages/details/info/token-select/TokenSelect.tsx index 8f1db1f172..16a03e3aa0 100644 --- a/client/slices/address/pages/details/info/token-select/TokenSelect.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelect.tsx @@ -14,11 +14,12 @@ import { getResourceKey } from 'client/api/hooks/useApiQuery'; import useFetchTokens from 'client/slices/token/pages/address/useFetchTokens'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx b/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx index dd537528ef..d7f4b3aabc 100644 --- a/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx +++ b/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx @@ -11,10 +11,10 @@ import TokenTransferFilter from 'client/slices/token-transfer/components/TokenTr import AddressAdvancedFilterLink from 'client/features/advanced-filter/components/AddressAdvancedFilterLink'; import CsvExport from 'client/features/csv-export/components/CsvExport'; +import { useMultichainContext } from 'client/features/multichain/context'; import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts index 3619817669..c6605f5e99 100644 --- a/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts +++ b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts @@ -11,11 +11,12 @@ import { getResourceKey } from 'client/api/hooks/useApiQuery'; import useSocketChannel from 'client/api/socket/useSocketChannel'; import useSocketMessage from 'client/api/socket/useSocketMessage'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as cookies from 'client/shared/storage/cookies'; import config from 'configs/app'; import { useAppContext } from 'lib/contexts/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import type { Filters } from './useAddressTokenTransfersQuery'; diff --git a/client/slices/block/components/entity/BlockEntity.tsx b/client/slices/block/components/entity/BlockEntity.tsx index f6c08c7e25..5c633e420c 100644 --- a/client/slices/block/components/entity/BlockEntity.tsx +++ b/client/slices/block/components/entity/BlockEntity.tsx @@ -5,8 +5,9 @@ import React from 'react'; import { route } from 'nextjs/routes'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import * as EntityBase from 'ui/shared/entities/base/components'; import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; diff --git a/client/slices/block/pages/countdown-details/BlockCountdown.tsx b/client/slices/block/pages/countdown-details/BlockCountdown.tsx index 246de226a8..12e03cb2c1 100644 --- a/client/slices/block/pages/countdown-details/BlockCountdown.tsx +++ b/client/slices/block/pages/countdown-details/BlockCountdown.tsx @@ -8,10 +8,11 @@ import { route } from 'nextjs/routes'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; diff --git a/client/slices/block/pages/countdown-details/create-google-calendar-link.ts b/client/slices/block/pages/countdown-details/create-google-calendar-link.ts index 2f28bf2771..693f84617a 100644 --- a/client/slices/block/pages/countdown-details/create-google-calendar-link.ts +++ b/client/slices/block/pages/countdown-details/create-google-calendar-link.ts @@ -2,8 +2,9 @@ import { route } from 'nextjs/routes'; +import type { TMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import type { TMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; interface Params { diff --git a/client/slices/block/pages/countdown-details/create-ics-file-blob.ts b/client/slices/block/pages/countdown-details/create-ics-file-blob.ts index 54190841ce..b0f9474ef7 100644 --- a/client/slices/block/pages/countdown-details/create-ics-file-blob.ts +++ b/client/slices/block/pages/countdown-details/create-ics-file-blob.ts @@ -2,8 +2,9 @@ import { route } from 'nextjs/routes'; +import type { TMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import type { TMultichainContext } from 'lib/contexts/multichain'; import type dayjs from 'lib/date/dayjs'; interface Params { diff --git a/client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx b/client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx index a9965be1b7..5460c83d2a 100644 --- a/client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx +++ b/client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx @@ -6,7 +6,8 @@ import React from 'react'; import { routeParams } from 'nextjs/routes'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { Heading } from 'toolkit/chakra/heading'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; diff --git a/client/slices/block/pages/details/Block.tsx b/client/slices/block/pages/details/Block.tsx index 0cd7e572e0..35dbffff15 100644 --- a/client/slices/block/pages/details/Block.tsx +++ b/client/slices/block/pages/details/Block.tsx @@ -26,6 +26,7 @@ import useBlockDepositsQuery from 'client/features/chain-variants/beacon-chain/p import useBlockWithdrawalsQuery from 'client/features/chain-variants/beacon-chain/pages/block/useBlockWithdrawalsQuery'; import BlockCeloEpochTag from 'client/features/chain-variants/celo/pages/block/BlockCeloEpochTag'; import useBlockBlobTxsQuery from 'client/features/data-availability/hooks/useBlockBlobTxsQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; import throwOnAbsentParamError from 'client/shared/errors/throw-on-absent-param-error'; @@ -34,7 +35,6 @@ import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Skeleton } from 'toolkit/chakra/skeleton'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import TextAd from 'ui/shared/ad/TextAd'; diff --git a/client/slices/block/pages/details/BlockDetails.tsx b/client/slices/block/pages/details/BlockDetails.tsx index 4d0583b3d5..6d020daa8c 100644 --- a/client/slices/block/pages/details/BlockDetails.tsx +++ b/client/slices/block/pages/details/BlockDetails.tsx @@ -18,6 +18,7 @@ import GasUsed from 'client/slices/gas/components/GasUsed'; import BlockDetailsBaseFeeCelo from 'client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo'; import BlockDetailsZilliqaQuorumCertificate from 'client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate'; import BlockDetailsBlobInfo from 'client/features/data-availability/pages/block/BlockDetailsBlobInfo'; +import { useMultichainContext } from 'client/features/multichain/context'; import * as arbitrum from 'client/features/rollup/arbitrum/utils/batch-verification'; import BatchEntityL2 from 'client/features/rollup/common/components/BatchEntityL2'; import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; @@ -31,7 +32,6 @@ import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-titl import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { CollapsibleDetails } from 'toolkit/chakra/collapsible'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/slices/block/pages/index/BlocksContent.tsx b/client/slices/block/pages/index/BlocksContent.tsx index 83749ce3cc..7188e68b40 100644 --- a/client/slices/block/pages/index/BlocksContent.tsx +++ b/client/slices/block/pages/index/BlocksContent.tsx @@ -16,9 +16,10 @@ import useSocketMessage from 'client/api/socket/useSocketMessage'; import BlocksList from 'client/slices/block/pages/index/BlocksList'; import BlocksTable from 'client/slices/block/pages/index/BlocksTable'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/client/slices/block/pages/index/BlocksList.tsx b/client/slices/block/pages/index/BlocksList.tsx index 7990ba1074..73153147ca 100644 --- a/client/slices/block/pages/index/BlocksList.tsx +++ b/client/slices/block/pages/index/BlocksList.tsx @@ -3,8 +3,8 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { Block } from 'client/slices/block/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import BlocksListItem from 'client/slices/block/pages/index/BlocksListItem'; diff --git a/client/slices/block/pages/index/BlocksListItem.tsx b/client/slices/block/pages/index/BlocksListItem.tsx index 4fe6224aea..41a864d3e1 100644 --- a/client/slices/block/pages/index/BlocksListItem.tsx +++ b/client/slices/block/pages/index/BlocksListItem.tsx @@ -5,8 +5,8 @@ import BigNumber from 'bignumber.js'; import { capitalize } from 'es-toolkit'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { Block } from 'client/slices/block/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; diff --git a/client/slices/block/pages/index/BlocksTable.tsx b/client/slices/block/pages/index/BlocksTable.tsx index 48bf1ddff6..3e1ccb8369 100644 --- a/client/slices/block/pages/index/BlocksTable.tsx +++ b/client/slices/block/pages/index/BlocksTable.tsx @@ -3,8 +3,8 @@ import { capitalize } from 'es-toolkit'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { Block } from 'client/slices/block/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; import BlocksTableItem from 'client/slices/block/pages/index/BlocksTableItem'; diff --git a/client/slices/block/pages/index/BlocksTableItem.tsx b/client/slices/block/pages/index/BlocksTableItem.tsx index 2c4009cc7b..b3aadc8116 100644 --- a/client/slices/block/pages/index/BlocksTableItem.tsx +++ b/client/slices/block/pages/index/BlocksTableItem.tsx @@ -4,8 +4,8 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { Block } from 'client/slices/block/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; diff --git a/client/slices/contract/pages/details/code/ContractCode.tsx b/client/slices/contract/pages/details/code/ContractCode.tsx index 3e46dc275f..bef9eaac8b 100644 --- a/client/slices/contract/pages/details/code/ContractCode.tsx +++ b/client/slices/contract/pages/details/code/ContractCode.tsx @@ -14,9 +14,10 @@ import type { ResourceError } from 'client/api/resources'; import * as stubs from 'client/slices/contract/stubs'; +import { useMultichainContext } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { useMultichainContext } from 'lib/contexts/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; diff --git a/client/slices/contract/pages/details/code/ContractDetailsVerificationButton.tsx b/client/slices/contract/pages/details/code/ContractDetailsVerificationButton.tsx index f20f064bdf..098c9c742b 100644 --- a/client/slices/contract/pages/details/code/ContractDetailsVerificationButton.tsx +++ b/client/slices/contract/pages/details/code/ContractDetailsVerificationButton.tsx @@ -4,8 +4,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Button } from 'toolkit/chakra/button'; import type { LinkProps } from 'toolkit/chakra/link'; import { Link } from 'toolkit/chakra/link'; diff --git a/client/slices/contract/pages/details/code/ContractSourceCode.tsx b/client/slices/contract/pages/details/code/ContractSourceCode.tsx index bbf1038a7e..9144bcd50f 100644 --- a/client/slices/contract/pages/details/code/ContractSourceCode.tsx +++ b/client/slices/contract/pages/details/code/ContractSourceCode.tsx @@ -9,7 +9,8 @@ import { route } from 'nextjs/routes'; import { formatLanguageName } from 'client/slices/contract/utils/language'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; diff --git a/client/slices/contract/pages/details/code/info/ContractDetailsInfo.tsx b/client/slices/contract/pages/details/code/info/ContractDetailsInfo.tsx index 88f5ab4916..70c1bf5da0 100644 --- a/client/slices/contract/pages/details/code/info/ContractDetailsInfo.tsx +++ b/client/slices/contract/pages/details/code/info/ContractDetailsInfo.tsx @@ -11,9 +11,9 @@ import { getGitHubOwnerAndRepo } from 'client/slices/contract/pages/contract-ver import { CONTRACT_LICENSES } from 'client/slices/contract/utils/licenses'; import ContractSecurityAudits from 'client/features/contract-audit-reports/components/ContractSecurityAudits'; +import { useMultichainContext } from 'client/features/multichain/context'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import Time from 'ui/shared/time/Time'; diff --git a/client/slices/contract/pages/details/methods/useCallMethodPublicClient.ts b/client/slices/contract/pages/details/methods/useCallMethodPublicClient.ts index 2d605d5cb9..9bc80f1ff3 100644 --- a/client/slices/contract/pages/details/methods/useCallMethodPublicClient.ts +++ b/client/slices/contract/pages/details/methods/useCallMethodPublicClient.ts @@ -7,9 +7,9 @@ import { usePublicClient } from 'wagmi'; import type { FormSubmitResult, MethodCallStrategy, SmartContractMethod } from './types'; import useAccount from 'client/features/connect-wallet/hooks/useAccount'; +import { useMultichainContext } from 'client/features/multichain/context'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { getNativeCoinValue } from './utils'; diff --git a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts index d2b5a8aa44..2b4906076a 100644 --- a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts +++ b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts @@ -7,10 +7,10 @@ import { useAccount, useSwitchChain, useWriteContract, useSendTransaction } from import type { FormSubmitResult, SmartContractMethod } from './types'; import useWallet from 'client/features/connect-wallet/hooks/useWallet'; +import { useMultichainContext } from 'client/features/multichain/context'; import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { getNativeCoinValue } from './utils'; diff --git a/client/slices/contract/pages/details/useContractTabs.tsx b/client/slices/contract/pages/details/useContractTabs.tsx index 48b93fbbf7..1f69faf6c4 100644 --- a/client/slices/contract/pages/details/useContractTabs.tsx +++ b/client/slices/contract/pages/details/useContractTabs.tsx @@ -3,8 +3,8 @@ import type { Channel } from 'phoenix'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { Address } from 'client/slices/address/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/client/slices/contract/pages/index/VerifiedContractsCounters.tsx b/client/slices/contract/pages/index/VerifiedContractsCounters.tsx index 60de41612b..1780943446 100644 --- a/client/slices/contract/pages/index/VerifiedContractsCounters.tsx +++ b/client/slices/contract/pages/index/VerifiedContractsCounters.tsx @@ -7,8 +7,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { VERIFIED_CONTRACTS_COUNTERS, VERIFIED_CONTRACTS_COUNTERS_MICROSERVICE } from 'client/slices/contract/stubs'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import StatsWidget from 'ui/shared/stats/StatsWidget'; const VerifiedContractsCounters = () => { diff --git a/client/slices/contract/pages/index/VerifiedContractsTable.tsx b/client/slices/contract/pages/index/VerifiedContractsTable.tsx index c897602fc7..abfd15469c 100644 --- a/client/slices/contract/pages/index/VerifiedContractsTable.tsx +++ b/client/slices/contract/pages/index/VerifiedContractsTable.tsx @@ -6,9 +6,10 @@ import type { VerifiedContract, VerifiedContractsSortingField, VerifiedContracts import { SORT_SEQUENCE } from 'client/slices/contract/pages/index/sort'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { currencyUnits } from 'client/shared/chain/units'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import getNextSortValue from 'ui/shared/sort/getNextSortValue'; diff --git a/client/slices/contract/pages/index/VerifiedContractsTableItem.tsx b/client/slices/contract/pages/index/VerifiedContractsTableItem.tsx index 10fd4b93e4..8c108d6910 100644 --- a/client/slices/contract/pages/index/VerifiedContractsTableItem.tsx +++ b/client/slices/contract/pages/index/VerifiedContractsTableItem.tsx @@ -3,8 +3,8 @@ import { Flex, chakra } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { VerifiedContract } from 'client/slices/contract/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import ContractCertifiedLabel from 'client/slices/contract/components/ContractCertifiedLabel'; diff --git a/client/slices/gas/components/GasPrice.tsx b/client/slices/gas/components/GasPrice.tsx index fdd2a45a8d..53a6cec44d 100644 --- a/client/slices/gas/components/GasPrice.tsx +++ b/client/slices/gas/components/GasPrice.tsx @@ -6,8 +6,9 @@ import React from 'react'; import type { GasPriceInfo } from 'client/slices/gas/types/api'; import type { GasUnit } from 'client/slices/gas/types/config'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import formatGasValue from '../utils/format-gas-value'; diff --git a/client/slices/internal-tx/components/InternalTxsList.tsx b/client/slices/internal-tx/components/InternalTxsList.tsx index 43aba67cd8..b5853172b5 100644 --- a/client/slices/internal-tx/components/InternalTxsList.tsx +++ b/client/slices/internal-tx/components/InternalTxsList.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { InternalTransaction } from 'client/slices/internal-tx/types/api'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; import InternalTxsListItem from './InternalTxsListItem'; diff --git a/client/slices/internal-tx/components/InternalTxsListItem.tsx b/client/slices/internal-tx/components/InternalTxsListItem.tsx index e932bfe14a..f3a125bbcb 100644 --- a/client/slices/internal-tx/components/InternalTxsListItem.tsx +++ b/client/slices/internal-tx/components/InternalTxsListItem.tsx @@ -3,8 +3,8 @@ import { Flex, HStack } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { InternalTransaction } from 'client/slices/internal-tx/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/internal-tx/components/InternalTxsTable.tsx b/client/slices/internal-tx/components/InternalTxsTable.tsx index d6c153c822..cd09f3665f 100644 --- a/client/slices/internal-tx/components/InternalTxsTable.tsx +++ b/client/slices/internal-tx/components/InternalTxsTable.tsx @@ -6,9 +6,10 @@ import type { InternalTransaction } from 'client/slices/internal-tx/types/api'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { currencyUnits } from 'client/shared/chain/units'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/client/slices/internal-tx/components/InternalTxsTableItem.tsx b/client/slices/internal-tx/components/InternalTxsTableItem.tsx index 1802e397cb..30ebd02c59 100644 --- a/client/slices/internal-tx/components/InternalTxsTableItem.tsx +++ b/client/slices/internal-tx/components/InternalTxsTableItem.tsx @@ -3,8 +3,8 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { InternalTransaction } from 'client/slices/internal-tx/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/log/components/LogItem.tsx b/client/slices/log/components/LogItem.tsx index 3fd41ba294..12f197f71d 100644 --- a/client/slices/log/components/LogItem.tsx +++ b/client/slices/log/components/LogItem.tsx @@ -3,8 +3,8 @@ import { Grid, GridItem } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TransactionLog } from 'client/slices/log/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx index 51c1b305b4..a0f4a986ef 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx @@ -4,18 +4,19 @@ import { chakra, Box, Text, Flex, Grid } from '@chakra-ui/react'; import React from 'react'; import type { ItemsProps } from './types'; +import type * as multichain from 'client/features/multichain/types/client'; import type { SearchResultAddressOrContract, SearchResultMetadataTag } from 'client/slices/search/types/api'; -import type * as multichain from 'types/client/multichainAggregator'; import * as AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { toBech32Address } from 'client/slices/address/utils/bech32'; import ContractCertifiedLabel from 'client/slices/contract/components/ContractCertifiedLabel'; import SearchResultEntityTag from 'client/slices/search/pages/search-results/SearchResultEntityTag'; +import * as contract from 'client/features/multichain/utils/contract'; + import highlightText from 'client/shared/text/highlight-text'; import dayjs from 'lib/date/dayjs'; -import * as contract from 'lib/multichain/contract'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestBlock.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestBlock.tsx index e0ee483924..42d532b5bc 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestBlock.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestBlock.tsx @@ -4,8 +4,8 @@ import { Text, Flex, Grid, Box } from '@chakra-ui/react'; import React from 'react'; import type { ItemsProps } from './types'; +import type * as multichain from 'client/features/multichain/types/client'; import type { SearchResultBlock } from 'client/slices/search/types/client'; -import type * as multichain from 'types/client/multichainAggregator'; import * as BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestToken.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestToken.tsx index 5b00be0cc4..80be294f94 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestToken.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestToken.tsx @@ -5,16 +5,17 @@ import { mapValues } from 'es-toolkit'; import React from 'react'; import type { ItemsProps } from './types'; +import type * as multichain from 'client/features/multichain/types/client'; import type { SearchResultToken } from 'client/slices/search/types/api'; -import type * as multichain from 'types/client/multichainAggregator'; import { toBech32Address } from 'client/slices/address/utils/bech32'; import ContractCertifiedLabel from 'client/slices/contract/components/ContractCertifiedLabel'; import * as TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import * as contract from 'client/features/multichain/utils/contract'; + import highlightText from 'client/shared/text/highlight-text'; -import * as contract from 'lib/multichain/contract'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestTx.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestTx.tsx index 15ff973cf4..718ed6366c 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestTx.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestTx.tsx @@ -4,8 +4,8 @@ import { chakra, Text, Flex } from '@chakra-ui/react'; import React from 'react'; import type { ItemsProps } from './types'; +import type * as multichain from 'client/features/multichain/types/client'; import type { SearchResultTx } from 'client/slices/search/types/api'; -import type * as multichain from 'types/client/multichainAggregator'; import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/types.ts b/client/slices/search/components/search-bar/SearchBarSuggest/types.ts index 2d4b5a12be..0c6d14f876 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/types.ts +++ b/client/slices/search/components/search-bar/SearchBarSuggest/types.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressFormat } from 'client/slices/address/types/config'; -import type { ClusterChainConfig } from 'types/multichain'; export interface ItemsProps { data: Data; diff --git a/client/slices/search/types/client.ts b/client/slices/search/types/client.ts index ca5d76b248..8ed561afb9 100644 --- a/client/slices/search/types/client.ts +++ b/client/slices/search/types/client.ts @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { SearchResultZetaChainCCTX } from 'client/features/chain-variants/zeta-chain/types/client'; +import type * as multichain from 'client/features/multichain/types/client'; import type * as api from 'client/slices/search/types/api'; -import type * as multichain from 'types/client/multichainAggregator'; export interface SearchResultFutureBlock { type: 'block'; diff --git a/client/slices/token-transfer/components/TokenTransferFilter.tsx b/client/slices/token-transfer/components/TokenTransferFilter.tsx index 756c1fbe31..f8a35309b3 100644 --- a/client/slices/token-transfer/components/TokenTransferFilter.tsx +++ b/client/slices/token-transfer/components/TokenTransferFilter.tsx @@ -3,9 +3,9 @@ import { Text, Stack, HStack } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressFromToFilter } from 'client/slices/address/types/api'; import type { TokenType } from 'client/slices/token/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import TokenTypeFilter from 'client/slices/token/components/TokenTypeFilter'; diff --git a/client/slices/token-transfer/components/list/TokenTransferList.tsx b/client/slices/token-transfer/components/list/TokenTransferList.tsx index 280370d826..bc7566559e 100644 --- a/client/slices/token-transfer/components/list/TokenTransferList.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferList.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; import TokenTransferListItem from './TokenTransferListItem'; diff --git a/client/slices/token-transfer/components/list/TokenTransferListItem.tsx b/client/slices/token-transfer/components/list/TokenTransferListItem.tsx index eb944c74e6..79c664ac83 100644 --- a/client/slices/token-transfer/components/list/TokenTransferListItem.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferListItem.tsx @@ -3,9 +3,9 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; diff --git a/client/slices/token-transfer/components/list/TokenTransferTable.tsx b/client/slices/token-transfer/components/list/TokenTransferTable.tsx index b09deb20ad..cc47127304 100644 --- a/client/slices/token-transfer/components/list/TokenTransferTable.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferTable.tsx @@ -6,7 +6,8 @@ import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx b/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx index 17485caab3..c888cf10bc 100644 --- a/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx @@ -3,9 +3,9 @@ import { Flex, Box } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; diff --git a/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx b/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx index 2f6cabcbac..2d7e7d6433 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx @@ -2,9 +2,9 @@ import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/token-transfer/pages/index/TokenTransfersTable.tsx b/client/slices/token-transfer/pages/index/TokenTransfersTable.tsx index 1fa877d07a..49ef050865 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfersTable.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfersTable.tsx @@ -2,8 +2,8 @@ import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; diff --git a/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx b/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx index 73a6663de7..4c298e0e1b 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx @@ -2,9 +2,9 @@ import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/token-transfer/pages/token/TokenTransferList.tsx b/client/slices/token-transfer/pages/token/TokenTransferList.tsx index 55ed385ab2..4060588bfb 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferList.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferList.tsx @@ -8,7 +8,7 @@ import type { TokenInstance } from 'client/slices/token/types/api'; import TokenTransferListItem from 'client/slices/token-transfer/pages/token/TokenTransferListItem'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; interface Props { data: Array; diff --git a/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx b/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx index 672273703f..2827eb112b 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx @@ -3,10 +3,10 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import type { TokenInstance } from 'client/slices/token/types/api'; import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/slices/token-transfer/pages/token/TokenTransferTable.tsx b/client/slices/token-transfer/pages/token/TokenTransferTable.tsx index a51c7da966..f37a936d5a 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferTable.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferTable.tsx @@ -9,7 +9,8 @@ import { hasTokenIds, hasTokenTransferValue, isConfidentialTokenType, isFungible import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; import TokenTransferTableItem from 'client/slices/token-transfer/pages/token/TokenTransferTableItem'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; diff --git a/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx b/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx index 3ac9bc765f..bdf33eabda 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx @@ -3,10 +3,10 @@ import { Flex, Box } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import type { TokenInstance } from 'client/slices/token/types/api'; import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/slices/token-transfer/pages/tx/TxTokenTransfer.tsx b/client/slices/token-transfer/pages/tx/TxTokenTransfer.tsx index 8bcb79e503..d6a501d0f6 100644 --- a/client/slices/token-transfer/pages/tx/TxTokenTransfer.tsx +++ b/client/slices/token-transfer/pages/tx/TxTokenTransfer.tsx @@ -15,13 +15,13 @@ import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; import useTxCrossChainTransfersQuery from 'client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery'; import TxTokenTransferCrossChain from 'client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain'; +import { useMultichainContext } from 'client/features/multichain/context'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/client/slices/token/components/TokenTypeFilter.tsx b/client/slices/token/components/TokenTypeFilter.tsx index 44e089fc0b..525382f9c4 100644 --- a/client/slices/token/components/TokenTypeFilter.tsx +++ b/client/slices/token/components/TokenTypeFilter.tsx @@ -3,9 +3,9 @@ import { Box, Flex, useCheckboxGroup, Fieldset } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { NFTTokenType, TokenType } from 'client/slices/token/types/api'; import { getTokenTypes } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import { Button } from 'toolkit/chakra/button'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; diff --git a/client/slices/token/components/entity/TokenEntity.pw.tsx b/client/slices/token/components/entity/TokenEntity.pw.tsx index 5b43b9ce07..8329f04915 100644 --- a/client/slices/token/components/entity/TokenEntity.pw.tsx +++ b/client/slices/token/components/entity/TokenEntity.pw.tsx @@ -4,7 +4,8 @@ import React from 'react'; import TokenEntity, { Icon } from 'client/slices/token/components/entity/TokenEntity'; import * as tokenMock from 'client/slices/token/mocks/info'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; + import { stableHover } from 'playwright/helpers/stableHover'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/token/components/entity/TokenEntity.tsx b/client/slices/token/components/entity/TokenEntity.tsx index 1117951c0d..d7ec361448 100644 --- a/client/slices/token/components/entity/TokenEntity.tsx +++ b/client/slices/token/components/entity/TokenEntity.tsx @@ -10,8 +10,9 @@ import { route } from 'nextjs/routes'; import TokenIconPlaceholder from 'client/slices/token/components/icon/TokenIconPlaceholder'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { TruncatedTextTooltip } from 'toolkit/components/truncation/TruncatedTextTooltip'; diff --git a/client/slices/token/pages/address/AddressCollections.tsx b/client/slices/token/pages/address/AddressCollections.tsx index e1e19207c2..562d5f7649 100644 --- a/client/slices/token/pages/address/AddressCollections.tsx +++ b/client/slices/token/pages/address/AddressCollections.tsx @@ -9,9 +9,10 @@ import { route } from 'nextjs/routes'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/client/slices/token/pages/address/AddressNFTs.tsx b/client/slices/token/pages/address/AddressNFTs.tsx index 206b9ddd5b..452bb63927 100644 --- a/client/slices/token/pages/address/AddressNFTs.tsx +++ b/client/slices/token/pages/address/AddressNFTs.tsx @@ -5,9 +5,10 @@ import React from 'react'; import type { NFTTokenType } from 'client/slices/token/types/api'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/client/slices/token/pages/address/NFTItem.tsx b/client/slices/token/pages/address/NFTItem.tsx index b64fbd9da5..e444d1580f 100644 --- a/client/slices/token/pages/address/NFTItem.tsx +++ b/client/slices/token/pages/address/NFTItem.tsx @@ -3,9 +3,9 @@ import { Flex, Text } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { AddressNFT } from 'client/slices/address/types/api'; import { getTokenTypeName } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; diff --git a/client/slices/token/pages/address/useFetchTokens.ts b/client/slices/token/pages/address/useFetchTokens.ts index e141f64dab..55ea525d3d 100644 --- a/client/slices/token/pages/address/useFetchTokens.ts +++ b/client/slices/token/pages/address/useFetchTokens.ts @@ -12,8 +12,9 @@ import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; import useSocketChannel from 'client/api/socket/useSocketChannel'; import useSocketMessage from 'client/api/socket/useSocketMessage'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import type { TokenEnhancedData } from './utils'; import { calculateUsdValue } from './utils'; diff --git a/client/slices/token/pages/details/TokenPageTitle.tsx b/client/slices/token/pages/details/TokenPageTitle.tsx index 3e53475bce..cf0ff55204 100644 --- a/client/slices/token/pages/details/TokenPageTitle.tsx +++ b/client/slices/token/pages/details/TokenPageTitle.tsx @@ -18,11 +18,11 @@ import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCod import * as TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import useAddressMetadataInfoQuery from 'client/features/address-metadata/hooks/useAddressMetadataInfoQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; import TokenVerifiedInfo from 'client/features/verified-tokens/pages/token/TokenVerifiedInfo'; import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Tooltip } from 'toolkit/chakra/tooltip'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; diff --git a/client/slices/token/pages/details/info/TokenDetails.tsx b/client/slices/token/pages/details/info/TokenDetails.tsx index dd5274e3ca..f1ad93315e 100644 --- a/client/slices/token/pages/details/info/TokenDetails.tsx +++ b/client/slices/token/pages/details/info/TokenDetails.tsx @@ -17,11 +17,12 @@ import AddressEntity from 'client/slices/address/components/entity/AddressEntity import type { TokenTabs } from 'client/slices/token/pages/details/Token'; import { TOKEN_COUNTERS } from 'client/slices/token/stubs'; +import { useMultichainContext } from 'client/features/multichain/context'; + import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import useIsMounted from 'client/shared/hooks/useIsMounted'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; diff --git a/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx b/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx index 640dda57bd..47cc6fc43f 100644 --- a/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx +++ b/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx @@ -9,9 +9,10 @@ import { route } from 'nextjs/routes'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { useMultichainContext } from 'client/features/multichain/context'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TruncatedTextTooltip } from 'toolkit/components/truncation/TruncatedTextTooltip'; diff --git a/client/slices/token/pages/index/TokensListItem.tsx b/client/slices/token/pages/index/TokensListItem.tsx index affe4d05d2..36770f51ed 100644 --- a/client/slices/token/pages/index/TokensListItem.tsx +++ b/client/slices/token/pages/index/TokensListItem.tsx @@ -4,9 +4,9 @@ import { Flex, HStack, Grid, GridItem } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { AggregatedTokenInfo } from 'client/features/multichain/types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; import { getTokenTypeName } from 'client/slices/token/utils/token-types'; -import type { AggregatedTokenInfo } from 'types/client/multichainAggregator'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; diff --git a/client/slices/token/pages/index/TokensTable.tsx b/client/slices/token/pages/index/TokensTable.tsx index f74e5c7f27..eb1ac1cdad 100644 --- a/client/slices/token/pages/index/TokensTable.tsx +++ b/client/slices/token/pages/index/TokensTable.tsx @@ -2,8 +2,8 @@ import React from 'react'; +import type { AggregatedTokenInfo } from 'client/features/multichain/types/client'; import type { TokenInfo, TokensSortingField, TokensSortingValue } from 'client/slices/token/types/api'; -import type { AggregatedTokenInfo } from 'types/client/multichainAggregator'; import type { OnValueChangeHandler } from 'toolkit/chakra/select'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; diff --git a/client/slices/token/pages/index/TokensTableItem.tsx b/client/slices/token/pages/index/TokensTableItem.tsx index 7e687f96b4..2e4a6c1df7 100644 --- a/client/slices/token/pages/index/TokensTableItem.tsx +++ b/client/slices/token/pages/index/TokensTableItem.tsx @@ -4,9 +4,9 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import type { AggregatedTokenInfo } from 'client/features/multichain/types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; import { getTokenTypeName } from 'client/slices/token/utils/token-types'; -import type { AggregatedTokenInfo } from 'types/client/multichainAggregator'; import type { EntityProps as AddressEntityProps } from 'client/slices/address/components/entity/AddressEntity'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/client/slices/token/pages/instance/TokenInstancePageTitle.tsx b/client/slices/token/pages/instance/TokenInstancePageTitle.tsx index 8af83ae85c..7bd29e0726 100644 --- a/client/slices/token/pages/instance/TokenInstancePageTitle.tsx +++ b/client/slices/token/pages/instance/TokenInstancePageTitle.tsx @@ -9,9 +9,9 @@ import { getTokenTypeName } from 'client/slices/token/utils/token-types'; import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCode'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import { useMultichainContext } from 'client/features/multichain/context'; import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { Tag } from 'toolkit/chakra/tag'; import * as regexp from 'toolkit/utils/regexp'; diff --git a/client/slices/token/utils/list-utils.ts b/client/slices/token/utils/list-utils.ts index 6d3f262af4..d38ca979ce 100644 --- a/client/slices/token/utils/list-utils.ts +++ b/client/slices/token/utils/list-utils.ts @@ -2,9 +2,9 @@ import { createListCollection } from '@chakra-ui/react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { TokensSortingValue } from 'client/slices/token/types/api'; import { getTokenTypes } from 'client/slices/token/utils/token-types'; -import type { ClusterChainConfig } from 'types/multichain'; import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; diff --git a/client/slices/token/utils/token-types.ts b/client/slices/token/utils/token-types.ts index bcc79dd79a..f31ff04e3a 100644 --- a/client/slices/token/utils/token-types.ts +++ b/client/slices/token/utils/token-types.ts @@ -2,8 +2,8 @@ import { uniqBy } from 'es-toolkit'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { NFTTokenType, TokenType } from 'client/slices/token/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import config from 'configs/app'; diff --git a/client/slices/tx/components/TxAdditionalInfoContent.tsx b/client/slices/tx/components/TxAdditionalInfoContent.tsx index 75eef2b655..d7fcf84265 100644 --- a/client/slices/tx/components/TxAdditionalInfoContent.tsx +++ b/client/slices/tx/components/TxAdditionalInfoContent.tsx @@ -12,11 +12,11 @@ import TxFee from 'client/slices/tx/components/TxFee'; import TxStatus from 'client/slices/tx/components/TxStatus'; import BlobEntity from 'client/features/data-availability/components/entity/BlobEntity'; +import { useMultichainContext } from 'client/features/multichain/context'; import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/client/slices/tx/components/entity/TxEntity.tsx b/client/slices/tx/components/entity/TxEntity.tsx index 7047ea3442..d32be606d0 100644 --- a/client/slices/tx/components/entity/TxEntity.tsx +++ b/client/slices/tx/components/entity/TxEntity.tsx @@ -5,8 +5,9 @@ import React from 'react'; import { route } from 'nextjs/routes'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import * as EntityBase from 'ui/shared/entities/base/components'; import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; diff --git a/client/slices/tx/hooks/useTxsSocketTypeAddress.ts b/client/slices/tx/hooks/useTxsSocketTypeAddress.ts index a988c5d9e5..c5cd3895b4 100644 --- a/client/slices/tx/hooks/useTxsSocketTypeAddress.ts +++ b/client/slices/tx/hooks/useTxsSocketTypeAddress.ts @@ -12,10 +12,11 @@ import { getResourceKey } from 'client/api/hooks/useApiQuery'; import useSocketChannel from 'client/api/socket/useSocketChannel'; import useSocketMessage from 'client/api/socket/useSocketMessage'; +import { useMultichainContext } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; import { sortTxsFromSocket } from '../utils/sort-txs'; diff --git a/client/slices/tx/pages/details/TxSubHeading.tsx b/client/slices/tx/pages/details/TxSubHeading.tsx index afcf3a3eba..6181650483 100644 --- a/client/slices/tx/pages/details/TxSubHeading.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.tsx @@ -10,13 +10,13 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import { useMultichainContext } from 'client/features/multichain/context'; import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; import { NOVES_TRANSLATE } from 'client/features/tx-interpretation/noves/stubs'; import { createNovesSummaryObject } from 'client/features/tx-interpretation/noves/utils/createNovesSummaryObject'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; diff --git a/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx index bb9fb147e7..f0e9448f14 100644 --- a/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx @@ -9,7 +9,8 @@ import { route } from 'nextjs/routes'; import TokenTransferSnippet from 'client/slices/token-transfer/components/snippet/TokenTransferSnippet'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import { Link } from 'toolkit/chakra/link'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/slices/tx/pages/index/list/TxsList.tsx b/client/slices/tx/pages/index/list/TxsList.tsx index 133e97f889..9c41579922 100644 --- a/client/slices/tx/pages/index/list/TxsList.tsx +++ b/client/slices/tx/pages/index/list/TxsList.tsx @@ -6,13 +6,12 @@ import React from 'react'; import type { Transaction } from 'client/slices/tx/types/api'; import type { TxsSocketType } from 'client/slices/tx/types/socket'; +import { useMultichainContext } from 'client/features/multichain/context'; import type { TxsTranslationQuery } from 'client/features/tx-interpretation/noves/hooks/useDescribeTxs'; import useInitialList from 'client/shared/lists/useInitialList'; import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; -import { useMultichainContext } from 'lib/contexts/multichain'; - import TxsSocketNotice from './socket/TxsSocketNotice'; import TxsListItem from './TxsListItem'; diff --git a/client/slices/tx/pages/index/list/TxsListItem.tsx b/client/slices/tx/pages/index/list/TxsListItem.tsx index fcd9641af5..6de9dbf7fc 100644 --- a/client/slices/tx/pages/index/list/TxsListItem.tsx +++ b/client/slices/tx/pages/index/list/TxsListItem.tsx @@ -6,9 +6,9 @@ import { } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { NovesDescribeTxsResponse } from 'client/features/tx-interpretation/noves/types/api'; import type { Transaction } from 'client/slices/tx/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; diff --git a/client/slices/tx/pages/index/list/TxsTable.tsx b/client/slices/tx/pages/index/list/TxsTable.tsx index 1c4e4713f7..f5160df3a2 100644 --- a/client/slices/tx/pages/index/list/TxsTable.tsx +++ b/client/slices/tx/pages/index/list/TxsTable.tsx @@ -7,6 +7,7 @@ import type { TxsSocketType } from 'client/slices/tx/types/socket'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import { useMultichainContext } from 'client/features/multichain/context'; import type { TxsTranslationQuery } from 'client/features/tx-interpretation/noves/hooks/useDescribeTxs'; import { currencyUnits } from 'client/shared/chain/units'; @@ -15,7 +16,6 @@ import useInitialList from 'client/shared/lists/useInitialList'; import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/client/slices/tx/pages/index/list/TxsTableItem.tsx b/client/slices/tx/pages/index/list/TxsTableItem.tsx index aba8adbce9..88d0de264b 100644 --- a/client/slices/tx/pages/index/list/TxsTableItem.tsx +++ b/client/slices/tx/pages/index/list/TxsTableItem.tsx @@ -3,9 +3,9 @@ import { Flex, VStack } from '@chakra-ui/react'; import React from 'react'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import type { NovesDescribeTxsResponse } from 'client/features/tx-interpretation/noves/types/api'; import type { Transaction } from 'client/slices/tx/types/api'; -import type { ClusterChainConfig } from 'types/multichain'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockPendingUpdateHint from 'client/slices/block/components/BlockPendingUpdateHint'; diff --git a/client/slices/tx/pages/index/list/TxsTabs.tsx b/client/slices/tx/pages/index/list/TxsTabs.tsx index c6d9451b65..0e45d6f9e1 100644 --- a/client/slices/tx/pages/index/list/TxsTabs.tsx +++ b/client/slices/tx/pages/index/list/TxsTabs.tsx @@ -12,6 +12,7 @@ import { TX } from 'client/slices/tx/stubs/tx'; import useIsAuth from 'client/features/account/hooks/useIsAuth'; import TxsWatchlist from 'client/features/account/pages/tx-index-watchlist/TxsWatchlist'; import AdvancedFilterLink from 'client/features/advanced-filter/components/AdvancedFilterLink'; +import { useMultichainContext } from 'client/features/multichain/context'; import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; @@ -19,7 +20,6 @@ import useIsMobile from 'client/shared/hooks/useIsMobile'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { generateListStub } from 'stubs/utils'; import type { RoutedTabsProps } from 'toolkit/components/RoutedTabs/RoutedTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; diff --git a/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx index 4c9dec7a00..8759af9797 100644 --- a/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx +++ b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx @@ -8,7 +8,8 @@ import { route } from 'nextjs/routes'; import useNewTxsSocketTypeAll from 'client/slices/tx/hooks/useTxsSocketTypeAll'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; interface Props { diff --git a/client/slices/tx/pages/index/stats/TxsStats.tsx b/client/slices/tx/pages/index/stats/TxsStats.tsx index 31f24754bd..683ed46c81 100644 --- a/client/slices/tx/pages/index/stats/TxsStats.tsx +++ b/client/slices/tx/pages/index/stats/TxsStats.tsx @@ -9,8 +9,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { HOMEPAGE_STATS } from 'client/slices/home/stubs'; import { TXS_STATS, TXS_STATS_MICROSERVICE } from 'client/slices/tx/stubs/tx'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import getStatsLabelFromTitle from 'lib/stats/getStatsLabelFromTitle'; import { thinsp } from 'toolkit/utils/htmlEntities'; import StatsWidget from 'ui/shared/stats/StatsWidget'; diff --git a/configs/app/features/multichain-button.ts b/configs/app/features/multichain-button.ts index 645da33ba3..a513b5e69d 100644 --- a/configs/app/features/multichain-button.ts +++ b/configs/app/features/multichain-button.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { MultichainProviderConfig, MultichainProviderConfigParsed } from 'types/client/multichainProviderConfig'; +import type { MultichainProviderConfig, MultichainProviderConfigParsed } from 'client/features/multichain-button/types/client'; import { getEnvValue, parseEnvJson } from '../utils'; import marketplace from './marketplace'; diff --git a/configs/essential-dapps-chains/config.nodejs.ts b/configs/essential-dapps-chains/config.nodejs.ts index af81cabb57..569e06f310 100644 --- a/configs/essential-dapps-chains/config.nodejs.ts +++ b/configs/essential-dapps-chains/config.nodejs.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { MultichainConfig } from 'types/multichain'; +import type { MultichainConfig } from 'client/features/multichain/types/client'; let value: MultichainConfig | undefined = undefined; diff --git a/configs/multichain/config.nodejs.ts b/configs/multichain/config.nodejs.ts index dfe96b61a9..961bbc6ff1 100644 --- a/configs/multichain/config.nodejs.ts +++ b/configs/multichain/config.nodejs.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { MultichainConfig } from 'types/multichain'; +import type { MultichainConfig } from 'client/features/multichain/types/client'; let value: MultichainConfig | undefined = undefined; diff --git a/configs/multichain/index.ts b/configs/multichain/index.ts index 77694c5e75..f983c88618 100644 --- a/configs/multichain/index.ts +++ b/configs/multichain/index.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { MultichainConfig } from 'types/multichain'; +import type { MultichainConfig } from 'client/features/multichain/types/client'; import config from 'configs/app'; import { isBrowser } from 'toolkit/utils/isBrowser'; diff --git a/deploy/tools/envs-validator/schemas/features/multichainButton.ts b/deploy/tools/envs-validator/schemas/features/multichainButton.ts index 236739990a..adfb88c8e9 100644 --- a/deploy/tools/envs-validator/schemas/features/multichainButton.ts +++ b/deploy/tools/envs-validator/schemas/features/multichainButton.ts @@ -1,5 +1,5 @@ import { replaceQuotes } from 'configs/app/utils'; -import { MultichainProviderConfig } from 'types/client/multichainProviderConfig'; +import { MultichainProviderConfig } from 'client/features/multichain-button/types/client'; import * as yup from 'yup'; const multichainProviderConfigSchema: yup.ObjectSchema = yup.object({ diff --git a/deploy/tools/multichain-config-generator/index.ts b/deploy/tools/multichain-config-generator/index.ts index 0dd75eb9a4..0bc4d09014 100644 --- a/deploy/tools/multichain-config-generator/index.ts +++ b/deploy/tools/multichain-config-generator/index.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; import { Worker } from 'node:worker_threads'; import { delay } from 'es-toolkit'; -import { ClusterChainConfig } from 'types/multichain'; +import { ClusterChainConfig } from 'client/features/multichain/types/client'; import appConfig from 'configs/app'; const currentFilePath = fileURLToPath(import.meta.url); diff --git a/global.d.ts b/global.d.ts index f0b25ac04b..f3a31546db 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,5 +1,5 @@ import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { MultichainConfig } from 'types/multichain'; +import type { MultichainConfig } from 'client/features/multichain/types/client'; import type { WalletProvider } from 'types/web3'; import 'vitest-fetch-mock'; diff --git a/nextjs/csp/policies/multichain.ts b/nextjs/csp/policies/multichain.ts index 9ee6188b1e..61498c4d36 100644 --- a/nextjs/csp/policies/multichain.ts +++ b/nextjs/csp/policies/multichain.ts @@ -23,7 +23,7 @@ export function multichain(): CspDev.DirectiveDescriptor { 'connect-src': [ ...apiEndpoints, ...rpcEndpoints, - // please see comment in the useFetchParentChainApi.tsx file + // please see comment in the useFetchParentChainApi.ts file 'https://eth.blockscout.com', ], }; diff --git a/pages/accounts/index.tsx b/pages/accounts/index.tsx index 1eb6ba532b..9c8a75e4a3 100644 --- a/pages/accounts/index.tsx +++ b/pages/accounts/index.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const Accounts = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/accounts/MultichainAccounts'); + return import('client/features/multichain/pages/addresses/MultichainAccounts'); } return import('client/slices/address/pages/index/Accounts'); diff --git a/pages/address/[hash]/index.tsx b/pages/address/[hash]/index.tsx index e6b75f8fa6..27c15968e1 100644 --- a/pages/address/[hash]/index.tsx +++ b/pages/address/[hash]/index.tsx @@ -12,10 +12,11 @@ import fetchApi from 'nextjs/utils/fetchApi'; import Address from 'client/slices/address/pages/details/Address'; +import MultichainAddress from 'client/features/multichain/pages/address/MultichainAddress'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import MultichainAddress from 'ui/multichain/address/MultichainAddress'; const pathname: Route['pathname'] = '/address/[hash]'; diff --git a/pages/blocks.tsx b/pages/blocks.tsx index 926b2ce4d0..4aed25dc22 100644 --- a/pages/blocks.tsx +++ b/pages/blocks.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const Blocks = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/blocks/MultichainBlocks'); + return import('client/features/multichain/pages/blocks/MultichainBlocks'); } return import('client/slices/block/pages/index/Blocks'); diff --git a/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx index 62021d50d8..08fc2c07f0 100644 --- a/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx +++ b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; const AccountsLabelSearch = dynamic(() => import('client/features/address-metadata/pages/tag-search/TagSearch'), { ssr: false }); diff --git a/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx index d657319edf..5e25c058c0 100644 --- a/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx +++ b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx @@ -6,8 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; import AdvancedFilter from 'client/features/advanced-filter/pages/index/AdvancedFilter'; - -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; const Page: NextPage = () => { return ( diff --git a/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx b/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx index b381437770..183e815146 100644 --- a/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx +++ b/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const MultichainBlock = dynamic(() => import('ui/multichain/block/MultichainBlock'), { ssr: false }); +const MultichainBlock = dynamic(() => import('client/features/multichain/pages/block/MultichainBlock'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx b/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx index b6e5fd5cc7..a6e8d41b53 100644 --- a/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx +++ b/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; const BlockCountdown = dynamic(() => import('client/slices/block/pages/countdown-details/BlockCountdown'), { ssr: false }); diff --git a/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx b/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx index 0497bb119d..279a8a70b8 100644 --- a/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx +++ b/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; const BlockCountdownIndex = dynamic(() => import('client/slices/block/pages/countdown-index/BlockCountdownIndex'), { ssr: false }); diff --git a/pages/chain/[chain_slug_or_id]/op/[hash].tsx b/pages/chain/[chain_slug_or_id]/op/[hash].tsx index e8aca597b3..1e1809fa9a 100644 --- a/pages/chain/[chain_slug_or_id]/op/[hash].tsx +++ b/pages/chain/[chain_slug_or_id]/op/[hash].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; const UserOp = dynamic(() => import('client/features/user-ops/pages/details/UserOp'), { ssr: false }); diff --git a/pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx b/pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx index c19a4a1daa..35cf161064 100644 --- a/pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx +++ b/pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx @@ -8,7 +8,7 @@ import type { Route } from 'nextjs-routes'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const MultichainToken = dynamic(() => import('ui/multichain/token/MultichainToken'), { ssr: false }); +const MultichainToken = dynamic(() => import('client/features/multichain/pages/token/MultichainToken'), { ssr: false }); const pathname: Route['pathname'] = '/token/[hash]'; diff --git a/pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx b/pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx index b22ff0b137..43d72050d1 100644 --- a/pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx +++ b/pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx @@ -8,7 +8,7 @@ import type { Route } from 'nextjs-routes'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const MultichainTokenInstance = dynamic(() => import('ui/multichain/tokenInstance/MultichainTokenInstance'), { ssr: false }); +const MultichainTokenInstance = dynamic(() => import('client/features/multichain/pages/token-instance/MultichainTokenInstance'), { ssr: false }); const pathname: Route['pathname'] = '/token/[hash]/instance/[id]'; diff --git a/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx b/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx index 29cd4f6461..e9713f87e6 100644 --- a/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx +++ b/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const MultichainTx = dynamic(() => import('ui/multichain/tx/MultichainTx'), { ssr: false }); +const MultichainTx = dynamic(() => import('client/features/multichain/pages/tx/MultichainTx'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx b/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx index c2088c9c66..09dbbfc8c0 100644 --- a/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx +++ b/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx @@ -5,7 +5,8 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import { MultichainProvider } from 'lib/contexts/multichain'; +import { MultichainProvider } from 'client/features/multichain/context'; + import Sol2Uml from 'ui/pages/Sol2Uml'; const Page: NextPage = () => { diff --git a/pages/ecosystems.tsx b/pages/ecosystems.tsx index 06bce1fbcb..54351f78cf 100644 --- a/pages/ecosystems.tsx +++ b/pages/ecosystems.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const MultichainEcosystems = dynamic(() => import('ui/multichain/ecosystems/MultichainEcosystems'), { ssr: false }); +const MultichainEcosystems = dynamic(() => import('client/features/multichain/pages/ecosystems/MultichainEcosystems'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/pages/index.tsx b/pages/index.tsx index bd2a72e18b..f2eee195de 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,8 +8,9 @@ import PageNextJs from 'nextjs/PageNextJs'; import Home from 'client/slices/home/pages/index/Home'; +import MultichainHome from 'client/features/multichain/pages/home/MultichainHome'; + import config from 'configs/app'; -import MultichainHome from 'ui/multichain/home/MultichainHome'; import LayoutHome from 'ui/shared/layout/LayoutHome'; const Page: NextPageWithLayout = () => { diff --git a/pages/internal-txs.tsx b/pages/internal-txs.tsx index aa81d008f0..511ae70c09 100644 --- a/pages/internal-txs.tsx +++ b/pages/internal-txs.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const InternalTxs = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/internalTxs/MultichainInternalTxs'); + return import('client/features/multichain/pages/internal-txs/MultichainInternalTxs'); } return import('client/slices/internal-tx/pages/index/InternalTxs'); diff --git a/pages/ops.tsx b/pages/ops.tsx index b7f16f2a15..0cd36fc39f 100644 --- a/pages/ops.tsx +++ b/pages/ops.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const UserOps = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/userOps/MultichainUserOps'); + return import('client/features/multichain/pages/user-ops/MultichainUserOps'); } return import('client/features/user-ops/pages/index/UserOps'); diff --git a/pages/stats/[id].tsx b/pages/stats/[id].tsx index ca11fce9dd..60c7ca1c2a 100644 --- a/pages/stats/[id].tsx +++ b/pages/stats/[id].tsx @@ -11,10 +11,11 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import { MultichainProvider } from 'client/features/multichain/context'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { MultichainProvider } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; const ChainStatsDetails = dynamic(() => import('client/features/chain-stats/pages/details/ChainStatsDetails'), { ssr: false }); diff --git a/pages/stats/index.tsx b/pages/stats/index.tsx index e85d5363b1..3bf683117f 100644 --- a/pages/stats/index.tsx +++ b/pages/stats/index.tsx @@ -6,9 +6,9 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; import ChainStatsIndex from 'client/features/chain-stats/pages/index/ChainStatsIndex'; +import MultichainStats from 'client/features/multichain/pages/stats/MultichainStats'; import config from 'configs/app'; -import MultichainStats from 'ui/multichain/stats/MultichainStats'; const Page: NextPage = () => { return ( diff --git a/pages/token-transfers.tsx b/pages/token-transfers.tsx index 7652dceff8..28ab326e02 100644 --- a/pages/token-transfers.tsx +++ b/pages/token-transfers.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const TokenTransfers = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/tokenTransfers/MultichainTokenTransfers'); + return import('client/features/multichain/pages/token-transfers/MultichainTokenTransfers'); } return import('client/slices/token-transfer/pages/index/TokenTransfers'); diff --git a/pages/tokens.tsx b/pages/tokens.tsx index e95e456f2d..eff4f42817 100644 --- a/pages/tokens.tsx +++ b/pages/tokens.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const Tokens = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/tokens/MultichainTokens'); + return import('client/features/multichain/pages/tokens/MultichainTokens'); } return import('client/slices/token/pages/index/Tokens'); diff --git a/pages/txs/index.tsx b/pages/txs/index.tsx index dee981a27d..87d50ae50e 100644 --- a/pages/txs/index.tsx +++ b/pages/txs/index.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const Transactions = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/txs/MultichainTxs'); + return import('client/features/multichain/pages/txs/MultichainTxs'); } if (config.features.zetachain.isEnabled) { diff --git a/pages/verified-contracts.tsx b/pages/verified-contracts.tsx index cf9a3d8588..6a0d592c6d 100644 --- a/pages/verified-contracts.tsx +++ b/pages/verified-contracts.tsx @@ -10,7 +10,7 @@ import config from 'configs/app'; const VerifiedContracts = dynamic(() => { if (config.features.multichain.isEnabled) { - return import('ui/multichain/verifiedContracts/MultichainVerifiedContracts'); + return import('client/features/multichain/pages/contracts/MultichainVerifiedContracts'); } return import('client/slices/contract/pages/index/VerifiedContracts'); diff --git a/playwright/fixtures/mockEssentialDappsChainsConfig.ts b/playwright/fixtures/mockEssentialDappsChainsConfig.ts index 7928578a64..8bed86838f 100644 --- a/playwright/fixtures/mockEssentialDappsChainsConfig.ts +++ b/playwright/fixtures/mockEssentialDappsChainsConfig.ts @@ -1,6 +1,6 @@ import type { TestFixture, Page } from '@playwright/test'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; export type MockEssentialDappsChainsConfigFixture = () => Promise; diff --git a/playwright/fixtures/mockMultichainConfig.ts b/playwright/fixtures/mockMultichainConfig.ts index 073b944a2a..3e87ef83b2 100644 --- a/playwright/fixtures/mockMultichainConfig.ts +++ b/playwright/fixtures/mockMultichainConfig.ts @@ -1,6 +1,6 @@ import type { TestFixture, Page } from '@playwright/test'; -import * as chainDataMock from 'mocks/multichain/chains'; +import * as chainDataMock from 'client/features/multichain/mocks/chains'; export type MockMultichainConfigFixture = () => Promise; diff --git a/toolkit/components/charts/components/ChartMenu.tsx b/toolkit/components/charts/components/ChartMenu.tsx index 56d614f672..6110a0426d 100644 --- a/toolkit/components/charts/components/ChartMenu.tsx +++ b/toolkit/components/charts/components/ChartMenu.tsx @@ -5,13 +5,14 @@ import { useCopyToClipboard } from '@uidotdev/usehooks'; import domToImage from 'dom-to-image'; import React from 'react'; +import { useMultichainContext } from 'client/features/multichain/context'; + import CopyIcon from 'icons/copy.svg'; import DotsIcon from 'icons/dots.svg'; import CsvIcon from 'icons/files/csv.svg'; import ImageIcon from 'icons/files/image.svg'; import ScopeIcon from 'icons/scope.svg'; import ShareIcon from 'icons/share.svg'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { useColorModeValue } from '../../../chakra/color-mode'; import { IconButton } from '../../../chakra/icon-button'; diff --git a/types/externalChains.ts b/types/externalChains.ts index 41df69f790..407af7da86 100644 --- a/types/externalChains.ts +++ b/types/externalChains.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { ClusterChainConfig } from 'types/multichain'; +import type { ClusterChainConfig } from 'client/features/multichain/types/client'; // minimal set of fields for external chains export interface ExternalChain { diff --git a/types/multichain.ts b/types/multichain.ts deleted file mode 100644 index 155d49ed76..0000000000 --- a/types/multichain.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { ExternalChain } from 'types/externalChains'; - -import type config from 'configs/app'; - -export interface ClusterChainConfig extends ExternalChain { - slug: string; - app_config: typeof config; -} - -export interface MultichainConfig { - chains: Array; -} diff --git a/ui/pages/Sol2Uml.tsx b/ui/pages/Sol2Uml.tsx index 05e40e27d1..0bed5c7091 100644 --- a/ui/pages/Sol2Uml.tsx +++ b/ui/pages/Sol2Uml.tsx @@ -6,7 +6,8 @@ import React from 'react'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import PageTitle from 'ui/shared/Page/PageTitle'; import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram'; diff --git a/ui/shared/EntityTags/EntityTag.tsx b/ui/shared/EntityTags/EntityTag.tsx index a8331b3980..b0763150d1 100644 --- a/ui/shared/EntityTags/EntityTag.tsx +++ b/ui/shared/EntityTags/EntityTag.tsx @@ -5,9 +5,10 @@ import React from 'react'; import type { EntityTag as TEntityTag } from './types'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as mixpanel from 'client/shared/analytics/mixpanel'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Link, LinkExternalIcon } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; diff --git a/ui/shared/EntityTags/utils.ts b/ui/shared/EntityTags/utils.ts index 6f888b3d11..face198b78 100644 --- a/ui/shared/EntityTags/utils.ts +++ b/ui/shared/EntityTags/utils.ts @@ -4,7 +4,7 @@ import type { EntityTag } from './types'; import { route } from 'nextjs/routes'; -import type { TMultichainContext } from 'lib/contexts/multichain'; +import type { TMultichainContext } from 'client/features/multichain/context'; export function getTagLinkParams(data: EntityTag, multichainContext?: TMultichainContext | null): { type: 'external' | 'internal'; href: string } | undefined { if (data.meta?.warpcastHandle) { diff --git a/ui/shared/entities/base/utils.ts b/ui/shared/entities/base/utils.ts index 9814f712f9..1e5d978555 100644 --- a/ui/shared/entities/base/utils.ts +++ b/ui/shared/entities/base/utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { TMultichainContext } from 'lib/contexts/multichain'; +import type { TMultichainContext } from 'client/features/multichain/context'; import type { IconBaseProps, EntityBaseProps } from './components'; diff --git a/ui/shared/entities/nft/NftEntity.tsx b/ui/shared/entities/nft/NftEntity.tsx index e3d2733166..acce30c91a 100644 --- a/ui/shared/entities/nft/NftEntity.tsx +++ b/ui/shared/entities/nft/NftEntity.tsx @@ -7,7 +7,8 @@ import type { TokenInstance } from 'client/slices/token/types/api'; import { route } from 'nextjs/routes'; -import { useMultichainContext } from 'lib/contexts/multichain'; +import { useMultichainContext } from 'client/features/multichain/context'; + import * as EntityBase from 'ui/shared/entities/base/components'; import NftMedia from 'ui/shared/nft/NftMedia'; diff --git a/ui/shared/pagination/useQueryWithPages.ts b/ui/shared/pagination/useQueryWithPages.ts index 7c934a4437..48df0470a9 100644 --- a/ui/shared/pagination/useQueryWithPages.ts +++ b/ui/shared/pagination/useQueryWithPages.ts @@ -17,11 +17,12 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import type { PaginatedResourceName, PaginationFilters, PaginationSorting, ResourceError, ResourcePayload } from 'client/api/resources'; import { SORTING_FIELDS } from 'client/api/resources'; +import { useMultichainContext } from 'client/features/multichain/context'; +import getChainValueFromQuery from 'client/features/multichain/utils/get-chain-value-from-query'; + import getQueryParamString from 'client/shared/router/get-query-param-string'; import multichainConfig from 'configs/multichain'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import getChainValueFromQuery from 'lib/multichain/getChainValueFromQuery'; type NextPageParams = Record; diff --git a/ui/shared/reCaptcha/ReCaptcha.tsx b/ui/shared/reCaptcha/ReCaptcha.tsx index ea8d3ccc1c..6a3457dcd1 100644 --- a/ui/shared/reCaptcha/ReCaptcha.tsx +++ b/ui/shared/reCaptcha/ReCaptcha.tsx @@ -3,8 +3,9 @@ import React from 'react'; import ReCaptcha from 'react-google-recaptcha'; +import { useMultichainContext } from 'client/features/multichain/context'; + import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; import { Alert } from 'toolkit/chakra/alert'; import { Link } from 'toolkit/chakra/link'; From 2e5b298f37786429f06d2a3bf06c8eaef8b04c28 Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 21 May 2026 20:02:13 +0200 Subject: [PATCH 25/85] =?UTF-8?q?track:=206-13=20in=20progress=20=E2=80=94?= =?UTF-8?q?=20blockscout/frontend#3483?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/MIGRATION_TASKS.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 21d1d63190..143dbfca09 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -238,8 +238,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-12 · [ ] Feature: `ad-banner` **Scope:** Includes `lib/hooks/useAdblockDetect` → `client/features/ad-banner/`. Covers both `adsBanner.ts` and `adsText.ts` configs. -### 6-13 · [ ] Feature: `safe-address-tags` -**Scope:** Includes `lib/hooks/useIsSafeAddress` → `client/features/safe-address-tags/`. See `configs/app/features/safe.ts`. +### 6-13 · [~] Features: `cross-chain-txs`, `gas-tracker`, `sol2uml` · [#3483](https://github.com/blockscout/frontend/issues/3483) ### 6-14 · [ ] Feature: `metasuites` **Scope:** Includes `lib/hooks/useNotifyOnNavigation` → `client/features/metasuites/` @@ -248,14 +247,14 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-16 · [~] Feature: `pools` · [#3463](https://github.com/blockscout/frontend/issues/3463) -### 6-17 · [ ] Feature: `hot-contracts` +### 6-17 · [x] Feature: `hot-contracts` -### 6-18 · [ ] Feature: `interchain-indexer` +### 6-18 · [x] Feature: `interchain-indexer` **Scope:** Cross-chain message indexer (not ZetaChain). See `configs/app/features/crossChainTxs.ts`. ### 6-19 · [x] Feature: `mud-framework` -### 6-20 · [ ] Feature: `visualize` +### 6-20 · [x] Feature: `visualize` **Scope:** Solidity-to-UML diagrams. See `configs/app/features/sol2uml.ts`. ### 6-21 · [~] Feature: `tx-interpretation` · [#3471](https://github.com/blockscout/frontend/issues/3471) @@ -266,12 +265,12 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-23 · [ ] Feature: `address-widgets` **Scope:** Third-party widgets on address pages. See `configs/app/features/address3rdPartyWidgets.ts`. -### 6-24 · [ ] Feature: `address-metadata` +### 6-24 · [x] Feature: `address-metadata` ### 6-25 · [x] Feature: `address-verification` **Scope:** Covers `addressProfileAPI.ts` and `addressVerification.ts` configs. -### 6-26 · [ ] Feature: `bridged-tokens` +### 6-26 · [x] Feature: `bridged-tokens` ### 6-27 · [~] Features: `web3-wallet` and `connect-wallet` · [#3480](https://github.com/blockscout/frontend/issues/3480) **Scope:** Includes `blockchainInteraction.ts` config. From 4f5c42b81b84a79efb7b523ef4ded1715d91767b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 20:03:44 +0200 Subject: [PATCH 26/85] [Migration 6-27] Features: web3-wallet and connect-wallet (#3482) * [Migration 6-27] Features: web3-wallet and connect-wallet Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/116ee604-9f2e-4f3c-b949-deb07ac2ddfb Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * [skip ci] fixes --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> Co-authored-by: tom --- .../screens/AuthModalScreenSuccessEmail.tsx | 2 +- .../user-profile/UserProfileDesktop.tsx | 2 +- .../user-profile/auth0/UserProfileContent.tsx | 6 ++--- .../user-profile/auth0/UserProfileDesktop.tsx | 4 ++-- .../user-profile/auth0/UserProfileMobile.tsx | 4 ++-- .../account/hooks/useSignInWithWallet.ts | 2 +- .../account/pages/profile/MyProfile.tsx | 2 +- .../AddressVerificationModal.tsx | 6 ++--- .../components}/Web3Provider.tsx | 2 +- .../components}/providers/DynamicProvider.tsx | 2 +- .../components}/providers/ReownProvider.tsx | 2 +- .../components}/providers/WagmiProvider.tsx | 0 .../connect-wallet/hooks/useAccount.ts | 2 +- .../connect-wallet/hooks/useWallet.ts | 2 +- .../connect-wallet/utils/wagmi-config.ts | 2 +- .../MultichainEcosystemsTableItem.tsx | 6 ++--- .../multichain/pages/home/ChainWidget.tsx | 7 +++--- .../components/OptimisticL2ClaimButton.tsx | 2 +- .../tx-interpretation/common/types/config.ts | 4 +--- .../components}/NetworkAddToWallet.tsx | 7 +++--- .../components/TokenAddToWallet.tsx | 6 ++--- .../web3-wallet/hooks/useAddChain.ts} | 2 +- .../web3-wallet/hooks}/useAddChainClick.ts | 0 .../hooks}/useDetectWalletEip6963.ts | 4 ++-- .../web3-wallet/hooks/useProvider.ts} | 2 +- .../web3-wallet/hooks/useSwitchChain.ts} | 2 +- .../web3-wallet/hooks/useSwitchOrAddChain.ts} | 0 client/features/web3-wallet/types/config.ts | 12 ++++++++++ .../web3-wallet/types/wallet-provider.ts | 2 +- .../web3-wallet/types}/wallets.ts | 12 ++++++++-- .../web3-wallet/utils}/detect-wallet.ts | 4 ++-- .../utils/get-hexadecimal-chain-id.ts} | 0 client/shared/analytics/mixpanel/utils.ts | 2 +- .../methods/alerts/ConnectWalletAlert.tsx | 2 +- .../methods/form/ContractMethodForm.tsx | 2 +- .../methods/useCallMethodWalletClient.ts | 2 +- .../pages/details/methods/useFormSubmit.ts | 2 +- .../components/LogDecodedInputDataTable.tsx | 5 ++--- ...chain-interaction.ts => connect-wallet.ts} | 0 configs/app/features/index.ts | 2 +- configs/app/features/marketplace.ts | 4 ++-- configs/app/features/rewards.ts | 4 ++-- configs/app/features/validators.ts | 3 +-- configs/app/features/web3-wallet.ts | 4 ++-- deploy/tools/envs-validator/schema.ts | 4 ++-- global.d.ts | 2 +- nextjs/csp/generateCspPolicy.ts | 2 +- ...kchainInteraction.ts => connect-wallet.ts} | 4 ++-- nextjs/csp/policies/index.ts | 2 +- pages/_app.tsx | 2 +- playwright/fixtures/injectMetaMaskProvider.ts | 2 +- stubs/utils.ts | 4 +--- types/client/adProviders.ts | 8 +++---- types/client/wallets.ts | 22 ------------------- types/networks.ts | 4 +--- types/utils.ts | 4 ---- types/views/block.ts | 4 +--- .../monaco/CodeEditorSearchResultItem.tsx | 5 ++--- ui/shared/monaco/types.ts | 9 ++++---- ui/shared/monaco/utils/sortFileTree.ts | 3 +-- ui/snippets/footer/Footer.tsx | 3 ++- ui/snippets/header/HeaderMobile.tsx | 2 +- ui/snippets/topBar/TopBar.tsx | 4 ++-- 63 files changed, 109 insertions(+), 125 deletions(-) rename {ui/shared/web3 => client/features/connect-wallet/components}/Web3Provider.tsx (92%) rename {ui/shared/web3 => client/features/connect-wallet/components}/providers/DynamicProvider.tsx (98%) rename {ui/shared/web3 => client/features/connect-wallet/components}/providers/ReownProvider.tsx (97%) rename {ui/shared/web3 => client/features/connect-wallet/components}/providers/WagmiProvider.tsx (100%) rename {ui/shared => client/features/web3-wallet/components}/NetworkAddToWallet.tsx (84%) rename client/{shared/web3/useAddChain.tsx => features/web3-wallet/hooks/useAddChain.ts} (96%) rename client/{shared/web3 => features/web3-wallet/hooks}/useAddChainClick.ts (100%) rename client/{shared/web3 => features/web3-wallet/hooks}/useDetectWalletEip6963.ts (94%) rename client/{shared/web3/useProvider.tsx => features/web3-wallet/hooks/useProvider.ts} (98%) rename client/{shared/web3/useSwitchChain.tsx => features/web3-wallet/hooks/useSwitchChain.ts} (93%) rename client/{shared/web3/useSwitchOrAddChain.tsx => features/web3-wallet/hooks/useSwitchOrAddChain.ts} (100%) create mode 100644 client/features/web3-wallet/types/config.ts rename types/web3.ts => client/features/web3-wallet/types/wallet-provider.ts (96%) rename client/{shared/web3 => features/web3-wallet/types}/wallets.ts (71%) rename client/{shared/web3 => features/web3-wallet/utils}/detect-wallet.ts (95%) rename client/{shared/web3/utils.ts => features/web3-wallet/utils/get-hexadecimal-chain-id.ts} (100%) rename configs/app/features/{blockchain-interaction.ts => connect-wallet.ts} (100%) rename nextjs/csp/policies/{blockchainInteraction.ts => connect-wallet.ts} (95%) delete mode 100644 types/client/wallets.ts diff --git a/client/features/account/components/auth-modal/screens/AuthModalScreenSuccessEmail.tsx b/client/features/account/components/auth-modal/screens/AuthModalScreenSuccessEmail.tsx index e1694e8d45..2db4a70fe2 100644 --- a/client/features/account/components/auth-modal/screens/AuthModalScreenSuccessEmail.tsx +++ b/client/features/account/components/auth-modal/screens/AuthModalScreenSuccessEmail.tsx @@ -47,7 +47,7 @@ const AuthModalScreenSuccessEmail = ({ email, onConnectWallet, onClose, isAuth, { email }{ ' ' } email has been successfully used to log in to your Blockscout account. - { !profile?.address_hash && config.features.blockchainInteraction.isEnabled ? ( + { !profile?.address_hash && config.features.connectWallet.isEnabled ? ( <> Add your web3 wallet to safely interact with smart contracts and dapps inside Blockscout. diff --git a/client/features/account/components/user-profile/UserProfileDesktop.tsx b/client/features/account/components/user-profile/UserProfileDesktop.tsx index 6e94cc0319..01ce5564e3 100644 --- a/client/features/account/components/user-profile/UserProfileDesktop.tsx +++ b/client/features/account/components/user-profile/UserProfileDesktop.tsx @@ -27,7 +27,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) => return null; } } - if (config.features.blockchainInteraction.isEnabled) { + if (config.features.connectWallet.isEnabled) { return ; } return null; diff --git a/client/features/account/components/user-profile/auth0/UserProfileContent.tsx b/client/features/account/components/user-profile/auth0/UserProfileContent.tsx index 8ef172e39d..fdaf1cac0b 100644 --- a/client/features/account/components/user-profile/auth0/UserProfileContent.tsx +++ b/client/features/account/components/user-profile/auth0/UserProfileContent.tsx @@ -77,7 +77,7 @@ const UserProfileContent = ({ data, onClose, onLogin, onAddEmail, onAddAddress } return ( { isAutoConnectDisabled && } - { config.features.blockchainInteraction.isEnabled && } + { config.features.connectWallet.isEnabled && } ); @@ -96,7 +96,7 @@ const UserProfileContent = ({ data, onClose, onLogin, onAddEmail, onAddAddress } borderRadius="base" color="text.secondary" > - { config.features.blockchainInteraction.isEnabled && ( + { config.features.connectWallet.isEnabled && ( Address - { config.features.blockchainInteraction.isEnabled && } + { config.features.connectWallet.isEnabled && } { navLinks.map((item) => ( diff --git a/client/features/account/components/user-profile/auth0/UserProfileDesktop.tsx b/client/features/account/components/user-profile/auth0/UserProfileDesktop.tsx index 2ac007a3cc..655e85cc93 100644 --- a/client/features/account/components/user-profile/auth0/UserProfileDesktop.tsx +++ b/client/features/account/components/user-profile/auth0/UserProfileDesktop.tsx @@ -25,7 +25,7 @@ interface Props { } const initialScreen = { - type: config.features.blockchainInteraction.isEnabled ? 'select_method' as const : 'email' as const, + type: config.features.connectWallet.isEnabled ? 'select_method' as const : 'email' as const, }; const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) => { @@ -45,7 +45,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) => return; } - if (router.pathname === '/apps/[id]' && config.features.blockchainInteraction.isEnabled) { + if (router.pathname === '/apps/[id]' && config.features.connectWallet.isEnabled) { setAuthInitialScreen({ type: 'connect_wallet', loginToRewards: true }); } diff --git a/client/features/account/components/user-profile/auth0/UserProfileMobile.tsx b/client/features/account/components/user-profile/auth0/UserProfileMobile.tsx index 9b9efb48a7..29fa1e0649 100644 --- a/client/features/account/components/user-profile/auth0/UserProfileMobile.tsx +++ b/client/features/account/components/user-profile/auth0/UserProfileMobile.tsx @@ -19,7 +19,7 @@ import UserProfileButton from './UserProfileButton'; import UserProfileContent from './UserProfileContent'; const initialScreen = { - type: config.features.blockchainInteraction.isEnabled ? 'select_method' as const : 'email' as const, + type: config.features.connectWallet.isEnabled ? 'select_method' as const : 'email' as const, }; const UserProfileMobile = () => { @@ -39,7 +39,7 @@ const UserProfileMobile = () => { return; } - if (router.pathname === '/apps/[id]' && config.features.blockchainInteraction.isEnabled) { + if (router.pathname === '/apps/[id]' && config.features.connectWallet.isEnabled) { setAuthInitialScreen({ type: 'connect_wallet', loginToRewards: true }); } diff --git a/client/features/account/hooks/useSignInWithWallet.ts b/client/features/account/hooks/useSignInWithWallet.ts index 12165e1ac5..d5b03db1ee 100644 --- a/client/features/account/hooks/useSignInWithWallet.ts +++ b/client/features/account/hooks/useSignInWithWallet.ts @@ -176,4 +176,4 @@ function useSignInWithWalletFallback() { return React.useMemo(() => ({ start: () => {}, isPending: false }), [ ]); } -export default config.features.blockchainInteraction.isEnabled ? useSignInWithWallet : useSignInWithWalletFallback; +export default config.features.connectWallet.isEnabled ? useSignInWithWallet : useSignInWithWalletFallback; diff --git a/client/features/account/pages/profile/MyProfile.tsx b/client/features/account/pages/profile/MyProfile.tsx index c1a08e1f58..0a7630035f 100644 --- a/client/features/account/pages/profile/MyProfile.tsx +++ b/client/features/account/pages/profile/MyProfile.tsx @@ -57,7 +57,7 @@ const MyProfile = () => { - { config.features.blockchainInteraction.isEnabled && + { config.features.connectWallet.isEnabled && } { authModal.open && authInitialScreen && diff --git a/client/features/account/pages/verified-addresses/address-verification/AddressVerificationModal.tsx b/client/features/account/pages/verified-addresses/address-verification/AddressVerificationModal.tsx index 742aa64bc5..4d6cb4db6e 100644 --- a/client/features/account/pages/verified-addresses/address-verification/AddressVerificationModal.tsx +++ b/client/features/account/pages/verified-addresses/address-verification/AddressVerificationModal.tsx @@ -12,7 +12,7 @@ import config from 'configs/app'; import { FallbackProvider } from 'lib/contexts/fallback'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; -const Web3ProviderBase = dynamic(() => import('ui/shared/web3/Web3Provider'), { ssr: false }); +const Web3ProviderBase = dynamic(() => import('client/features/connect-wallet/components/Web3Provider'), { ssr: false }); import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress'; import AddressVerificationStepSignature from './steps/AddressVerificationStepSignature'; @@ -20,7 +20,7 @@ import AddressVerificationStepSuccess from './steps/AddressVerificationStepSucce type StateData = AddressVerificationFormFirstStepFields & AddressCheckStatusSuccess & { isToken?: boolean }; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; // Dynamic providers cannot be nested, so a dummy provider is used here const Web3Provider = feature.isEnabled && feature.connectorType === 'dynamic' ? FallbackProvider : Web3ProviderBase; @@ -92,7 +92,7 @@ const AddressVerificationModal = ({ defaultAddress, open, onOpenChange, onSubmit ), }, diff --git a/ui/shared/web3/Web3Provider.tsx b/client/features/connect-wallet/components/Web3Provider.tsx similarity index 92% rename from ui/shared/web3/Web3Provider.tsx rename to client/features/connect-wallet/components/Web3Provider.tsx index 140e9934e8..e20ba8e2e1 100644 --- a/ui/shared/web3/Web3Provider.tsx +++ b/client/features/connect-wallet/components/Web3Provider.tsx @@ -8,7 +8,7 @@ const ReownProvider = dynamic(() => import('./providers/ReownProvider'), { ssr: const DynamicProvider = dynamic(() => import('./providers/DynamicProvider'), { ssr: false }); const WagmiProvider = dynamic(() => import('./providers/WagmiProvider'), { ssr: false }); -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; const Web3Provider = (() => { if (feature.isEnabled && feature.connectorType === 'reown') { diff --git a/ui/shared/web3/providers/DynamicProvider.tsx b/client/features/connect-wallet/components/providers/DynamicProvider.tsx similarity index 98% rename from ui/shared/web3/providers/DynamicProvider.tsx rename to client/features/connect-wallet/components/providers/DynamicProvider.tsx index e730c6f4b9..12e75cdcca 100644 --- a/ui/shared/web3/providers/DynamicProvider.tsx +++ b/client/features/connect-wallet/components/providers/DynamicProvider.tsx @@ -26,7 +26,7 @@ import { castToString } from 'toolkit/utils/guards'; import WagmiProvider from './WagmiProvider'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; const walletConnectors = [ EthereumWalletConnectors, diff --git a/ui/shared/web3/providers/ReownProvider.tsx b/client/features/connect-wallet/components/providers/ReownProvider.tsx similarity index 97% rename from ui/shared/web3/providers/ReownProvider.tsx rename to client/features/connect-wallet/components/providers/ReownProvider.tsx index 148127d930..d2ea38aa42 100644 --- a/ui/shared/web3/providers/ReownProvider.tsx +++ b/client/features/connect-wallet/components/providers/ReownProvider.tsx @@ -15,7 +15,7 @@ import zIndex from 'toolkit/theme/foundations/zIndex'; import WagmiProvider from './WagmiProvider'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; const initReown = () => { try { diff --git a/ui/shared/web3/providers/WagmiProvider.tsx b/client/features/connect-wallet/components/providers/WagmiProvider.tsx similarity index 100% rename from ui/shared/web3/providers/WagmiProvider.tsx rename to client/features/connect-wallet/components/providers/WagmiProvider.tsx diff --git a/client/features/connect-wallet/hooks/useAccount.ts b/client/features/connect-wallet/hooks/useAccount.ts index 21e19a4a2b..d45c3375f4 100644 --- a/client/features/connect-wallet/hooks/useAccount.ts +++ b/client/features/connect-wallet/hooks/useAccount.ts @@ -2,7 +2,7 @@ import config from 'configs/app'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; // eslint-disable-next-line no-nested-ternary const useAccount = (feature.isEnabled && feature.connectorType === 'dynamic') ? diff --git a/client/features/connect-wallet/hooks/useWallet.ts b/client/features/connect-wallet/hooks/useWallet.ts index 03157255ea..c1e981cae5 100644 --- a/client/features/connect-wallet/hooks/useWallet.ts +++ b/client/features/connect-wallet/hooks/useWallet.ts @@ -2,7 +2,7 @@ import config from 'configs/app'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; // eslint-disable-next-line no-nested-ternary const useWallet = (feature.isEnabled && feature.connectorType === 'dynamic') ? diff --git a/client/features/connect-wallet/utils/wagmi-config.ts b/client/features/connect-wallet/utils/wagmi-config.ts index 9f64e132b6..e09a5de94f 100644 --- a/client/features/connect-wallet/utils/wagmi-config.ts +++ b/client/features/connect-wallet/utils/wagmi-config.ts @@ -12,7 +12,7 @@ import appConfig from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; import multichainConfig from 'configs/multichain'; -const feature = appConfig.features.blockchainInteraction; +const feature = appConfig.features.connectWallet; const getChainTransportFromConfig = (config: Partial | undefined, readOnly?: boolean): Record => { if (!config?.chain?.id) { diff --git a/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx index 97126febde..db28a32c5a 100644 --- a/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx +++ b/client/features/multichain/pages/ecosystems/MultichainEcosystemsTableItem.tsx @@ -5,10 +5,10 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'client/features/multichain/types/client'; +import { WALLETS_INFO } from 'client/features/web3-wallet/types/wallets'; -import useAddChainClick from 'client/shared/web3/useAddChainClick'; -import useProvider from 'client/shared/web3/useProvider'; -import { WALLETS_INFO } from 'client/shared/web3/wallets'; +import useAddChainClick from 'client/features/web3-wallet/hooks/useAddChainClick'; +import useProvider from 'client/features/web3-wallet/hooks/useProvider'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/multichain/pages/home/ChainWidget.tsx b/client/features/multichain/pages/home/ChainWidget.tsx index 0edd845925..e1ca6e5a2f 100644 --- a/client/features/multichain/pages/home/ChainWidget.tsx +++ b/client/features/multichain/pages/home/ChainWidget.tsx @@ -5,11 +5,12 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'client/features/multichain/types/client'; +import { WALLETS_INFO } from 'client/features/web3-wallet/types/wallets'; + +import useAddChainClick from 'client/features/web3-wallet/hooks/useAddChainClick'; +import useProvider from 'client/features/web3-wallet/hooks/useProvider'; import useIsMobile from 'client/shared/hooks/useIsMobile'; -import useAddChainClick from 'client/shared/web3/useAddChainClick'; -import useProvider from 'client/shared/web3/useProvider'; -import { WALLETS_INFO } from 'client/shared/web3/wallets'; import { Heading } from 'toolkit/chakra/heading'; import { IconButton } from 'toolkit/chakra/icon-button'; diff --git a/client/features/rollup/optimism/components/OptimisticL2ClaimButton.tsx b/client/features/rollup/optimism/components/OptimisticL2ClaimButton.tsx index d0332e2f30..96eaef2604 100644 --- a/client/features/rollup/optimism/components/OptimisticL2ClaimButton.tsx +++ b/client/features/rollup/optimism/components/OptimisticL2ClaimButton.tsx @@ -17,7 +17,7 @@ const rollupFeature = config.features.rollup; export const canClaimDirectlyGuard = (data: OptimisticL2WithdrawalClaimInfo) => { return ( - config.features.blockchainInteraction.isEnabled && + config.features.connectWallet.isEnabled && Boolean(parentChain) && data.portal_contract_address_hash !== null && data.msg_sender_address_hash !== null && diff --git a/client/features/tx-interpretation/common/types/config.ts b/client/features/tx-interpretation/common/types/config.ts index 36e051626f..2ba4862b6e 100644 --- a/client/features/tx-interpretation/common/types/config.ts +++ b/client/features/tx-interpretation/common/types/config.ts @@ -1,11 +1,9 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ArrayElement } from 'types/utils'; - export const PROVIDERS = [ 'blockscout', 'noves', 'none', ] as const; -export type Provider = ArrayElement; +export type Provider = (typeof PROVIDERS)[number]; diff --git a/ui/shared/NetworkAddToWallet.tsx b/client/features/web3-wallet/components/NetworkAddToWallet.tsx similarity index 84% rename from ui/shared/NetworkAddToWallet.tsx rename to client/features/web3-wallet/components/NetworkAddToWallet.tsx index 4004756b5c..d31b183619 100644 --- a/ui/shared/NetworkAddToWallet.tsx +++ b/client/features/web3-wallet/components/NetworkAddToWallet.tsx @@ -2,14 +2,15 @@ import React from 'react'; -import useAddChainClick from 'client/shared/web3/useAddChainClick'; -import useProvider from 'client/shared/web3/useProvider'; -import { WALLETS_INFO } from 'client/shared/web3/wallets'; +import { WALLETS_INFO } from '../types/wallets'; import config from 'configs/app'; import { Button } from 'toolkit/chakra/button'; import IconSvg from 'ui/shared/IconSvg'; +import useAddChainClick from '../hooks/useAddChainClick'; +import useProvider from '../hooks/useProvider'; + interface Props { source: 'Footer' | 'Top bar'; onAddSuccess?: () => void; diff --git a/client/features/web3-wallet/components/TokenAddToWallet.tsx b/client/features/web3-wallet/components/TokenAddToWallet.tsx index 7f4a459258..cb9b5c41ca 100644 --- a/client/features/web3-wallet/components/TokenAddToWallet.tsx +++ b/client/features/web3-wallet/components/TokenAddToWallet.tsx @@ -4,15 +4,15 @@ import { Box, chakra } from '@chakra-ui/react'; import React from 'react'; import type { WatchAssetParams } from 'viem'; +import { WALLETS_INFO } from 'client/features/web3-wallet/types/wallets'; import type { TokenInfo } from 'client/slices/token/types/api'; import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity'; +import useProvider from 'client/features/web3-wallet/hooks/useProvider'; +import useSwitchOrAddChain from 'client/features/web3-wallet/hooks/useSwitchOrAddChain'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMobile from 'client/shared/hooks/useIsMobile'; -import useProvider from 'client/shared/web3/useProvider'; -import useSwitchOrAddChain from 'client/shared/web3/useSwitchOrAddChain'; -import { WALLETS_INFO } from 'client/shared/web3/wallets'; import config from 'configs/app'; import { IconButton } from 'toolkit/chakra/icon-button'; diff --git a/client/shared/web3/useAddChain.tsx b/client/features/web3-wallet/hooks/useAddChain.ts similarity index 96% rename from client/shared/web3/useAddChain.tsx rename to client/features/web3-wallet/hooks/useAddChain.ts index 55c45f70dc..a498159199 100644 --- a/client/shared/web3/useAddChain.tsx +++ b/client/features/web3-wallet/hooks/useAddChain.ts @@ -9,8 +9,8 @@ import useRewardsActivity from 'client/features/rewards/hooks/useRewardsActivity import config from 'configs/app'; import { SECOND } from 'toolkit/utils/consts'; +import { getHexadecimalChainId } from '../utils/get-hexadecimal-chain-id'; import useProvider from './useProvider'; -import { getHexadecimalChainId } from './utils'; function getParams(chainConfig: typeof config): AddEthereumChainParameter { if (!chainConfig.chain.id) { diff --git a/client/shared/web3/useAddChainClick.ts b/client/features/web3-wallet/hooks/useAddChainClick.ts similarity index 100% rename from client/shared/web3/useAddChainClick.ts rename to client/features/web3-wallet/hooks/useAddChainClick.ts diff --git a/client/shared/web3/useDetectWalletEip6963.ts b/client/features/web3-wallet/hooks/useDetectWalletEip6963.ts similarity index 94% rename from client/shared/web3/useDetectWalletEip6963.ts rename to client/features/web3-wallet/hooks/useDetectWalletEip6963.ts index fe21d84508..671cc04278 100644 --- a/client/shared/web3/useDetectWalletEip6963.ts +++ b/client/features/web3-wallet/hooks/useDetectWalletEip6963.ts @@ -4,8 +4,8 @@ import React from 'react'; -import type { WalletType } from 'types/client/wallets'; -import type { WalletProvider } from 'types/web3'; +import type { WalletType } from '../types/config'; +import type { WalletProvider } from '../types/wallet-provider'; interface EIP6963ProviderInfo { uuid: string; diff --git a/client/shared/web3/useProvider.tsx b/client/features/web3-wallet/hooks/useProvider.ts similarity index 98% rename from client/shared/web3/useProvider.tsx rename to client/features/web3-wallet/hooks/useProvider.ts index f972e9cbc7..b260497c74 100644 --- a/client/shared/web3/useProvider.tsx +++ b/client/features/web3-wallet/hooks/useProvider.ts @@ -8,7 +8,7 @@ import { useMultichainContext } from 'client/features/multichain/context'; import config from 'configs/app'; -import detectWallet from './detect-wallet'; +import detectWallet from '../utils/detect-wallet'; import useDetectWalletEip6963 from './useDetectWalletEip6963'; export default function useProvider() { diff --git a/client/shared/web3/useSwitchChain.tsx b/client/features/web3-wallet/hooks/useSwitchChain.ts similarity index 93% rename from client/shared/web3/useSwitchChain.tsx rename to client/features/web3-wallet/hooks/useSwitchChain.ts index e38779d63d..a6a5c89cca 100644 --- a/client/shared/web3/useSwitchChain.tsx +++ b/client/features/web3-wallet/hooks/useSwitchChain.ts @@ -6,8 +6,8 @@ import { useMultichainContext } from 'client/features/multichain/context'; import config from 'configs/app'; +import { getHexadecimalChainId } from '../utils/get-hexadecimal-chain-id'; import useProvider from './useProvider'; -import { getHexadecimalChainId } from './utils'; function getParams(chainConfig: typeof config): { chainId: string } { if (!chainConfig.chain.id) { diff --git a/client/shared/web3/useSwitchOrAddChain.tsx b/client/features/web3-wallet/hooks/useSwitchOrAddChain.ts similarity index 100% rename from client/shared/web3/useSwitchOrAddChain.tsx rename to client/features/web3-wallet/hooks/useSwitchOrAddChain.ts diff --git a/client/features/web3-wallet/types/config.ts b/client/features/web3-wallet/types/config.ts new file mode 100644 index 0000000000..5129e25c8a --- /dev/null +++ b/client/features/web3-wallet/types/config.ts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +export const SUPPORTED_WALLETS = [ + 'metamask', + 'coinbase', + 'token_pocket', + 'rabby', + 'okx', + 'trust', +] as const; + +export type WalletType = (typeof SUPPORTED_WALLETS)[number]; diff --git a/types/web3.ts b/client/features/web3-wallet/types/wallet-provider.ts similarity index 96% rename from types/web3.ts rename to client/features/web3-wallet/types/wallet-provider.ts index 56081c0f41..a196a4c004 100644 --- a/types/web3.ts +++ b/client/features/web3-wallet/types/wallet-provider.ts @@ -3,7 +3,7 @@ // copied from node_modules/@wagmi/core/src/connectors/injected.ts import type { EIP1193Provider } from 'viem'; -import type { Evaluate } from './utils'; +import type { Evaluate } from 'types/utils'; type WalletProviderFlags = 'isApexWallet' | diff --git a/client/shared/web3/wallets.ts b/client/features/web3-wallet/types/wallets.ts similarity index 71% rename from client/shared/web3/wallets.ts rename to client/features/web3-wallet/types/wallets.ts index a127aa6645..f160df5f31 100644 --- a/client/shared/web3/wallets.ts +++ b/client/features/web3-wallet/types/wallets.ts @@ -1,8 +1,16 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { WalletType, WalletInfo } from 'types/client/wallets'; +import type { IconName } from 'ui/shared/IconSvg'; -export const WALLETS_INFO: Record, WalletInfo> = { +import type { WalletType } from './config'; + +export interface WalletInfo { + name: string; + icon: IconName; + color: string; +} + +export const WALLETS_INFO: Record = { metamask: { name: 'MetaMask', icon: 'wallets/metamask', diff --git a/client/shared/web3/detect-wallet.ts b/client/features/web3-wallet/utils/detect-wallet.ts similarity index 95% rename from client/shared/web3/detect-wallet.ts rename to client/features/web3-wallet/utils/detect-wallet.ts index 303e4018d8..b9b7500ea1 100644 --- a/client/shared/web3/detect-wallet.ts +++ b/client/features/web3-wallet/utils/detect-wallet.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { WalletType } from 'types/client/wallets'; -import type { WalletProvider } from 'types/web3'; +import type { WalletType } from '../types/config'; +import type { WalletProvider } from '../types/wallet-provider'; const isWalletProvider = (wallet: WalletType) => (provider: WalletProvider): boolean | undefined => { switch (wallet) { diff --git a/client/shared/web3/utils.ts b/client/features/web3-wallet/utils/get-hexadecimal-chain-id.ts similarity index 100% rename from client/shared/web3/utils.ts rename to client/features/web3-wallet/utils/get-hexadecimal-chain-id.ts diff --git a/client/shared/analytics/mixpanel/utils.ts b/client/shared/analytics/mixpanel/utils.ts index 269a0eb768..5e15c9678b 100644 --- a/client/shared/analytics/mixpanel/utils.ts +++ b/client/shared/analytics/mixpanel/utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { WalletType } from 'types/client/wallets'; +import type { WalletType } from 'client/features/web3-wallet/types/config'; import type { ColorThemeId } from 'types/settings'; export enum EventTypes { diff --git a/client/slices/contract/pages/details/methods/alerts/ConnectWalletAlert.tsx b/client/slices/contract/pages/details/methods/alerts/ConnectWalletAlert.tsx index eae01570f5..a3f66684d7 100644 --- a/client/slices/contract/pages/details/methods/alerts/ConnectWalletAlert.tsx +++ b/client/slices/contract/pages/details/methods/alerts/ConnectWalletAlert.tsx @@ -82,4 +82,4 @@ const ConnectWalletAlert = ({ isLoading }: Props) => { const Fallback = () => null; -export default config.features.blockchainInteraction.isEnabled ? ConnectWalletAlert : Fallback; +export default config.features.connectWallet.isEnabled ? ConnectWalletAlert : Fallback; diff --git a/client/slices/contract/pages/details/methods/form/ContractMethodForm.tsx b/client/slices/contract/pages/details/methods/form/ContractMethodForm.tsx index 4bfd6e6345..5efe4384e8 100644 --- a/client/slices/contract/pages/details/methods/form/ContractMethodForm.tsx +++ b/client/slices/contract/pages/details/methods/form/ContractMethodForm.tsx @@ -150,7 +150,7 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) }, [ data ]); const primaryButton = (() => { - const isDisabled = !config.features.blockchainInteraction.isEnabled && methodType === 'write'; + const isDisabled = !config.features.connectWallet.isEnabled && methodType === 'write'; const text = methodType === 'write' ? 'Write' : 'Read'; const buttonCallStrategy = methodType === 'write' ? 'write' : 'read'; diff --git a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts index 2b4906076a..5d792efb1a 100644 --- a/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts +++ b/client/slices/contract/pages/details/methods/useCallMethodWalletClient.ts @@ -14,7 +14,7 @@ import config from 'configs/app'; import { getNativeCoinValue } from './utils'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; interface Params { item: SmartContractMethod; diff --git a/client/slices/contract/pages/details/methods/useFormSubmit.ts b/client/slices/contract/pages/details/methods/useFormSubmit.ts index d9796fb58a..fa36682f08 100644 --- a/client/slices/contract/pages/details/methods/useFormSubmit.ts +++ b/client/slices/contract/pages/details/methods/useFormSubmit.ts @@ -52,6 +52,6 @@ function useFormSubmitFallback({ addressHash }: Params): FormSubmitHandler { }, [ callMethodPublicClient, addressHash ]); } -const hook = config.features.blockchainInteraction.isEnabled ? useFormSubmit : useFormSubmitFallback; +const hook = config.features.connectWallet.isEnabled ? useFormSubmit : useFormSubmitFallback; export default hook; diff --git a/client/slices/log/components/LogDecodedInputDataTable.tsx b/client/slices/log/components/LogDecodedInputDataTable.tsx index 3a4368ec7a..11ea87cbf7 100644 --- a/client/slices/log/components/LogDecodedInputDataTable.tsx +++ b/client/slices/log/components/LogDecodedInputDataTable.tsx @@ -3,8 +3,7 @@ import { Flex, Grid } from '@chakra-ui/react'; import React from 'react'; -import type { DecodedInput } from 'client/slices/log/types/api'; -import type { ArrayElement } from 'types/utils'; +import type { DecodedInput, DecodedInputParams } from 'client/slices/log/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; @@ -32,7 +31,7 @@ const HeaderItem = ({ children, isLoading }: { children: React.ReactNode; isLoad ); }; -const Row = ({ name, type, indexed, value, isLoading }: ArrayElement & { isLoading?: boolean }) => { +const Row = ({ name, type, indexed, value, isLoading }: DecodedInputParams & { isLoading?: boolean }) => { const content = (() => { if (type === 'address' && typeof value === 'string') { return ( diff --git a/configs/app/features/blockchain-interaction.ts b/configs/app/features/connect-wallet.ts similarity index 100% rename from configs/app/features/blockchain-interaction.ts rename to configs/app/features/connect-wallet.ts diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index 576c4896e3..fbc86d6cf1 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -10,7 +10,7 @@ export { default as adsText } from './ads-text'; export { default as apiDocs } from './api-docs'; export { default as beaconChain } from './beacon-chain'; export { default as bridgedTokens } from './bridged-tokens'; -export { default as blockchainInteraction } from './blockchain-interaction'; +export { default as connectWallet } from './connect-wallet'; export { default as celo } from './celo'; export { default as crossChainTxs } from './cross-chain-txs'; export { default as csvExport } from './csv-export'; diff --git a/configs/app/features/marketplace.ts b/configs/app/features/marketplace.ts index 9c295b3463..4567c325b5 100644 --- a/configs/app/features/marketplace.ts +++ b/configs/app/features/marketplace.ts @@ -7,7 +7,7 @@ import apis from '../apis'; import app from '../app'; import chain from '../chain'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from '../utils'; -import blockchainInteraction from './blockchain-interaction'; +import connectWallet from './connect-wallet'; const defaultTitles: MarketplaceTitles = { entity_name: 'Dapp', @@ -63,7 +63,7 @@ const config: Feature<( linkUrl: bannerLinkUrl, } : undefined, graphLinksUrl, - essentialDapps: blockchainInteraction.isEnabled ? (essentialDappsConfig || undefined) : undefined, + essentialDapps: connectWallet.isEnabled ? (essentialDappsConfig || undefined) : undefined, essentialDappsAdEnabled, titles: { ...defaultTitles, ...customTitles }, }; diff --git a/configs/app/features/rewards.ts b/configs/app/features/rewards.ts index cdedbed75a..d3703aa913 100644 --- a/configs/app/features/rewards.ts +++ b/configs/app/features/rewards.ts @@ -5,13 +5,13 @@ import type { Feature } from './types'; import apis from '../apis'; import app from '../app'; import account from './account'; -import blockchainInteraction from './blockchain-interaction'; +import connectWallet from './connect-wallet'; const title = 'Rewards service integration'; const config: Feature<{}> = (() => { // @0xdeval: as of now, we won't support rewards programs with dynamic auth provider - if (!app.isPrivateMode && apis.rewards && account.isEnabled && account.authProvider === 'auth0' && blockchainInteraction.isEnabled) { + if (!app.isPrivateMode && apis.rewards && account.isEnabled && account.authProvider === 'auth0' && connectWallet.isEnabled) { return Object.freeze({ title, isEnabled: true, diff --git a/configs/app/features/validators.ts b/configs/app/features/validators.ts index d76ba2dae1..2305150e2b 100644 --- a/configs/app/features/validators.ts +++ b/configs/app/features/validators.ts @@ -1,7 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { ArrayElement } from 'types/utils'; import { getEnvValue } from '../utils'; @@ -11,7 +10,7 @@ export const VALIDATORS_CHAIN_TYPE = [ 'zilliqa', ] as const; -export type ValidatorsChainType = ArrayElement; +export type ValidatorsChainType = (typeof VALIDATORS_CHAIN_TYPE)[number]; const chainType = ((): ValidatorsChainType | undefined => { const envValue = getEnvValue('NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE') as ValidatorsChainType | undefined; diff --git a/configs/app/features/web3-wallet.ts b/configs/app/features/web3-wallet.ts index 4f120e35ac..1fe0d60bb2 100644 --- a/configs/app/features/web3-wallet.ts +++ b/configs/app/features/web3-wallet.ts @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import { SUPPORTED_WALLETS } from 'types/client/wallets'; -import type { WalletType } from 'types/client/wallets'; +import { SUPPORTED_WALLETS } from 'client/features/web3-wallet/types/config'; +import type { WalletType } from 'client/features/web3-wallet/types/config'; import app from '../app'; import { getEnvValue, parseEnvJson } from '../utils'; diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 2cb8f7658b..e7881e433f 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -22,8 +22,8 @@ import type { GasUnit } from 'client/slices/gas/types/config'; import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from 'client/features/tx-interpretation/common/types/config'; import { VALIDATORS_CHAIN_TYPE } from 'configs/app/features/validators'; import type { ValidatorsChainType } from 'configs/app/features/validators'; -import type { WalletType } from 'types/client/wallets'; -import { SUPPORTED_WALLETS } from 'types/client/wallets'; +import type { WalletType } from 'client/features/web3-wallet/types/config'; +import { SUPPORTED_WALLETS } from 'client/features/web3-wallet/types/config'; import type { TxExternalTxsConfig } from 'types/client/externalTxsConfig'; import { replaceQuotes } from 'configs/app/utils'; diff --git a/global.d.ts b/global.d.ts index f3a31546db..8ba09121e3 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,6 +1,6 @@ import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { MultichainConfig } from 'client/features/multichain/types/client'; -import type { WalletProvider } from 'types/web3'; +import type { WalletProvider } from 'client/features/web3-wallet/types/wallet-provider'; import 'vitest-fetch-mock'; declare global { diff --git a/nextjs/csp/generateCspPolicy.ts b/nextjs/csp/generateCspPolicy.ts index 6ee0e6f403..cb2aa69ab5 100644 --- a/nextjs/csp/generateCspPolicy.ts +++ b/nextjs/csp/generateCspPolicy.ts @@ -8,7 +8,7 @@ function generateCspPolicy(isPrivateMode = false, nonce?: string) { descriptors.app(isPrivateMode), // Exclude tracking/analytics sources in private mode isPrivateMode ? {} : descriptors.ad(nonce), - isPrivateMode ? {} : descriptors.blockchainInteraction(), + isPrivateMode ? {} : descriptors.connectWallet(), descriptors.cloudFlare(), descriptors.flashblocks(), isPrivateMode ? {} : descriptors.googleAnalytics(), diff --git a/nextjs/csp/policies/blockchainInteraction.ts b/nextjs/csp/policies/connect-wallet.ts similarity index 95% rename from nextjs/csp/policies/blockchainInteraction.ts rename to nextjs/csp/policies/connect-wallet.ts index 07737f633f..b93deea3e4 100644 --- a/nextjs/csp/policies/blockchainInteraction.ts +++ b/nextjs/csp/policies/connect-wallet.ts @@ -6,9 +6,9 @@ import config from 'configs/app'; import { KEY_WORDS } from '../utils'; -const feature = config.features.blockchainInteraction; +const feature = config.features.connectWallet; -export function blockchainInteraction(): CspDev.DirectiveDescriptor { +export function connectWallet(): CspDev.DirectiveDescriptor { if (!feature.isEnabled) { return {}; } diff --git a/nextjs/csp/policies/index.ts b/nextjs/csp/policies/index.ts index 1670d93290..d497ec8d85 100644 --- a/nextjs/csp/policies/index.ts +++ b/nextjs/csp/policies/index.ts @@ -2,7 +2,7 @@ export { ad } from './ad'; export { app } from './app'; -export { blockchainInteraction } from './blockchainInteraction'; +export { connectWallet } from './connect-wallet'; export { cloudFlare } from './cloudFlare'; export { flashblocks } from './flashblocks'; export { googleAnalytics } from './googleAnalytics'; diff --git a/pages/_app.tsx b/pages/_app.tsx index b44c30eb16..4048bd56cd 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -17,6 +17,7 @@ import getSocketUrl from 'client/api/get-socket-url'; import useQueryClientConfig from 'client/api/hooks/useQueryClientConfig'; import { SocketProvider } from 'client/api/socket/context'; +import Web3Provider from 'client/features/connect-wallet/components/Web3Provider'; import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; import { MarketplaceContextProvider } from 'client/features/marketplace/context'; @@ -34,7 +35,6 @@ import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorGlobalContainer from 'ui/shared/AppError/AppErrorGlobalContainer'; import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import Layout from 'ui/shared/layout/Layout'; -import Web3Provider from 'ui/shared/web3/Web3Provider'; const RewardsContextProvider = dynamic(() => import('client/features/rewards/context').then(module => module.RewardsContextProvider), { ssr: false }); const RewardsLoginModal = dynamic(() => import('client/features/rewards/components/login/RewardsLoginModal'), { ssr: false }); diff --git a/playwright/fixtures/injectMetaMaskProvider.ts b/playwright/fixtures/injectMetaMaskProvider.ts index f653b9c773..a2ba649c8c 100644 --- a/playwright/fixtures/injectMetaMaskProvider.ts +++ b/playwright/fixtures/injectMetaMaskProvider.ts @@ -1,6 +1,6 @@ import type { TestFixture, Page } from '@playwright/test'; -import type { WalletProvider } from 'types/web3'; +import type { WalletProvider } from 'client/features/web3-wallet/types/wallet-provider'; export type InjectMetaMaskProvider = () => Promise; diff --git a/stubs/utils.ts b/stubs/utils.ts index bbbbd35a43..78f87f579c 100644 --- a/stubs/utils.ts +++ b/stubs/utils.ts @@ -1,9 +1,7 @@ -import type { ArrayElement } from 'types/utils'; - import type { PaginatedResourceName, PaginatedResourceResponse, PaginatedResourceResponseItems } from 'client/api/resources'; export function generateListStub( - stub: ArrayElement>, + stub: (PaginatedResourceResponseItems)[number], num = 50, rest: Omit, 'items'>, ) { diff --git a/types/client/adProviders.ts b/types/client/adProviders.ts index 6ce469b67f..fbfe27a4b9 100644 --- a/types/client/adProviders.ts +++ b/types/client/adProviders.ts @@ -1,17 +1,15 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ArrayElement } from 'types/utils'; - export const SUPPORTED_AD_BANNER_PROVIDERS = [ 'slise', 'adbutler', 'sevio', 'none', ] as const; -export type AdBannerProviders = ArrayElement; +export type AdBannerProviders = (typeof SUPPORTED_AD_BANNER_PROVIDERS)[number]; export const SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS = [ 'adbutler' ] as const; -export type AdBannerAdditionalProviders = ArrayElement; +export type AdBannerAdditionalProviders = (typeof SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS)[number]; export const SUPPORTED_AD_TEXT_PROVIDERS = [ 'sevio', 'none' ] as const; -export type AdTextProviders = ArrayElement; +export type AdTextProviders = (typeof SUPPORTED_AD_TEXT_PROVIDERS)[number]; diff --git a/types/client/wallets.ts b/types/client/wallets.ts deleted file mode 100644 index 03a9bcba64..0000000000 --- a/types/client/wallets.ts +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { ArrayElement } from 'types/utils'; - -import type { IconName } from 'ui/shared/IconSvg'; - -export const SUPPORTED_WALLETS = [ - 'metamask', - 'coinbase', - 'token_pocket', - 'rabby', - 'okx', - 'trust', -] as const; - -export type WalletType = ArrayElement; - -export interface WalletInfo { - name: string; - icon: IconName; - color: string; -} diff --git a/types/networks.ts b/types/networks.ts index 069b60fa3d..af3c0f2cdc 100644 --- a/types/networks.ts +++ b/types/networks.ts @@ -1,9 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ArrayElement } from 'types/utils'; - export const NETWORK_GROUPS = [ 'Mainnets', 'Testnets', 'Other' ] as const; -export type NetworkGroup = ArrayElement; +export type NetworkGroup = (typeof NETWORK_GROUPS)[number]; export interface FeaturedNetwork { title: string; diff --git a/types/utils.ts b/types/utils.ts index 8981534864..f9cede76a5 100644 --- a/types/utils.ts +++ b/types/utils.ts @@ -1,9 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -export type ArrayElement = ArrType extends ReadonlyArray ? - ElementType : - never; - export type ExcludeNull = T extends null ? never : T; export type ExcludeUndefined = T extends undefined ? never : T; diff --git a/types/views/block.ts b/types/views/block.ts index b05e52b765..e31bd1e652 100644 --- a/types/views/block.ts +++ b/types/views/block.ts @@ -1,7 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ArrayElement } from 'types/utils'; - export const BLOCK_FIELDS_IDS = [ 'base_fee', 'burnt_fees', @@ -12,4 +10,4 @@ export const BLOCK_FIELDS_IDS = [ 'batch', ] as const; -export type BlockFieldId = ArrayElement; +export type BlockFieldId = (typeof BLOCK_FIELDS_IDS)[number]; diff --git a/ui/shared/monaco/CodeEditorSearchResultItem.tsx b/ui/shared/monaco/CodeEditorSearchResultItem.tsx index 197291aea3..97269671a0 100644 --- a/ui/shared/monaco/CodeEditorSearchResultItem.tsx +++ b/ui/shared/monaco/CodeEditorSearchResultItem.tsx @@ -3,12 +3,11 @@ import { Box, chakra } from '@chakra-ui/react'; import React from 'react'; -import type { SearchResult } from './types'; -import type { ArrayElement } from 'types/utils'; +import type { SearchResultMatch } from './types'; import useThemeColors from './utils/useThemeColors'; -interface Props extends ArrayElement { +interface Props extends SearchResultMatch { filePath: string; onClick: (event: React.MouseEvent) => void; } diff --git a/ui/shared/monaco/types.ts b/ui/shared/monaco/types.ts index 833ed39ee5..005e14f7e5 100644 --- a/ui/shared/monaco/types.ts +++ b/ui/shared/monaco/types.ts @@ -22,8 +22,9 @@ export type Monaco = typeof monaco; export interface SearchResult { file_path: string; - matches: Array< - Pick & - { lineContent: string } - >; + matches: Array; +} + +export interface SearchResultMatch extends Pick { + lineContent: string; } diff --git a/ui/shared/monaco/utils/sortFileTree.ts b/ui/shared/monaco/utils/sortFileTree.ts index dc919bd6c4..ecff6f7e89 100644 --- a/ui/shared/monaco/utils/sortFileTree.ts +++ b/ui/shared/monaco/utils/sortFileTree.ts @@ -1,9 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { FileTree } from '../types'; -import type { ArrayElement } from 'types/utils'; -export default function sortFileTree(a: ArrayElement, b: ArrayElement) { +export default function sortFileTree(a: FileTree[number], b: FileTree[number]) { if ('children' in a && !('children' in b)) { return -1; } diff --git a/ui/snippets/footer/Footer.tsx b/ui/snippets/footer/Footer.tsx index 58f32de382..8ea5cbc39a 100644 --- a/ui/snippets/footer/Footer.tsx +++ b/ui/snippets/footer/Footer.tsx @@ -11,6 +11,8 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import useFetch from 'client/api/hooks/useFetch'; import type { ResourceError } from 'client/api/resources'; +import NetworkAddToWallet from 'client/features/web3-wallet/components/NetworkAddToWallet'; + import config from 'configs/app'; import { useAppContext } from 'lib/contexts/app'; import { Link } from 'toolkit/chakra/link'; @@ -19,7 +21,6 @@ import { copy } from 'toolkit/utils/htmlEntities'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import IconSvg from 'ui/shared/IconSvg'; import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils'; -import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet'; import FooterLinkItem from './FooterLinkItem'; import IntTxsIndexingStatus from './IntTxsIndexingStatus'; diff --git a/ui/snippets/header/HeaderMobile.tsx b/ui/snippets/header/HeaderMobile.tsx index 70363d0543..ba1c819d45 100644 --- a/ui/snippets/header/HeaderMobile.tsx +++ b/ui/snippets/header/HeaderMobile.tsx @@ -41,7 +41,7 @@ const HeaderMobile = ({ hideSearchButton, onGoToSearchResults }: Props) => { return null; } } - if (config.features.blockchainInteraction.isEnabled) { + if (config.features.connectWallet.isEnabled) { return ; } })(); diff --git a/ui/snippets/topBar/TopBar.tsx b/ui/snippets/topBar/TopBar.tsx index 2c546f54c0..1044fce185 100644 --- a/ui/snippets/topBar/TopBar.tsx +++ b/ui/snippets/topBar/TopBar.tsx @@ -4,15 +4,15 @@ import { Flex, Box, HStack } from '@chakra-ui/react'; import React from 'react'; import CsvExportDownloads from 'client/features/csv-export/components/downloads/CsvExportDownloads'; +import NetworkAddToWallet from 'client/features/web3-wallet/components/NetworkAddToWallet'; +import useProvider from 'client/features/web3-wallet/hooks/useProvider'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import * as cookies from 'client/shared/storage/cookies'; -import useProvider from 'client/shared/web3/useProvider'; import config from 'configs/app'; import { useAppContext } from 'lib/contexts/app'; import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils'; -import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet'; import DeFiDropdown from './DeFiDropdown'; import NetworkMenu from './NetworkMenu'; From a0360a9f4584b305d4885221d46ed786d3f2e3a6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 21:04:56 +0200 Subject: [PATCH 27/85] [Migration 6-13] Features: cross-chain-txs, gas-tracker, sol2uml (#3484) * feat(migration-6-13): move cross-chain-txs, gas-tracker, sol2uml to client/features Agent-Logs-Url: https://github.com/blockscout/frontend/sessions/a3b87e12-c516-499b-a208-62c641fd8f29 Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> * [skip ci] fixes --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tom2drum <22130104+tom2drum@users.noreply.github.com> Co-authored-by: tom --- .../components}/CrossChainBridgeLink.tsx | 0 .../components}/CrossChainFromToTag.tsx | 0 .../components}/CrossChainMessageEntity.tsx | 3 +-- .../components}/CrossChainTxsStatusTag.tsx | 3 +-- .../TokenTransfersCrossChainContent.tsx | 0 .../TokenTransfersCrossChainListItem.tsx | 9 +++++---- .../TokenTransfersCrossChainTable.tsx | 0 .../TokenTransfersCrossChainTableItem.tsx | 9 +++++---- .../components/token-transfers}/utils.ts | 0 .../txs/TransactionsCrossChainContent.tsx | 0 .../txs/TransactionsCrossChainListItem.tsx | 9 +++++---- .../components}/txs/TransactionsCrossChainTable.tsx | 0 .../txs/TransactionsCrossChainTableItem.tsx | 9 +++++---- .../cross-chain-txs/hooks/useBridgedTokensQuery.ts | 3 ++- .../hooks/useTxCrossChainTransfersQuery.ts | 3 ++- .../features/cross-chain-txs/mocks}/config.ts | 0 .../features/cross-chain-txs/mocks}/transfers.ts | 0 .../features/cross-chain-txs/mocks}/txs.ts | 0 .../pages}/address/AddressTxsCrossChain.tsx | 4 ++-- .../pages/home/LatestCrossChainTxs.tsx | 5 +++-- .../pages/home/LatestCrossChainTxsItemDesktop.tsx | 7 ++++--- .../cross-chain-txs/pages/ictt-users/IcttUsers.tsx | 2 +- .../TokenTransfersCrossChain.pw.tsx | 5 +++-- .../token-transfers}/TokenTransfersCrossChain.tsx | 4 ++-- ...color-mode_txs-desktop-base-view-dark-mode-1.png | Bin ...sx_default_txs-desktop-base-view-dark-mode-1.png | Bin ...sChain.pw.tsx_default_txs-mobile-base-view-1.png | Bin .../cross-chain-txs/pages}/tx/TxCrossChain.pw.tsx | 7 ++++--- .../cross-chain-txs/pages}/tx/TxCrossChain.tsx | 4 ++-- .../pages}/tx/TxCrossChainDetails.tsx | 4 ++-- .../pages}/tx/TxCrossChainDetailsLifecycle.pw.tsx | 7 ++++--- .../pages}/tx/TxCrossChainDetailsLifecycle.tsx | 0 .../pages}/tx/TxCrossChainDetailsTransfers.tsx | 0 .../pages}/tx/TxCrossChainTransfers.tsx | 6 +++--- .../pages/tx/TxDetailsCrossChainMessage.tsx | 5 +++-- .../pages/tx/TxDetailsCrossChainMessages.pw.tsx | 7 ++++--- .../pages/tx/TxDetailsCrossChainMessages.tsx | 2 +- .../pages/tx/TxDetailsCrossChainTransfers.tsx | 4 ++-- .../pages/tx/TxTokenTransferCrossChain.tsx | 7 ++++--- ...sChain.pw.tsx_default_successful-tx-mobile-1.png | Bin ...ssChain.pw.tsx_mobile_successful-tx-mobile-1.png | Bin ...-color-mode_successful-tx-mobile-dark-mode-1.png | Bin ...ecycle.pw.tsx_default_failed-on-first-step-1.png | Bin ...tailsLifecycle.pw.tsx_default_initiated-tx-1.png | Bin ...tsx_default_successful-tx-mobile-dark-mode-1.png | Bin ....tsx_mobile_successful-tx-mobile-dark-mode-1.png | Bin .../cross-chain-txs/pages}/txs/Transactions.tsx | 0 .../pages}/txs/TransactionsCrossChain.pw.tsx | 6 +++--- .../pages}/txs/TransactionsCrossChain.tsx | 4 ++-- .../pages}/txs/TransactionsCrossChainStats.tsx | 3 ++- ...color-mode_txs-desktop-base-view-dark-mode-1.png | Bin ...sx_default_txs-desktop-base-view-dark-mode-1.png | Bin ...sChain.pw.tsx_default_txs-mobile-base-view-1.png | Bin .../features/cross-chain-txs/stubs/messages.ts | 0 .../gas-tracker/components}/GasTrackerChart.tsx | 0 .../gas-tracker/components}/GasTrackerFaq.tsx | 0 .../gas-tracker/components}/GasTrackerFaqItem.tsx | 0 .../components}/GasTrackerNetworkUtilization.tsx | 0 .../components}/GasTrackerPriceSnippet.pw.tsx | 0 .../components}/GasTrackerPriceSnippet.tsx | 0 .../gas-tracker/components}/GasTrackerPrices.tsx | 0 ...r-mode_with-gwei-as-primary-unit-dark-mode-1.png | Bin ...or-mode_with-usd-as-primary-unit-dark-mode-1.png | Bin ...rPriceSnippet.pw.tsx_default_loading-state-1.png | Bin ...efault_with-gwei-as-primary-unit-dark-mode-1.png | Bin ...ceSnippet.pw.tsx_default_with-small-values-1.png | Bin ...default_with-usd-as-primary-unit-dark-mode-1.png | Bin ...iceSnippet.pw.tsx_default_with-zero-values-1.png | Bin .../gas-tracker/pages/index}/GasTracker.pw.tsx | 0 .../gas-tracker/pages/index}/GasTracker.tsx | 9 +++++---- ...dark-color-mode_base-view-dark-mode-mobile-1.png | Bin ....pw.tsx_default_base-view-dark-mode-mobile-1.png | Bin ...r.pw.tsx_mobile_base-view-dark-mode-mobile-1.png | Bin .../features/sol2uml/components}/Sol2UmlDiagram.tsx | 0 .../features/sol2uml/pages/index}/Sol2Uml.tsx | 2 +- .../token-transfers/AddressTokenTransfers.tsx | 4 ++-- .../slices/address/pages/details/txs/AddressTxs.tsx | 4 ++-- .../token-transfer/pages/index/TokenTransfers.tsx | 3 ++- .../chain/[chain_slug_or_id]/visualize/sol2uml.tsx | 3 +-- pages/cross-chain-tx/[id].tsx | 2 +- pages/gas-tracker.tsx | 2 +- pages/txs/index.tsx | 2 +- pages/visualize/sol2uml.tsx | 2 +- 83 files changed, 94 insertions(+), 80 deletions(-) rename {ui/shared/crossChain => client/features/cross-chain-txs/components}/CrossChainBridgeLink.tsx (100%) rename {ui/shared/crossChain => client/features/cross-chain-txs/components}/CrossChainFromToTag.tsx (100%) rename {ui/shared/entities/crossChainMessage => client/features/cross-chain-txs/components}/CrossChainMessageEntity.tsx (97%) rename {ui/shared/statusTag => client/features/cross-chain-txs/components}/CrossChainTxsStatusTag.tsx (92%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/components/token-transfers}/TokenTransfersCrossChainContent.tsx (100%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/components/token-transfers}/TokenTransfersCrossChainListItem.tsx (93%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/components/token-transfers}/TokenTransfersCrossChainTable.tsx (100%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/components/token-transfers}/TokenTransfersCrossChainTableItem.tsx (94%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/components/token-transfers}/utils.ts (100%) rename {ui/crossChain => client/features/cross-chain-txs/components}/txs/TransactionsCrossChainContent.tsx (100%) rename {ui/crossChain => client/features/cross-chain-txs/components}/txs/TransactionsCrossChainListItem.tsx (94%) rename {ui/crossChain => client/features/cross-chain-txs/components}/txs/TransactionsCrossChainTable.tsx (100%) rename {ui/crossChain => client/features/cross-chain-txs/components}/txs/TransactionsCrossChainTableItem.tsx (95%) rename {mocks/crossChain => client/features/cross-chain-txs/mocks}/config.ts (100%) rename {mocks/crossChain => client/features/cross-chain-txs/mocks}/transfers.ts (100%) rename {mocks/crossChain => client/features/cross-chain-txs/mocks}/txs.ts (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/address/AddressTxsCrossChain.tsx (88%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/pages/token-transfers}/TokenTransfersCrossChain.pw.tsx (91%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/pages/token-transfers}/TokenTransfersCrossChain.tsx (88%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/pages/token-transfers}/__screenshots__/TokenTransfersCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png (100%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/pages/token-transfers}/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png (100%) rename {ui/crossChain/transfers => client/features/cross-chain-txs/pages/token-transfers}/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-mobile-base-view-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChain.pw.tsx (81%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChain.tsx (92%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChainDetails.tsx (96%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChainDetailsLifecycle.pw.tsx (86%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChainDetailsLifecycle.tsx (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChainDetailsTransfers.tsx (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/TxCrossChainTransfers.tsx (82%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChain.pw.tsx_default_successful-tx-mobile-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChain.pw.tsx_mobile_successful-tx-mobile-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_dark-color-mode_successful-tx-mobile-dark-mode-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_failed-on-first-step-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_initiated-tx-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_successful-tx-mobile-dark-mode-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_mobile_successful-tx-mobile-dark-mode-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/Transactions.tsx (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/TransactionsCrossChain.pw.tsx (90%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/TransactionsCrossChain.tsx (89%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/TransactionsCrossChainStats.tsx (93%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/__screenshots__/TransactionsCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png (100%) rename {ui/crossChain => client/features/cross-chain-txs/pages}/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-mobile-base-view-1.png (100%) rename stubs/interchainIndexer.ts => client/features/cross-chain-txs/stubs/messages.ts (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerChart.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerFaq.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerFaqItem.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerNetworkUtilization.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerPriceSnippet.pw.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerPriceSnippet.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/GasTrackerPrices.tsx (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-gwei-as-primary-unit-dark-mode-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-usd-as-primary-unit-dark-mode-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_loading-state-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-gwei-as-primary-unit-dark-mode-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-small-values-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-usd-as-primary-unit-dark-mode-1.png (100%) rename {ui/gasTracker => client/features/gas-tracker/components}/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-zero-values-1.png (100%) rename {ui/pages => client/features/gas-tracker/pages/index}/GasTracker.pw.tsx (100%) rename {ui/pages => client/features/gas-tracker/pages/index}/GasTracker.tsx (90%) rename {ui/pages => client/features/gas-tracker/pages/index}/__screenshots__/GasTracker.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/gas-tracker/pages/index}/__screenshots__/GasTracker.pw.tsx_default_base-view-dark-mode-mobile-1.png (100%) rename {ui/pages => client/features/gas-tracker/pages/index}/__screenshots__/GasTracker.pw.tsx_mobile_base-view-dark-mode-mobile-1.png (100%) rename {ui/sol2uml => client/features/sol2uml/components}/Sol2UmlDiagram.tsx (100%) rename {ui/pages => client/features/sol2uml/pages/index}/Sol2Uml.tsx (94%) diff --git a/ui/shared/crossChain/CrossChainBridgeLink.tsx b/client/features/cross-chain-txs/components/CrossChainBridgeLink.tsx similarity index 100% rename from ui/shared/crossChain/CrossChainBridgeLink.tsx rename to client/features/cross-chain-txs/components/CrossChainBridgeLink.tsx diff --git a/ui/shared/crossChain/CrossChainFromToTag.tsx b/client/features/cross-chain-txs/components/CrossChainFromToTag.tsx similarity index 100% rename from ui/shared/crossChain/CrossChainFromToTag.tsx rename to client/features/cross-chain-txs/components/CrossChainFromToTag.tsx diff --git a/ui/shared/entities/crossChainMessage/CrossChainMessageEntity.tsx b/client/features/cross-chain-txs/components/CrossChainMessageEntity.tsx similarity index 97% rename from ui/shared/entities/crossChainMessage/CrossChainMessageEntity.tsx rename to client/features/cross-chain-txs/components/CrossChainMessageEntity.tsx index bf4e567b51..733c530377 100644 --- a/ui/shared/entities/crossChainMessage/CrossChainMessageEntity.tsx +++ b/client/features/cross-chain-txs/components/CrossChainMessageEntity.tsx @@ -6,10 +6,9 @@ import React from 'react'; import { route } from 'nextjs/routes'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; -import { distributeEntityProps } from '../base/utils'; - type LinkProps = EntityBase.LinkBaseProps & Pick; const Link = chakra((props: LinkProps) => { diff --git a/ui/shared/statusTag/CrossChainTxsStatusTag.tsx b/client/features/cross-chain-txs/components/CrossChainTxsStatusTag.tsx similarity index 92% rename from ui/shared/statusTag/CrossChainTxsStatusTag.tsx rename to client/features/cross-chain-txs/components/CrossChainTxsStatusTag.tsx index 1c0b77a04e..ff77b85544 100644 --- a/ui/shared/statusTag/CrossChainTxsStatusTag.tsx +++ b/client/features/cross-chain-txs/components/CrossChainTxsStatusTag.tsx @@ -5,8 +5,7 @@ import React from 'react'; import { MessageStatus } from '@blockscout/interchain-indexer-types'; import type { BadgeProps } from 'toolkit/chakra/badge'; - -import StatusTag, { type Props as StatusTagProps } from './StatusTag'; +import StatusTag, { type Props as StatusTagProps } from 'ui/shared/statusTag/StatusTag'; interface Props extends BadgeProps { status: MessageStatus; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainContent.tsx b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainContent.tsx similarity index 100% rename from ui/crossChain/transfers/TokenTransfersCrossChainContent.tsx rename to client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainContent.tsx diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx similarity index 93% rename from ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx rename to client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx index e11b758892..2ea4250569 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx +++ b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx @@ -12,15 +12,16 @@ import config from 'configs/app'; import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; -import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TextSeparator from 'ui/shared/TextSeparator'; import Time from 'ui/shared/time/Time'; import TokenValueInterchain from 'ui/shared/value/TokenValueInterchain'; +import CrossChainBridgeLink from '../CrossChainBridgeLink'; +import CrossChainFromToTag from '../CrossChainFromToTag'; +import CrossChainMessageEntity from '../CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../CrossChainTxsStatusTag'; + interface Props extends JsxStyleProps { data: InterchainTransfer; isLoading?: boolean; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainTable.tsx similarity index 100% rename from ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx rename to client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainTable.tsx diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainTableItem.tsx similarity index 94% rename from ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx rename to client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainTableItem.tsx index 98d75b530a..25ee06c8a4 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx +++ b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainTableItem.tsx @@ -12,14 +12,15 @@ import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInter import config from 'configs/app'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; -import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import TokenValueInterchain from 'ui/shared/value/TokenValueInterchain'; +import CrossChainBridgeLink from '../CrossChainBridgeLink'; +import CrossChainFromToTag from '../CrossChainFromToTag'; +import CrossChainMessageEntity from '../CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../CrossChainTxsStatusTag'; + interface Props { data: InterchainTransfer; isLoading?: boolean; diff --git a/ui/crossChain/transfers/utils.ts b/client/features/cross-chain-txs/components/token-transfers/utils.ts similarity index 100% rename from ui/crossChain/transfers/utils.ts rename to client/features/cross-chain-txs/components/token-transfers/utils.ts diff --git a/ui/crossChain/txs/TransactionsCrossChainContent.tsx b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainContent.tsx similarity index 100% rename from ui/crossChain/txs/TransactionsCrossChainContent.tsx rename to client/features/cross-chain-txs/components/txs/TransactionsCrossChainContent.tsx diff --git a/ui/crossChain/txs/TransactionsCrossChainListItem.tsx b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx similarity index 94% rename from ui/crossChain/txs/TransactionsCrossChainListItem.tsx rename to client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx index f2b87cbbcf..f3e5493c48 100644 --- a/ui/crossChain/txs/TransactionsCrossChainListItem.tsx +++ b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx @@ -16,15 +16,16 @@ import dayjs from 'lib/date/dayjs'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; -import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TextSeparator from 'ui/shared/TextSeparator'; import Time from 'ui/shared/time/Time'; import TokenValueInterchain from 'ui/shared/value/TokenValueInterchain'; +import CrossChainBridgeLink from '../CrossChainBridgeLink'; +import CrossChainFromToTag from '../CrossChainFromToTag'; +import CrossChainMessageEntity from '../CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../CrossChainTxsStatusTag'; + interface Props extends JsxStyleProps { data: InterchainMessage; isLoading?: boolean; diff --git a/ui/crossChain/txs/TransactionsCrossChainTable.tsx b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainTable.tsx similarity index 100% rename from ui/crossChain/txs/TransactionsCrossChainTable.tsx rename to client/features/cross-chain-txs/components/txs/TransactionsCrossChainTable.tsx diff --git a/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainTableItem.tsx similarity index 95% rename from ui/crossChain/txs/TransactionsCrossChainTableItem.tsx rename to client/features/cross-chain-txs/components/txs/TransactionsCrossChainTableItem.tsx index d81b4549e1..5df2983a24 100644 --- a/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx +++ b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainTableItem.tsx @@ -16,14 +16,15 @@ import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; -import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import TokenValueInterchain from 'ui/shared/value/TokenValueInterchain'; +import CrossChainBridgeLink from '../CrossChainBridgeLink'; +import CrossChainFromToTag from '../CrossChainFromToTag'; +import CrossChainMessageEntity from '../CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../CrossChainTxsStatusTag'; + interface Props { data: InterchainMessage; isLoading?: boolean; diff --git a/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts b/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts index 92ef350b43..9f1cedd97d 100644 --- a/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts +++ b/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts @@ -5,11 +5,12 @@ import React from 'react'; import type { CrossChainBridgedTokensSorting, CrossChainBridgedTokensSortingField, CrossChainBridgedTokensSortingValue } from '../types/api'; +import { INTERCHAIN_BRIDGED_TOKEN_ITEM } from 'client/features/cross-chain-txs/stubs/messages'; + import useDebounce from 'client/shared/hooks/useDebounce'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { INTERCHAIN_BRIDGED_TOKEN_ITEM } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import type { OnValueChangeHandler } from 'toolkit/chakra/select'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts b/client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts index 76b7f6c3ad..aaf0b7e9b3 100644 --- a/client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts +++ b/client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts @@ -1,7 +1,8 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import { INTERCHAIN_TRANSFER } from 'client/features/cross-chain-txs/stubs/messages'; + import config from 'configs/app'; -import { INTERCHAIN_TRANSFER } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/mocks/crossChain/config.ts b/client/features/cross-chain-txs/mocks/config.ts similarity index 100% rename from mocks/crossChain/config.ts rename to client/features/cross-chain-txs/mocks/config.ts diff --git a/mocks/crossChain/transfers.ts b/client/features/cross-chain-txs/mocks/transfers.ts similarity index 100% rename from mocks/crossChain/transfers.ts rename to client/features/cross-chain-txs/mocks/transfers.ts diff --git a/mocks/crossChain/txs.ts b/client/features/cross-chain-txs/mocks/txs.ts similarity index 100% rename from mocks/crossChain/txs.ts rename to client/features/cross-chain-txs/mocks/txs.ts diff --git a/ui/crossChain/address/AddressTxsCrossChain.tsx b/client/features/cross-chain-txs/pages/address/AddressTxsCrossChain.tsx similarity index 88% rename from ui/crossChain/address/AddressTxsCrossChain.tsx rename to client/features/cross-chain-txs/pages/address/AddressTxsCrossChain.tsx index 6456adf4ac..b157fb7a3c 100644 --- a/ui/crossChain/address/AddressTxsCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/address/AddressTxsCrossChain.tsx @@ -9,8 +9,8 @@ import ActionBar from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import TableViewToggleButton from 'ui/shared/TableViewToggleButton'; -import type { Props } from '../txs/TransactionsCrossChainContent'; -import TransactionsCrossChainContent from '../txs/TransactionsCrossChainContent'; +import TransactionsCrossChainContent from '../../components/txs/TransactionsCrossChainContent'; +import type { Props } from '../../components/txs/TransactionsCrossChainContent'; const AddressCrossChainTxs = ({ pagination, isLoading: isLoadingProp, ...rest }: Props) => { const isMobile = useIsMobile(); diff --git a/client/features/cross-chain-txs/pages/home/LatestCrossChainTxs.tsx b/client/features/cross-chain-txs/pages/home/LatestCrossChainTxs.tsx index 718c9b4b49..941fcb0a0b 100644 --- a/client/features/cross-chain-txs/pages/home/LatestCrossChainTxs.tsx +++ b/client/features/cross-chain-txs/pages/home/LatestCrossChainTxs.tsx @@ -7,13 +7,14 @@ import { route } from 'nextjs-routes'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import TransactionsCrossChainListItem from 'client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem'; +import { INTERCHAIN_MESSAGE } from 'client/features/cross-chain-txs/stubs/messages'; + import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; import { TableBody, TableRoot } from 'toolkit/chakra/table'; -import TransactionsCrossChainListItem from 'ui/crossChain/txs/TransactionsCrossChainListItem'; import LatestCrossChainTxsItemDesktop from './LatestCrossChainTxsItemDesktop'; diff --git a/client/features/cross-chain-txs/pages/home/LatestCrossChainTxsItemDesktop.tsx b/client/features/cross-chain-txs/pages/home/LatestCrossChainTxsItemDesktop.tsx index 28de215677..fe5511f167 100644 --- a/client/features/cross-chain-txs/pages/home/LatestCrossChainTxsItemDesktop.tsx +++ b/client/features/cross-chain-txs/pages/home/LatestCrossChainTxsItemDesktop.tsx @@ -9,12 +9,13 @@ import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInter import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; +import CrossChainBridgeLink from '../../components/CrossChainBridgeLink'; +import CrossChainMessageEntity from '../../components/CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../../components/CrossChainTxsStatusTag'; + interface Props { data: InterchainMessage; isLoading?: boolean; diff --git a/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx b/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx index 5814a0aac5..edc559f1da 100644 --- a/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx +++ b/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx @@ -8,7 +8,6 @@ import type { CrossChainChainsStatsSorting, CrossChainChainsStatsSortingField, C import useIsMobile from 'client/shared/hooks/useIsMobile'; -import { INTERCHAIN_STATS_CHAINS_ITEM } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; @@ -19,6 +18,7 @@ import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; import Sort from 'ui/shared/sort/Sort'; +import { INTERCHAIN_STATS_CHAINS_ITEM } from '../../stubs/messages'; import { ICTT_USERS_SORT_OPTIONS } from '../../utils/ictt-sort'; import IcttUsersListItem from './IcttUsersListItem'; import IcttUsersTable from './IcttUsersTable'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChain.pw.tsx b/client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.pw.tsx similarity index 91% rename from ui/crossChain/transfers/TokenTransfersCrossChain.pw.tsx rename to client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.pw.tsx index cebd3a7f84..782935d78d 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChain.pw.tsx +++ b/client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.pw.tsx @@ -1,7 +1,8 @@ import { Box } from '@chakra-ui/react'; -import * as crossChainConfigMock from 'mocks/crossChain/config'; -import * as crossChainTransfersMock from 'mocks/crossChain/transfers'; +import * as crossChainConfigMock from 'client/features/cross-chain-txs/mocks/config'; +import * as crossChainTransfersMock from 'client/features/cross-chain-txs/mocks/transfers'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChain.tsx b/client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.tsx similarity index 88% rename from ui/crossChain/transfers/TokenTransfersCrossChain.tsx rename to client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.tsx index 39bab0582b..d784f1a032 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain.tsx @@ -4,13 +4,13 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { INTERCHAIN_STATS_COMMON, INTERCHAIN_TRANSFER } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText'; -import TokenTransfersCrossChainContent from './TokenTransfersCrossChainContent'; +import TokenTransfersCrossChainContent from '../../components/token-transfers/TokenTransfersCrossChainContent'; +import { INTERCHAIN_STATS_COMMON, INTERCHAIN_TRANSFER } from '../../stubs/messages'; const TokenTransfersCrossChain = () => { const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({ diff --git a/ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png b/client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png similarity index 100% rename from ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png rename to client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png diff --git a/ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png b/client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png similarity index 100% rename from ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png rename to client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png diff --git a/ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-mobile-base-view-1.png b/client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-mobile-base-view-1.png similarity index 100% rename from ui/crossChain/transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-mobile-base-view-1.png rename to client/features/cross-chain-txs/pages/token-transfers/__screenshots__/TokenTransfersCrossChain.pw.tsx_default_txs-mobile-base-view-1.png diff --git a/ui/crossChain/tx/TxCrossChain.pw.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChain.pw.tsx similarity index 81% rename from ui/crossChain/tx/TxCrossChain.pw.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChain.pw.tsx index 9ea7408eb1..da1ec97e52 100644 --- a/ui/crossChain/tx/TxCrossChain.pw.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxCrossChain.pw.tsx @@ -1,6 +1,7 @@ -import * as crossChainConfigMock from 'mocks/crossChain/config'; -import * as crossChainTransfersMock from 'mocks/crossChain/transfers'; -import * as crossChainTxMock from 'mocks/crossChain/txs'; +import * as crossChainConfigMock from 'client/features/cross-chain-txs/mocks/config'; +import * as crossChainTransfersMock from 'client/features/cross-chain-txs/mocks/transfers'; +import * as crossChainTxMock from 'client/features/cross-chain-txs/mocks/txs'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/ui/crossChain/tx/TxCrossChain.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChain.tsx similarity index 92% rename from ui/crossChain/tx/TxCrossChain.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChain.tsx index 5afc353aad..67c81e914f 100644 --- a/ui/crossChain/tx/TxCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxCrossChain.tsx @@ -10,12 +10,12 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import TextAd from 'ui/shared/ad/TextAd'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import PageTitle from 'ui/shared/Page/PageTitle'; +import CrossChainMessageEntity from '../../components/CrossChainMessageEntity'; +import { INTERCHAIN_MESSAGE } from '../../stubs/messages'; import TxCrossChainDetails from './TxCrossChainDetails'; import TxCrossChainTransfers from './TxCrossChainTransfers'; diff --git a/ui/crossChain/tx/TxCrossChainDetails.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetails.tsx similarity index 96% rename from ui/crossChain/tx/TxCrossChainDetails.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChainDetails.tsx index b29f3c551a..80656dd8eb 100644 --- a/ui/crossChain/tx/TxCrossChainDetails.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetails.tsx @@ -6,13 +6,13 @@ import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; -import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; import RawInputData from 'ui/shared/RawInputData'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; +import CrossChainBridgeLink from '../../components/CrossChainBridgeLink'; +import CrossChainTxsStatusTag from '../../components/CrossChainTxsStatusTag'; import TxCrossChainDetailsLifecycle from './TxCrossChainDetailsLifecycle'; import TxCrossChainDetailsTransfers from './TxCrossChainDetailsTransfers'; diff --git a/ui/crossChain/tx/TxCrossChainDetailsLifecycle.pw.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsLifecycle.pw.tsx similarity index 86% rename from ui/crossChain/tx/TxCrossChainDetailsLifecycle.pw.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsLifecycle.pw.tsx index fe0816c55d..e191fde9b6 100644 --- a/ui/crossChain/tx/TxCrossChainDetailsLifecycle.pw.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsLifecycle.pw.tsx @@ -1,6 +1,7 @@ -import * as crossChainConfigMock from 'mocks/crossChain/config'; -import * as crossChainTransfersMock from 'mocks/crossChain/transfers'; -import * as crossChainTxMock from 'mocks/crossChain/txs'; +import * as crossChainConfigMock from 'client/features/cross-chain-txs/mocks/config'; +import * as crossChainTransfersMock from 'client/features/cross-chain-txs/mocks/transfers'; +import * as crossChainTxMock from 'client/features/cross-chain-txs/mocks/txs'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsLifecycle.tsx similarity index 100% rename from ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsLifecycle.tsx diff --git a/ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsTransfers.tsx similarity index 100% rename from ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChainDetailsTransfers.tsx diff --git a/ui/crossChain/tx/TxCrossChainTransfers.tsx b/client/features/cross-chain-txs/pages/tx/TxCrossChainTransfers.tsx similarity index 82% rename from ui/crossChain/tx/TxCrossChainTransfers.tsx rename to client/features/cross-chain-txs/pages/tx/TxCrossChainTransfers.tsx index 8cd7db9b71..76a33205b8 100644 --- a/ui/crossChain/tx/TxCrossChainTransfers.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxCrossChainTransfers.tsx @@ -7,9 +7,9 @@ import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import TokenTransfersCrossChainListItem from '../transfers/TokenTransfersCrossChainListItem'; -import TokenTransfersCrossChainTable from '../transfers/TokenTransfersCrossChainTable'; -import { getItemKey } from '../transfers/utils'; +import TokenTransfersCrossChainListItem from '../../components/token-transfers/TokenTransfersCrossChainListItem'; +import TokenTransfersCrossChainTable from '../../components/token-transfers/TokenTransfersCrossChainTable'; +import { getItemKey } from '../../components/token-transfers/utils'; interface Props { data: Array | undefined; diff --git a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx index 6f74b62030..1da64b338c 100644 --- a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx @@ -12,9 +12,10 @@ import config from 'configs/app'; import { mdash } from 'toolkit/utils/htmlEntities'; import * as DetailedInfoItemBreakdown from 'ui/shared/DetailedInfo/DetailedInfoItemBreakdown'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; -import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; + +import CrossChainMessageEntity from '../../components/CrossChainMessageEntity'; +import CrossChainTxsStatusTag from '../../components/CrossChainTxsStatusTag'; interface Props { data: InterchainMessage; diff --git a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx index fac985b1e0..3f004870b2 100644 --- a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx @@ -1,6 +1,7 @@ -import * as crossChainConfigMock from 'mocks/crossChain/config'; -import * as crossChainTransfersMock from 'mocks/crossChain/transfers'; -import * as crossChainTxMock from 'mocks/crossChain/txs'; +import * as crossChainConfigMock from 'client/features/cross-chain-txs/mocks/config'; +import * as crossChainTransfersMock from 'client/features/cross-chain-txs/mocks/transfers'; +import * as crossChainTxMock from 'client/features/cross-chain-txs/mocks/txs'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import { Container } from 'ui/shared/DetailedInfo/DetailedInfo'; diff --git a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx index 1d3e5922f9..329532433b 100644 --- a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx @@ -6,11 +6,11 @@ import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { CollapsibleList } from 'toolkit/chakra/collapsible'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; +import { INTERCHAIN_MESSAGE } from '../../stubs/messages'; import TxDetailsCrossChainMessage from './TxDetailsCrossChainMessage'; interface Props { diff --git a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx index 5544cbbf6b..0f080c853e 100644 --- a/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx @@ -4,12 +4,12 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useTxCrossChainTransfersQuery from 'client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery'; - import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; +import useTxCrossChainTransfersQuery from '../../hooks/useTxCrossChainTransfersQuery'; + interface Props { hash: string; isLoading: boolean; diff --git a/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx b/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx index a1a09f3b84..29fa2c8918 100644 --- a/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx @@ -5,12 +5,13 @@ import React from 'react'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; -import TokenTransfersCrossChainListItem from 'ui/crossChain/transfers/TokenTransfersCrossChainListItem'; -import TokenTransfersCrossChainTable from 'ui/crossChain/transfers/TokenTransfersCrossChainTable'; -import { getItemKey } from 'ui/crossChain/transfers/utils'; import DataListDisplay from 'ui/shared/DataListDisplay'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; +import TokenTransfersCrossChainListItem from '../../components/token-transfers/TokenTransfersCrossChainListItem'; +import TokenTransfersCrossChainTable from '../../components/token-transfers/TokenTransfersCrossChainTable'; +import { getItemKey } from '../../components/token-transfers/utils'; + interface Props { txQuery: TxQuery; crossChainQuery: QueryWithPagesResult<'interchainIndexer:tx_transfers'>; diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChain.pw.tsx_default_successful-tx-mobile-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChain.pw.tsx_default_successful-tx-mobile-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChain.pw.tsx_default_successful-tx-mobile-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChain.pw.tsx_default_successful-tx-mobile-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChain.pw.tsx_mobile_successful-tx-mobile-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChain.pw.tsx_mobile_successful-tx-mobile-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChain.pw.tsx_mobile_successful-tx-mobile-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChain.pw.tsx_mobile_successful-tx-mobile-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_dark-color-mode_successful-tx-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_dark-color-mode_successful-tx-mobile-dark-mode-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_dark-color-mode_successful-tx-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_dark-color-mode_successful-tx-mobile-dark-mode-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_failed-on-first-step-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_failed-on-first-step-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_failed-on-first-step-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_failed-on-first-step-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_initiated-tx-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_initiated-tx-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_initiated-tx-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_initiated-tx-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_successful-tx-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_successful-tx-mobile-dark-mode-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_successful-tx-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_default_successful-tx-mobile-dark-mode-1.png diff --git a/ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_mobile_successful-tx-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_mobile_successful-tx-mobile-dark-mode-1.png similarity index 100% rename from ui/crossChain/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_mobile_successful-tx-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxCrossChainDetailsLifecycle.pw.tsx_mobile_successful-tx-mobile-dark-mode-1.png diff --git a/ui/crossChain/txs/Transactions.tsx b/client/features/cross-chain-txs/pages/txs/Transactions.tsx similarity index 100% rename from ui/crossChain/txs/Transactions.tsx rename to client/features/cross-chain-txs/pages/txs/Transactions.tsx diff --git a/ui/crossChain/txs/TransactionsCrossChain.pw.tsx b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.pw.tsx similarity index 90% rename from ui/crossChain/txs/TransactionsCrossChain.pw.tsx rename to client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.pw.tsx index 8f126ee2c3..5f04d8abb2 100644 --- a/ui/crossChain/txs/TransactionsCrossChain.pw.tsx +++ b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.pw.tsx @@ -1,10 +1,10 @@ -import * as crossChainConfigMock from 'mocks/crossChain/config'; -import * as crossChainTransfersMock from 'mocks/crossChain/transfers'; -import * as crossChainTxMock from 'mocks/crossChain/txs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; +import * as crossChainConfigMock from '../../mocks/config'; +import * as crossChainTransfersMock from '../../mocks/transfers'; +import * as crossChainTxMock from '../../mocks/txs'; import TransactionsCrossChain from './TransactionsCrossChain'; test.describe('txs', () => { diff --git a/ui/crossChain/txs/TransactionsCrossChain.tsx b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.tsx similarity index 89% rename from ui/crossChain/txs/TransactionsCrossChain.tsx rename to client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.tsx index 3a55a0c449..1451db9ee3 100644 --- a/ui/crossChain/txs/TransactionsCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChain.tsx @@ -4,13 +4,13 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { INTERCHAIN_MESSAGE, INTERCHAIN_STATS_COMMON } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText'; -import TransactionsCrossChainContent from './TransactionsCrossChainContent'; +import TransactionsCrossChainContent from '../../components/txs/TransactionsCrossChainContent'; +import { INTERCHAIN_MESSAGE, INTERCHAIN_STATS_COMMON } from '../../stubs/messages'; import TransactionsCrossChainStats from './TransactionsCrossChainStats'; const TransactionsCrossChain = () => { diff --git a/ui/crossChain/txs/TransactionsCrossChainStats.tsx b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx similarity index 93% rename from ui/crossChain/txs/TransactionsCrossChainStats.tsx rename to client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx index ef715521c6..88dcf83f67 100644 --- a/ui/crossChain/txs/TransactionsCrossChainStats.tsx +++ b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx @@ -5,9 +5,10 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import { INTERCHAIN_STATS_DAILY } from 'stubs/interchainIndexer'; import StatsWidget from 'ui/shared/stats/StatsWidget'; +import { INTERCHAIN_STATS_DAILY } from '../../stubs/messages'; + interface Props {} const TransactionsCrossChainStats = (props: Props) => { diff --git a/ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png b/client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png similarity index 100% rename from ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png rename to client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_dark-color-mode_txs-desktop-base-view-dark-mode-1.png diff --git a/ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png b/client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png similarity index 100% rename from ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png rename to client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-desktop-base-view-dark-mode-1.png diff --git a/ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-mobile-base-view-1.png b/client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-mobile-base-view-1.png similarity index 100% rename from ui/crossChain/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-mobile-base-view-1.png rename to client/features/cross-chain-txs/pages/txs/__screenshots__/TransactionsCrossChain.pw.tsx_default_txs-mobile-base-view-1.png diff --git a/stubs/interchainIndexer.ts b/client/features/cross-chain-txs/stubs/messages.ts similarity index 100% rename from stubs/interchainIndexer.ts rename to client/features/cross-chain-txs/stubs/messages.ts diff --git a/ui/gasTracker/GasTrackerChart.tsx b/client/features/gas-tracker/components/GasTrackerChart.tsx similarity index 100% rename from ui/gasTracker/GasTrackerChart.tsx rename to client/features/gas-tracker/components/GasTrackerChart.tsx diff --git a/ui/gasTracker/GasTrackerFaq.tsx b/client/features/gas-tracker/components/GasTrackerFaq.tsx similarity index 100% rename from ui/gasTracker/GasTrackerFaq.tsx rename to client/features/gas-tracker/components/GasTrackerFaq.tsx diff --git a/ui/gasTracker/GasTrackerFaqItem.tsx b/client/features/gas-tracker/components/GasTrackerFaqItem.tsx similarity index 100% rename from ui/gasTracker/GasTrackerFaqItem.tsx rename to client/features/gas-tracker/components/GasTrackerFaqItem.tsx diff --git a/ui/gasTracker/GasTrackerNetworkUtilization.tsx b/client/features/gas-tracker/components/GasTrackerNetworkUtilization.tsx similarity index 100% rename from ui/gasTracker/GasTrackerNetworkUtilization.tsx rename to client/features/gas-tracker/components/GasTrackerNetworkUtilization.tsx diff --git a/ui/gasTracker/GasTrackerPriceSnippet.pw.tsx b/client/features/gas-tracker/components/GasTrackerPriceSnippet.pw.tsx similarity index 100% rename from ui/gasTracker/GasTrackerPriceSnippet.pw.tsx rename to client/features/gas-tracker/components/GasTrackerPriceSnippet.pw.tsx diff --git a/ui/gasTracker/GasTrackerPriceSnippet.tsx b/client/features/gas-tracker/components/GasTrackerPriceSnippet.tsx similarity index 100% rename from ui/gasTracker/GasTrackerPriceSnippet.tsx rename to client/features/gas-tracker/components/GasTrackerPriceSnippet.tsx diff --git a/ui/gasTracker/GasTrackerPrices.tsx b/client/features/gas-tracker/components/GasTrackerPrices.tsx similarity index 100% rename from ui/gasTracker/GasTrackerPrices.tsx rename to client/features/gas-tracker/components/GasTrackerPrices.tsx diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-gwei-as-primary-unit-dark-mode-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-gwei-as-primary-unit-dark-mode-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-gwei-as-primary-unit-dark-mode-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-gwei-as-primary-unit-dark-mode-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-usd-as-primary-unit-dark-mode-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-usd-as-primary-unit-dark-mode-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-usd-as-primary-unit-dark-mode-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_dark-color-mode_with-usd-as-primary-unit-dark-mode-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_loading-state-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_loading-state-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_loading-state-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_loading-state-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-gwei-as-primary-unit-dark-mode-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-gwei-as-primary-unit-dark-mode-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-gwei-as-primary-unit-dark-mode-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-gwei-as-primary-unit-dark-mode-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-small-values-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-small-values-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-small-values-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-small-values-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-usd-as-primary-unit-dark-mode-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-usd-as-primary-unit-dark-mode-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-usd-as-primary-unit-dark-mode-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-usd-as-primary-unit-dark-mode-1.png diff --git a/ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-zero-values-1.png b/client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-zero-values-1.png similarity index 100% rename from ui/gasTracker/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-zero-values-1.png rename to client/features/gas-tracker/components/__screenshots__/GasTrackerPriceSnippet.pw.tsx_default_with-zero-values-1.png diff --git a/ui/pages/GasTracker.pw.tsx b/client/features/gas-tracker/pages/index/GasTracker.pw.tsx similarity index 100% rename from ui/pages/GasTracker.pw.tsx rename to client/features/gas-tracker/pages/index/GasTracker.pw.tsx diff --git a/ui/pages/GasTracker.tsx b/client/features/gas-tracker/pages/index/GasTracker.tsx similarity index 90% rename from ui/pages/GasTracker.tsx rename to client/features/gas-tracker/pages/index/GasTracker.tsx index 8ff5c58b76..281f78f100 100644 --- a/ui/pages/GasTracker.tsx +++ b/client/features/gas-tracker/pages/index/GasTracker.tsx @@ -13,14 +13,15 @@ import GasInfoUpdateTimer from 'client/slices/gas/components/GasInfoUpdateTimer' import { HOMEPAGE_STATS } from 'client/slices/home/stubs'; import NativeTokenIcon from 'client/slices/token/components/icon/TokenIconNative'; +import GasTrackerChart from 'client/features/gas-tracker/components/GasTrackerChart'; +import GasTrackerFaq from 'client/features/gas-tracker/components/GasTrackerFaq'; +import GasTrackerNetworkUtilization from 'client/features/gas-tracker/components/GasTrackerNetworkUtilization'; +import GasTrackerPrices from 'client/features/gas-tracker/components/GasTrackerPrices'; + import config from 'configs/app'; import { Alert } from 'toolkit/chakra/alert'; import { Heading } from 'toolkit/chakra/heading'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import GasTrackerChart from 'ui/gasTracker/GasTrackerChart'; -import GasTrackerFaq from 'ui/gasTracker/GasTrackerFaq'; -import GasTrackerNetworkUtilization from 'ui/gasTracker/GasTrackerNetworkUtilization'; -import GasTrackerPrices from 'ui/gasTracker/GasTrackerPrices'; import PageTitle from 'ui/shared/Page/PageTitle'; import Time from 'ui/shared/time/Time'; diff --git a/ui/pages/__screenshots__/GasTracker.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/GasTracker.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png rename to client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/GasTracker.pw.tsx_default_base-view-dark-mode-mobile-1.png b/client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_default_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/GasTracker.pw.tsx_default_base-view-dark-mode-mobile-1.png rename to client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_default_base-view-dark-mode-mobile-1.png diff --git a/ui/pages/__screenshots__/GasTracker.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_mobile_base-view-dark-mode-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/GasTracker.pw.tsx_mobile_base-view-dark-mode-mobile-1.png rename to client/features/gas-tracker/pages/index/__screenshots__/GasTracker.pw.tsx_mobile_base-view-dark-mode-mobile-1.png diff --git a/ui/sol2uml/Sol2UmlDiagram.tsx b/client/features/sol2uml/components/Sol2UmlDiagram.tsx similarity index 100% rename from ui/sol2uml/Sol2UmlDiagram.tsx rename to client/features/sol2uml/components/Sol2UmlDiagram.tsx diff --git a/ui/pages/Sol2Uml.tsx b/client/features/sol2uml/pages/index/Sol2Uml.tsx similarity index 94% rename from ui/pages/Sol2Uml.tsx rename to client/features/sol2uml/pages/index/Sol2Uml.tsx index 0bed5c7091..5799aeccd2 100644 --- a/ui/pages/Sol2Uml.tsx +++ b/client/features/sol2uml/pages/index/Sol2Uml.tsx @@ -7,10 +7,10 @@ import React from 'react'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { useMultichainContext } from 'client/features/multichain/context'; +import Sol2UmlDiagram from 'client/features/sol2uml/components/Sol2UmlDiagram'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import PageTitle from 'ui/shared/Page/PageTitle'; -import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram'; const Sol2Uml = () => { const router = useRouter(); diff --git a/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx b/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx index 0c5a475d84..4abdf97698 100644 --- a/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx +++ b/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx @@ -7,6 +7,8 @@ import React from 'react'; import TokenTransferFilter from 'client/slices/token-transfer/components/TokenTransferFilter'; import AddressAdvancedFilterLink from 'client/features/advanced-filter/components/AddressAdvancedFilterLink'; +import TokenTransfersCrossChainContent from 'client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainContent'; +import { INTERCHAIN_TRANSFER } from 'client/features/cross-chain-txs/stubs/messages'; import CsvExport from 'client/features/csv-export/components/CsvExport'; import useIsMobile from 'client/shared/hooks/useIsMobile'; @@ -14,10 +16,8 @@ import useIsMounted from 'client/shared/hooks/useIsMounted'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { INTERCHAIN_TRANSFER } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import TokenTransfersCrossChainContent from 'ui/crossChain/transfers/TokenTransfersCrossChainContent'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/slices/address/pages/details/txs/AddressTxs.tsx b/client/slices/address/pages/details/txs/AddressTxs.tsx index d382d024d3..85331c49a4 100644 --- a/client/slices/address/pages/details/txs/AddressTxs.tsx +++ b/client/slices/address/pages/details/txs/AddressTxs.tsx @@ -6,6 +6,8 @@ import React from 'react'; import TxsWithApiSorting from 'client/slices/tx/pages/index/list/TxsWithApiSorting'; +import AddressTxsCrossChain from 'client/features/cross-chain-txs/pages/address/AddressTxsCrossChain'; +import { INTERCHAIN_MESSAGE } from 'client/features/cross-chain-txs/stubs/messages'; import CsvExport from 'client/features/csv-export/components/CsvExport'; import useIsMobile from 'client/shared/hooks/useIsMobile'; @@ -13,10 +15,8 @@ import useIsMounted from 'client/shared/hooks/useIsMounted'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AddressTxsCrossChain from 'ui/crossChain/address/AddressTxsCrossChain'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/slices/token-transfer/pages/index/TokenTransfers.tsx b/client/slices/token-transfer/pages/index/TokenTransfers.tsx index 010a0b50bb..36fde96ba1 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfers.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfers.tsx @@ -4,9 +4,10 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import TokenTransfersCrossChain from 'client/features/cross-chain-txs/pages/token-transfers/TokenTransfersCrossChain'; + import config from 'configs/app'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import TokenTransfersCrossChain from 'ui/crossChain/transfers/TokenTransfersCrossChain'; import PageTitle from 'ui/shared/Page/PageTitle'; import TokenTransfersLocal from './TokenTransfersLocal'; diff --git a/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx b/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx index 09dbbfc8c0..59bf647c63 100644 --- a/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx +++ b/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx @@ -6,8 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; import { MultichainProvider } from 'client/features/multichain/context'; - -import Sol2Uml from 'ui/pages/Sol2Uml'; +import Sol2Uml from 'client/features/sol2uml/pages/index/Sol2Uml'; const Page: NextPage = () => { return ( diff --git a/pages/cross-chain-tx/[id].tsx b/pages/cross-chain-tx/[id].tsx index 261394e5b3..49284743ff 100644 --- a/pages/cross-chain-tx/[id].tsx +++ b/pages/cross-chain-tx/[id].tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const TxCrossChain = dynamic(() => import('ui/crossChain/tx/TxCrossChain'), { ssr: false }); +const TxCrossChain = dynamic(() => import('client/features/cross-chain-txs/pages/tx/TxCrossChain'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/gas-tracker.tsx b/pages/gas-tracker.tsx index 5dd072dc3e..602c5f29e1 100644 --- a/pages/gas-tracker.tsx +++ b/pages/gas-tracker.tsx @@ -6,7 +6,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -const GasTracker = dynamic(() => import('ui/pages/GasTracker'), { ssr: false }); +const GasTracker = dynamic(() => import('client/features/gas-tracker/pages/index/GasTracker'), { ssr: false }); const Page: NextPage = () => { return ( diff --git a/pages/txs/index.tsx b/pages/txs/index.tsx index 87d50ae50e..5559b1affd 100644 --- a/pages/txs/index.tsx +++ b/pages/txs/index.tsx @@ -18,7 +18,7 @@ const Transactions = dynamic(() => { } if (config.features.crossChainTxs.isEnabled) { - return import('ui/crossChain/txs/Transactions'); + return import('client/features/cross-chain-txs/pages/txs/Transactions'); } return import('client/slices/tx/pages/index/TxIndex'); diff --git a/pages/visualize/sol2uml.tsx b/pages/visualize/sol2uml.tsx index e4592f7410..7339f01d06 100644 --- a/pages/visualize/sol2uml.tsx +++ b/pages/visualize/sol2uml.tsx @@ -5,7 +5,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import Sol2Uml from 'ui/pages/Sol2Uml'; +import Sol2Uml from 'client/features/sol2uml/pages/index/Sol2Uml'; const Page: NextPage = () => { return ( From 0c462d75bcbdf2b9d23cddd55baaca68dfca6caf Mon Sep 17 00:00:00 2001 From: tom goriunov Date: Fri, 22 May 2026 12:47:50 +0200 Subject: [PATCH 28/85] [ Migration ] Clean up lib, stubs, mocks, types and ui folders (#3485) * clean up lib, mocks, stubs and types * refactor easter eggs feature * clean up ui/pages/ * clean up ui/shared/ and ui/snippets/ * update tasks --- .agents/rules/code-quality.mdc | 2 +- client/MIGRATION_TASKS.md | 30 ++++---- client/api/services/general/misc.ts | 5 +- client/api/services/general/tx.ts | 2 +- .../screens/AuthModalScreenConnectWallet.tsx | 5 +- .../screens/AuthModalScreenEmail.tsx | 4 +- .../screens/AuthModalScreenOtpCode.tsx | 4 +- .../account/pages/login}/Login.pw.tsx | 0 .../features/account/pages/login}/Login.tsx | 0 .../account/pages/profile/MyProfileEmail.tsx | 4 +- .../verified-addresses}/AdminSupportText.tsx | 0 .../verified-addresses/VerifiedAddresses.tsx | 3 +- .../steps/AddressVerificationStepAddress.tsx | 3 +- .../AddressVerificationStepSignature.tsx | 3 +- .../index/VerifiedAddressesListItem.tsx | 3 +- .../index/VerifiedAddressesTableItem.tsx | 3 +- .../pages/address/useWidgetsConfigQuery.tsx | 3 +- .../address-3rd-party-widgets/stubs.ts | 0 .../components}/AppActionButton.pw.tsx | 4 +- .../components}/AppActionButton.tsx | 0 ...mode_button-without-styles-dark-mode-1.png | Bin ...r-mode_link-without-styles-dark-mode-1.png | Bin ...on.pw.tsx_default_button-with-styles-1.png | Bin ...ault_button-without-styles-dark-mode-1.png | Bin ...tton.pw.tsx_default_link-with-styles-1.png | Bin ...efault_link-without-styles-dark-mode-1.png | Bin .../hooks}/useAppActionData.tsx | 4 +- .../mocks/app-action-button.ts | 0 .../address-metadata/mocks/tag-types.ts | 0 .../features/address-metadata/mocks/tags.ts | 0 .../tag-submition/TagSubmitionForm.pw.tsx | 3 +- .../pages/tag-submition/TagSubmitionForm.tsx | 4 +- .../components/filters/BaseAgeFilter.tsx | 3 +- .../pages/index/AdvancedFilter.tsx | 2 +- client/features/advanced-filter/utils/lib.ts | 3 +- .../components/ChartWidgetContainer.tsx | 3 +- .../details/ChainStatsDetailsLineChart.tsx | 2 +- .../pages/index/ChainStatsCounters.tsx | 3 +- client/features/chain-stats/utils/interval.ts | 3 +- .../validator-index/ValidatorsCounters.tsx | 3 +- .../celo/pages/epoch-index/EpochsListItem.tsx | 3 +- .../mega-eth/pages/uptime/UptimeCharts.tsx | 3 +- .../mega-eth/pages/uptime/UptimeStats.tsx | 2 +- .../validator-index/ValidatorsCounters.tsx | 3 +- .../pages/cctx-index/ZetaChainAgeFilter.tsx | 3 +- .../cctx-index/ZetaChainCCTXListItem.tsx | 3 +- .../pages/cctx-index/ZetaChainCCTXsStats.tsx | 5 +- .../pages/cctx-index/ZetaChainCCTXsTab.tsx | 2 +- .../pages/cctx-index/ZetaChainFilterTags.tsx | 2 +- .../components/ContractSecurityAudits.tsx | 3 +- .../components/ContractSubmitAuditForm.tsx | 3 +- .../TokenTransfersCrossChainListItem.tsx | 3 +- .../txs/TransactionsCrossChainListItem.tsx | 3 +- .../pages/txs/TransactionsCrossChainStats.tsx | 2 +- .../csv-export/components/CsvExport.tsx | 6 +- .../components/dialog/CsvExportDialog.tsx | 3 +- .../dialog/CsvExportFormDateField.tsx | 3 +- .../downloads/CsvExportDownloadsItem.tsx | 2 +- client/features/csv-export/utils/context.tsx | 2 +- .../csv-export/utils/get-file-name.ts | 3 +- .../components}/DeFiDropdown.tsx | 0 .../components}/DeFiDropdownItem.tsx | 2 +- .../features/defi-dropdown/types/client.ts | 0 .../components/puzzle-15}/Confetti.tsx | 0 .../components/puzzle-15}/Puzzle15.tsx | 9 ++- .../components/runner}/CapybaraRunner.tsx | 6 +- .../features/external-txs/types/client.ts | 0 .../pages/tx/TxFheOperations.tsx | 2 +- .../pages/tx/TxFheOperationsList.tsx | 2 +- .../pages/tx/TxFheOperationsListItem.tsx | 2 +- .../pages/tx/TxFheOperationsStats.tsx | 2 +- .../pages/tx/TxFheOperationsTable.tsx | 2 +- .../pages/tx/TxFheOperationsTableItem.tsx | 2 +- .../features/fhe-operations/stubs.ts | 2 +- client/features/fhe-operations/types/api.ts | 54 +++++++++++++ client/features/fhe-operations/utils/utils.ts | 2 +- .../pages/index/FlashblocksStats.tsx | 3 +- .../components}/GetGasButton.tsx | 0 .../features/get-gas-button/types/client.ts | 0 .../pages/dapp/MarketplaceAppTopBar.tsx | 3 +- .../revoke/components/Approvals.tsx | 2 +- .../revoke/components/ApprovalsListItem.tsx | 5 +- .../revoke/components/ApprovalsTable.tsx | 2 +- .../revoke/components/ApprovalsTableItem.tsx | 2 +- .../revoke/components/Content.pw.tsx | 2 +- .../revoke/components/Content.tsx | 2 +- .../revoke/hooks/useApprovalsQuery.tsx | 3 +- .../essential-dapp/revoke/hooks/useRevoke.tsx | 2 +- .../revoke/hooks/useSearchErc20Allowances.tsx | 2 +- .../revoke/hooks/useSearchNftAllowances.tsx | 2 +- .../revoke/lib/formatAllowance.ts | 2 +- .../essential-dapp/revoke/mocks.ts} | 2 +- .../pages/essential-dapp/revoke/stubs.ts | 2 +- .../pages/essential-dapp/revoke/types.ts | 0 .../hooks/useNotifyOnNavigation.tsx | 0 .../MultichainAddressTokensListItem.tsx | 2 +- .../MultichainAddressTokensTableItem.tsx | 2 +- .../multichain/pages/home/ChainWidget.tsx | 2 +- .../features/multichain/pages/home/Stats.tsx | 2 +- .../pages/home/useChartDataQuery.ts | 2 +- .../clusters/components}/ClusterIcon.tsx | 0 .../components/SearchBarSuggestCluster.tsx | 3 +- .../pages/index/ClustersDirectoryListItem.tsx | 3 +- .../components/NameDomainExpiryStatus.tsx | 2 +- .../components/SearchBarSuggestDomain.tsx | 2 +- .../pages/address/AddressEnsDomains.tsx | 3 +- .../pages/details/info/NameDomainDetails.tsx | 2 +- .../pages/index/NameDomainsListItem.tsx | 3 +- .../pages/index/NameDomainsTableItem.tsx | 3 +- .../pages/dashboard/tabs/ActivityTab.tsx | 2 +- .../ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx | 3 +- .../ArbitrumL2TxnBatchDetailsCelestiaDA.tsx | 3 +- .../common/components}/CeleniumLink.tsx | 0 .../common/components}/RollupStageBadge.tsx | 0 .../OptimisticL2TxnBatchBlobCelestia.tsx | 2 +- .../OptimisticL2WithdrawalsListItem.tsx | 3 +- .../OptimisticL2WithdrawalsTableItem.tsx | 3 +- .../features/safe}/hooks/useIsSafeAddress.tsx | 0 .../tx-actions/pages/tx/TxDetailsAction.tsx | 4 +- .../tx-actions/pages/tx/TxDetailsActions.tsx | 2 +- .../pages/tx/TxDetailsActionsRaw.tsx | 2 +- client/features/tx-actions/types/api.ts | 27 ++++++- .../components}/TxAuthorizationStatus.tsx | 3 +- .../pages/tx/TxAuthorizationsListItem.tsx | 3 +- .../pages/tx/TxAuthorizationsTableItem.tsx | 3 +- .../common/components/TxInterpretation.tsx | 2 +- .../x-star-score/hooks}/useFetchXStarScore.ts | 0 .../analytics/google}/GoogleAnalytics.tsx | 0 .../analytics/mixpanel/useMixpanelInit.tsx | 2 +- .../shared/date-and-time}/dayjs.ts | 0 .../date-and-time/useTimeAgoIncrement.tsx | 3 +- .../shared/numbers}/compareBns.ts | 0 .../shared/numbers}/sumBnReducer.ts | 0 .../shared/re-captcha}/ReCaptcha.tsx | 0 .../shared/re-captcha}/useReCaptcha.tsx | 0 .../shared/stats/LineChartWidget.pw.tsx | 16 ++-- .../shared/stats/LineChartWidget.pwstory.tsx | 6 +- .../shared/stats/StatsWidget.pw.tsx | 0 {ui => client}/shared/stats/StatsWidget.tsx | 0 ..._dark-color-mode_base-view-dark-mode-1.png | Bin ..._dark-color-mode_base-view-dark-mode-2.png | Bin ..._dark-color-mode_base-view-dark-mode-3.png | Bin ..._dark-color-mode_base-view-dark-mode-4.png | Bin ...t.pw.tsx_default_base-view-dark-mode-1.png | Bin ...t.pw.tsx_default_base-view-dark-mode-2.png | Bin ...t.pw.tsx_default_base-view-dark-mode-3.png | Bin ...t.pw.tsx_default_base-view-dark-mode-4.png | Bin ...LineChartWidget.pw.tsx_default_error-1.png | Bin ...Widget.pw.tsx_default_incomplete-day-1.png | Bin ...Widget.pw.tsx_default_incomplete-day-2.png | Bin ...neChartWidget.pw.tsx_default_loading-1.png | Bin ...idget.pw.tsx_default_multiple-charts-1.png | Bin ...idget.pw.tsx_default_multiple-charts-2.png | Bin ...idget.pw.tsx_default_multiple-charts-3.png | Bin ...rtWidget.pw.tsx_default_small-values-1.png | Bin ...fault_small-variations-in-big-values-1.png | Bin ...or-mode_with-positive-diff-dark-mode-1.png | Bin ...sWidget.pw.tsx_default_loading-state-1.png | Bin ...idget.pw.tsx_default_with-long-value-1.png | Bin ...et.pw.tsx_default_with-negative-diff-1.png | Bin ...dget.pw.tsx_default_with-period-only-1.png | Bin ...default_with-positive-diff-dark-mode-1.png | Bin .../stats/get-stats-label-from-title.ts | 0 .../shared/stats/line-chart-config.ts | 0 .../shared/stats/line-chart-utils.ts | 0 .../components/entity/AddressEntity.pw.tsx | 3 +- .../slices/address/pages/details/Address.tsx | 4 +- .../coin-balance/AddressCoinBalanceChart.tsx | 2 +- .../pages/details/info/AddressAlerts.pw.tsx | 3 +- .../info/token-select/TokenSelectItem.tsx | 2 +- client/slices/block/hooks/useBlockTxsQuery.ts | 2 +- .../countdown-details/BlockCountdown.tsx | 6 +- .../create-google-calendar-link.ts | 3 +- .../countdown-details/create-ics-file-blob.ts | 3 +- client/slices/block/types/api.ts | 8 +- .../slices/block/types/config.ts | 0 client/slices/block/utils/format-rpc-data.ts | 2 +- .../slices/chain}/TestnetBadge.tsx | 0 .../indexing-status/IndexingStatusBlocks.tsx | 6 +- .../IndexingStatusInternalTxs.tsx | 6 +- .../slices/chain/indexing-status/types.ts | 2 +- .../slices/chain/logo}/NetworkIcon.pw.tsx | 0 .../slices/chain/logo}/NetworkIcon.tsx | 0 .../slices/chain/logo}/NetworkLogo.pw.tsx | 0 .../slices/chain/logo}/NetworkLogo.tsx | 0 ...ark-color-mode_custom-icon-dark-mode-1.png | Bin ...option---default-dark-mode-base-view-1.png | Bin ...olor-mode_placeholder-icon-dark-mode-1.png | Bin ...pw.tsx_default_custom-icon-dark-mode-1.png | Bin ...x_default_placeholder-icon-dark-mode-1.png | Bin ...ark-color-mode_custom-logo-dark-mode-1.png | Bin ...option---default-dark-mode-base-view-1.png | Bin ...olor-mode_placeholder-logo-dark-mode-1.png | Bin ...pw.tsx_default_custom-logo-dark-mode-1.png | Bin ...x_default_placeholder-logo-dark-mode-1.png | Bin .../slices/chain/logo}/consts.ts | 0 .../pages/index/VerifiedContractsCounters.tsx | 3 +- .../gas/components/GasInfoUpdateTimer.tsx | 3 +- .../slices/home/mocks/indexing-status.ts | 0 .../charts/ChainIndicatorChartContent.tsx | 3 +- .../index/stats/LatestBatchStatsWidget.tsx | 2 +- .../index/stats/LatestBlockStatsWidget.tsx | 2 +- .../slices/home/pages/index/stats/Stats.tsx | 3 +- .../home/pages/index/stats/StatsDegraded.tsx | 5 +- client/slices/home/types/api.ts | 28 ++++++- client/slices/home/types/client.ts | 2 +- client/slices/home/utils/chart.ts | 3 +- client/slices/home/utils/stats.ts | 3 +- .../internal-tx/pages/tx/TxInternals.tsx | 3 +- .../SearchBarSuggestAddress.tsx | 2 +- .../search-results/SearchResultListItem.tsx | 2 +- .../search-results/SearchResultTableItem.tsx | 2 +- .../components/list/TokenTransferListItem.tsx | 2 +- .../list/TokenTransferTableItem.tsx | 2 +- .../snippet/TokenTransferSnippetNft.tsx | 3 +- .../pages/index/TokenTransfersListItem.tsx | 2 +- .../pages/index/TokenTransfersTableItem.tsx | 2 +- .../pages/token/TokenTransferListItem.tsx | 2 +- .../pages/token/TokenTransferTableItem.tsx | 2 +- .../token/components}/NativeTokenTag.tsx | 0 .../token/components/entity}/NftEntity.pw.tsx | 0 .../token/components/entity}/NftEntity.tsx | 4 +- ...tEntity.pw.tsx_default_customization-1.png | Bin .../NftEntity.pw.tsx_default_loading-1.png | Bin .../NftEntity.pw.tsx_default_long-id-1.png | Bin ...tity.pw.tsx_default_variants-content-1.png | Bin ...y.pw.tsx_default_variants-subheading-1.png | Bin .../components/nft-media}/NftFallback.tsx | 0 .../token/components/nft-media}/NftHtml.tsx | 0 .../token/components/nft-media}/NftImage.tsx | 0 .../components/nft-media}/NftMedia.pw.tsx | 0 .../token/components/nft-media}/NftMedia.tsx | 0 .../nft-media}/NftMediaFullscreenModal.tsx | 0 .../token/components/nft-media}/NftVideo.tsx | 0 ...de_image-fullscreen-dark-mode-mobile-1.png | Bin ...k-color-mode_image-preview-dark-mode-1.png | Bin ...-color-mode_no-url-preview-dark-mode-1.png | Bin ...rk-color-mode_page-preview-dark-mode-1.png | Bin ...lt_image-fullscreen-dark-mode-mobile-1.png | Bin ....tsx_default_image-preview-dark-mode-1.png | Bin ...a.pw.tsx_default_image-preview-hover-1.png | Bin ...efault_image-preview-with-thumbnails-1.png | Bin ...lt_no-url-non-media-url-and-fallback-1.png | Bin ...tsx_default_no-url-preview-dark-mode-1.png | Bin ....pw.tsx_default_no-url-with-fallback-1.png | Bin ...w.tsx_default_page-preview-dark-mode-1.png | Bin ...le_image-fullscreen-dark-mode-mobile-1.png | Bin .../nft-media}/useLoadImageViaIpfs.tsx | 0 .../components/nft-media}/useNftMediaInfo.tsx | 0 .../token/components/nft-media}/utils.ts | 0 .../pages/address/AddressCollections.tsx | 2 +- .../pages/address/ERC20TokensListItem.tsx | 2 +- .../pages/address/ERC20TokensTableItem.tsx | 2 +- client/slices/token/pages/address/NFTItem.tsx | 4 +- client/slices/token/pages/address/utils.ts | 3 +- .../token/pages/details/info/TokenDetails.tsx | 4 +- .../details/info/TokenNftMarketplaces.tsx | 3 +- .../details/inventory/TokenInventoryItem.tsx | 2 +- .../instance/TokenInstanceMetadataFetcher.tsx | 4 +- .../instance/info/TokenInstanceDetails.pw.tsx | 3 +- .../instance/info/TokenInstanceDetails.tsx | 7 +- client/slices/token/types/client.ts | 7 ++ .../token/utils/metadata/attributes-parser.ts | 3 +- client/slices/tx/mocks/tx.ts | 3 +- .../tx/pages/details/TxSubHeading.pw.tsx | 2 +- .../slices/tx/pages/details/TxSubHeading.tsx | 4 +- .../details/state/TxStateTokenIdList.tsx | 3 +- .../slices/tx/pages/details/state/utils.tsx | 2 +- .../slices/tx/pages/index/stats/TxsStats.tsx | 5 +- client/slices/tx/utils/format-rpc-data.ts | 3 +- client/slices/tx/utils/sort-txs.ts | 2 +- {ui => client/sprite}/pages/Sprite.tsx | 0 .../pages/design-system/DesignSystem.tsx | 71 +++++++++--------- .../toolkit/pages/design-system}/parts.tsx | 0 .../pages/design-system/tabs}/Accordion.tsx | 2 +- .../pages/design-system/tabs}/Alert.pw.tsx | 0 .../pages/design-system/tabs}/Alert.tsx | 2 +- .../pages/design-system/tabs}/Badge.pw.tsx | 0 .../pages/design-system/tabs}/Badge.tsx | 2 +- .../pages/design-system/tabs}/Button.pw.tsx | 0 .../pages/design-system/tabs}/Button.tsx | 2 +- .../pages/design-system/tabs}/Checkbox.pw.tsx | 0 .../pages/design-system/tabs}/Checkbox.tsx | 2 +- .../pages/design-system/tabs}/Clipboard.tsx | 2 +- .../pages/design-system/tabs}/CloseButton.tsx | 2 +- .../pages/design-system/tabs}/Collapsible.tsx | 4 +- .../design-system/tabs}/ContentLoader.tsx | 2 +- .../pages/design-system/tabs}/Dialog.tsx | 4 +- .../pages/design-system/tabs}/EmptyState.tsx | 2 +- .../pages/design-system/tabs}/Field.pw.tsx | 0 .../pages/design-system/tabs}/Field.tsx | 2 +- .../design-system/tabs}/IconButton.pw.tsx | 0 .../pages/design-system/tabs}/IconButton.tsx | 2 +- .../pages/design-system/tabs}/Input.pw.tsx | 0 .../pages/design-system/tabs}/Input.tsx | 2 +- .../pages/design-system/tabs}/Link.pw.tsx | 0 .../pages/design-system/tabs}/Link.tsx | 4 +- .../pages/design-system/tabs}/Menu.tsx | 2 +- .../design-system/tabs}/Pagination.pw.tsx | 0 .../pages/design-system/tabs}/Pagination.tsx | 2 +- .../pages/design-system/tabs}/PinInput.tsx | 2 +- .../pages/design-system/tabs}/Popover.tsx | 2 +- .../pages/design-system/tabs}/Progress.tsx | 2 +- .../design-system/tabs}/ProgressCircle.pw.tsx | 0 .../design-system/tabs}/ProgressCircle.tsx | 2 +- .../pages/design-system/tabs}/Radio.pw.tsx | 0 .../pages/design-system/tabs}/Radio.tsx | 2 +- .../pages/design-system/tabs}/Rating.tsx | 2 +- .../pages/design-system/tabs}/Select.pw.tsx | 0 .../pages/design-system/tabs}/Select.tsx | 2 +- .../pages/design-system/tabs}/Skeleton.tsx | 2 +- .../pages/design-system/tabs}/Spinner.tsx | 2 +- .../pages/design-system/tabs}/Status.tsx | 2 +- .../pages/design-system/tabs}/Switch.pw.tsx | 0 .../pages/design-system/tabs}/Switch.tsx | 2 +- .../pages/design-system/tabs}/Table.tsx | 2 +- .../pages/design-system/tabs}/Tabs.pw.tsx | 0 .../pages/design-system/tabs}/Tabs.tsx | 2 +- .../pages/design-system/tabs}/Tag.pw.tsx | 0 .../toolkit/pages/design-system/tabs}/Tag.tsx | 5 +- .../pages/design-system/tabs}/Textarea.pw.tsx | 0 .../pages/design-system/tabs}/Textarea.tsx | 4 +- .../pages/design-system/tabs}/Toast.tsx | 4 +- .../pages/design-system/tabs}/Tooltip.tsx | 2 +- .../pages/design-system/tabs}/Values.pw.tsx | 0 .../pages/design-system/tabs}/Values.tsx | 2 +- ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ert.pw.tsx_default_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...dge.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...ton.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...box.pw.tsx_default_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...eld.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...ton.pw.tsx_default_default-dark-mode-1.png | Bin .../Input.pw.tsx_default_default-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-2.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...ink.pw.tsx_default_default-dark-mode-1.png | Bin ...ink.pw.tsx_default_default-dark-mode-2.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...ion.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...cle.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...dio.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-2.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-2.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-2.png | Bin ...ect.pw.tsx_default_default-dark-mode-1.png | Bin ...ect.pw.tsx_default_default-dark-mode-2.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...tch.pw.tsx_default_default-dark-mode-1.png | Bin ...mode_color-theme-overrides-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...ault_color-theme-overrides-dark-mode-1.png | Bin ...abs.pw.tsx_default_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...Tag.pw.tsx_default_default-dark-mode-1.png | Bin ...sx_dark-color-mode_default-dark-mode-1.png | Bin ...rea.pw.tsx_default_default-dark-mode-1.png | Bin .../Values.pw.tsx_default_default-1.png | Bin .../toolkit/pages/design-system}/utils.ts | 0 configs/app/features/defi-dropdown.ts | 2 +- configs/app/features/easter-egg-badge.ts | 26 ------- .../app/features/easter-egg-puzzle-badge.ts | 26 ------- configs/app/features/easter-eggs.ts | 28 +++++++ configs/app/features/external-txs.ts | 2 +- configs/app/features/get-gas-button.ts | 2 +- configs/app/features/index.ts | 3 +- configs/app/ui/views/block.ts | 4 +- configs/app/ui/views/nft.ts | 2 +- cspell.jsonc | 4 +- deploy/tools/envs-validator/schema.ts | 4 +- .../schemas/features/defiDropdown.ts | 2 +- deploy/tools/envs-validator/schemas/ui.ts | 6 +- docs/ENVS.md | 13 +--- eslint.config.mjs | 2 +- nextjs/PageNextJs.tsx | 2 +- {lib => nextjs}/monitoring/metrics.ts | 2 +- nextjs/utils/fetchApi.ts | 3 +- nextjs/utils/logRequestFromBot.ts | 3 +- pages/_app.tsx | 2 +- pages/api/monitoring/invalid-api-schema.ts | 3 +- .../[hash]/instances/[id]/media-type.ts | 2 +- pages/chakra.tsx | 5 +- pages/login.tsx | 2 +- pages/sprite.tsx | 3 +- pages/stats/[id].tsx | 2 +- playwright-ct.config.ts | 4 +- .../shared/feature-flags}/useFeatureValue.js | 0 .../shared/re-captcha}/useReCaptcha.js | 0 types/api/charts.ts | 27 ------- types/api/fheOperations.ts | 56 -------------- types/api/reward.ts | 6 -- types/api/txAction.ts | 28 ------- types/views/nft.ts | 8 -- ...olor-mode_base-view-mobile-dark-mode-1.png | Bin 43505 -> 0 bytes ...x_default_base-view-mobile-dark-mode-1.png | Bin 42994 -> 0 bytes ...sx_mobile_base-view-mobile-dark-mode-1.png | Bin 63873 -> 0 bytes ...ress.pw.tsx_default_degradation-view-1.png | Bin 45156 -> 0 bytes .../Token.pw.tsx_default_base-view-1.png | Bin 47622 -> 0 bytes .../Token.pw.tsx_default_bridged-token-1.png | Bin 54308 -> 0 bytes ...oken.pw.tsx_default_mobile-base-view-1.png | Bin 38917 -> 0 bytes ...sx_default_mobile-with-verified-info-1.png | Bin 43780 -> 0 bytes .../Token.pw.tsx_default_scam-token-1.png | Bin 52187 -> 0 bytes ...en.pw.tsx_default_with-verified-info-1.png | Bin 78413 -> 0 bytes ...tance.pw.tsx_default_metadata-update-1.png | Bin 189206 -> 0 bytes ...tance.pw.tsx_default_metadata-update-2.png | Bin 108617 -> 0 bytes ...tance.pw.tsx_default_metadata-update-3.png | Bin 107292 -> 0 bytes ...w.tsx_default_metadata-update-failed-1.png | Bin 195170 -> 0 bytes ...or-mode_with-search-mobile-dark-mode-1.png | Bin 48763 -> 0 bytes ...tsx_default_bridged-tokens-base-view-1.png | Bin 57684 -> 0 bytes ...tsx_default_bridged-tokens-base-view-2.png | Bin 32954 -> 0 bytes ...default_with-search-mobile-dark-mode-1.png | Bin 47764 -> 0 bytes ..._mobile_with-search-mobile-dark-mode-1.png | Bin 53870 -> 0 bytes .../custom/AppErrorTooManyRequests.tsx | 4 +- .../AppError/custom/AppErrorTxNotFound.tsx | 3 +- .../DetailedInfo/DetailedInfoTimestamp.tsx | 3 +- ui/shared/EntityTags/EntityTag.pw.tsx | 3 +- ui/shared/EntityTags/EntityTags.pw.tsx | 3 +- ui/shared/layout/Layout.pw.tsx | 3 +- ui/shared/layout/LayoutError.pw.tsx | 3 +- ui/shared/layout/LayoutHome.pw.tsx | 3 +- ui/shared/time/Time.tsx | 3 +- ui/shared/time/TimeWithTooltip.tsx | 2 +- ui/snippets/footer/Footer.tsx | 5 +- ui/snippets/header/Burger.tsx | 8 +- ui/snippets/header/HeaderAlert.tsx | 6 +- ui/snippets/header/HeaderMobile.tsx | 6 +- .../horizontal/NavigationDesktop.tsx | 7 +- .../navigation/vertical/NavigationDesktop.tsx | 9 ++- ui/snippets/topBar/TopBar.tsx | 2 +- ui/snippets/topBar/TopBarStats.tsx | 6 +- 456 files changed, 580 insertions(+), 557 deletions(-) rename {ui/pages => client/features/account/pages/login}/Login.pw.tsx (100%) rename {ui/pages => client/features/account/pages/login}/Login.tsx (100%) rename {ui/shared/texts => client/features/account/pages/verified-addresses}/AdminSupportText.tsx (100%) rename stubs/address3rdPartyWidgets.ts => client/features/address-3rd-party-widgets/stubs.ts (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/AppActionButton.pw.tsx (90%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/AppActionButton.tsx (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_button-without-styles-dark-mode-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_link-without-styles-dark-mode-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_default_button-with-styles-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_default_button-without-styles-dark-mode-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_default_link-with-styles-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/components}/__screenshots__/AppActionButton.pw.tsx_default_link-without-styles-dark-mode-1.png (100%) rename {ui/shared/AppActionButton => client/features/address-metadata/hooks}/useAppActionData.tsx (85%) rename mocks/metadata/appActionButton.ts => client/features/address-metadata/mocks/app-action-button.ts (100%) rename mocks/metadata/publicTagTypes.ts => client/features/address-metadata/mocks/tag-types.ts (100%) rename mocks/metadata/address.ts => client/features/address-metadata/mocks/tags.ts (100%) rename {ui/snippets/topBar => client/features/defi-dropdown/components}/DeFiDropdown.tsx (100%) rename {ui/snippets/topBar => client/features/defi-dropdown/components}/DeFiDropdownItem.tsx (91%) rename types/client/deFiDropdown.ts => client/features/defi-dropdown/types/client.ts (100%) rename {ui/games => client/features/easter-eggs/components/puzzle-15}/Confetti.tsx (100%) rename {ui/games => client/features/easter-eggs/components/puzzle-15}/Puzzle15.tsx (96%) rename {ui/games => client/features/easter-eggs/components/runner}/CapybaraRunner.tsx (92%) rename types/client/externalTxsConfig.ts => client/features/external-txs/types/client.ts (100%) rename stubs/fheOperations.ts => client/features/fhe-operations/stubs.ts (87%) rename {ui/snippets/topBar => client/features/get-gas-button/components}/GetGasButton.tsx (100%) rename types/client/gasRefuelProviderConfig.ts => client/features/get-gas-button/types/client.ts (100%) rename client/features/marketplace/{mocks/revoke-dapp.ts => pages/essential-dapp/revoke/mocks.ts} (96%) rename stubs/essentialDapps/revoke.ts => client/features/marketplace/pages/essential-dapp/revoke/stubs.ts (88%) rename types/client/revoke.ts => client/features/marketplace/pages/essential-dapp/revoke/types.ts (100%) rename {lib => client/features/metasuites}/hooks/useNotifyOnNavigation.tsx (100%) rename {ui/shared => client/features/name-services/clusters/components}/ClusterIcon.tsx (100%) rename {ui/shared/batch => client/features/rollup/common/components}/CeleniumLink.tsx (100%) rename {ui/snippets/navigation => client/features/rollup/common/components}/RollupStageBadge.tsx (100%) rename {lib => client/features/safe}/hooks/useIsSafeAddress.tsx (100%) rename {ui/shared/statusTag => client/features/tx-authorization/components}/TxAuthorizationStatus.tsx (93%) rename {lib/xStarScore => client/features/x-star-score/hooks}/useFetchXStarScore.ts (100%) rename {ui/shared => client/shared/analytics/google}/GoogleAnalytics.tsx (100%) rename {lib/date => client/shared/date-and-time}/dayjs.ts (100%) rename {lib/bigint => client/shared/numbers}/compareBns.ts (100%) rename {lib/bigint => client/shared/numbers}/sumBnReducer.ts (100%) rename {ui/shared/reCaptcha => client/shared/re-captcha}/ReCaptcha.tsx (100%) rename {ui/shared/reCaptcha => client/shared/re-captcha}/useReCaptcha.tsx (100%) rename ui/shared/chart/ChartWidget.pw.tsx => client/shared/stats/LineChartWidget.pw.tsx (91%) rename ui/shared/chart/ChartWidget.pwstory.tsx => client/shared/stats/LineChartWidget.pwstory.tsx (76%) rename {ui => client}/shared/stats/StatsWidget.pw.tsx (100%) rename {ui => client}/shared/stats/StatsWidget.tsx (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-2.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-2.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-3.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-3.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-4.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-4.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_error-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_error-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-2.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-2.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_loading-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_loading-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-2.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-2.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-3.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-3.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-values-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-values-1.png (100%) rename ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-variations-in-big-values-1.png => client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-variations-in-big-values-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_dark-color-mode_with-positive-diff-dark-mode-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_loading-state-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-long-value-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-negative-diff-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-period-only-1.png (100%) rename {ui => client}/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-positive-diff-dark-mode-1.png (100%) rename lib/stats/getStatsLabelFromTitle.ts => client/shared/stats/get-stats-label-from-title.ts (100%) rename ui/shared/chart/config.ts => client/shared/stats/line-chart-config.ts (100%) rename ui/shared/chart/utils.ts => client/shared/stats/line-chart-utils.ts (100%) rename types/views/block.ts => client/slices/block/types/config.ts (100%) rename {ui/snippets/navigation => client/slices/chain}/TestnetBadge.tsx (100%) rename ui/snippets/header/alerts/IndexingBlocksAlert.tsx => client/slices/chain/indexing-status/IndexingStatusBlocks.tsx (94%) rename ui/snippets/footer/IntTxsIndexingStatus.tsx => client/slices/chain/indexing-status/IndexingStatusInternalTxs.tsx (95%) rename types/api/indexingStatus.ts => client/slices/chain/indexing-status/types.ts (85%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/NetworkIcon.pw.tsx (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/NetworkIcon.tsx (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/NetworkLogo.pw.tsx (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/NetworkLogo.tsx (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-with-dark-option---default-dark-mode-base-view-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_placeholder-icon-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkIcon.pw.tsx_default_custom-icon-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkIcon.pw.tsx_default_placeholder-icon-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-with-dark-option---default-dark-mode-base-view-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_placeholder-logo-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkLogo.pw.tsx_default_custom-logo-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/__screenshots__/NetworkLogo.pw.tsx_default_placeholder-logo-dark-mode-1.png (100%) rename {ui/snippets/networkLogo => client/slices/chain/logo}/consts.ts (100%) rename mocks/stats/index.ts => client/slices/home/mocks/indexing-status.ts (100%) rename {ui/shared/celo => client/slices/token/components}/NativeTokenTag.tsx (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/NftEntity.pw.tsx (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/NftEntity.tsx (95%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/__screenshots__/NftEntity.pw.tsx_default_customization-1.png (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/__screenshots__/NftEntity.pw.tsx_default_loading-1.png (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/__screenshots__/NftEntity.pw.tsx_default_variants-content-1.png (100%) rename {ui/shared/entities/nft => client/slices/token/components/entity}/__screenshots__/NftEntity.pw.tsx_default_variants-subheading-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftFallback.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftHtml.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftImage.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftMedia.pw.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftMedia.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftMediaFullscreenModal.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/NftVideo.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_image-preview-with-thumbnails-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/useLoadImageViaIpfs.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/useNftMediaInfo.tsx (100%) rename {ui/shared/nft => client/slices/token/components/nft-media}/utils.ts (100%) rename {ui => client/sprite}/pages/Sprite.tsx (100%) rename ui/pages/Chakra.tsx => client/toolkit/pages/design-system/DesignSystem.tsx (67%) rename {ui/showcases => client/toolkit/pages/design-system}/parts.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Accordion.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Alert.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Alert.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Badge.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Badge.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Button.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Button.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Checkbox.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Checkbox.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Clipboard.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/CloseButton.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Collapsible.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/ContentLoader.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Dialog.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/EmptyState.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Field.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Field.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/IconButton.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/IconButton.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Input.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Input.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Link.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Link.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Menu.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Pagination.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Pagination.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/PinInput.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Popover.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Progress.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/ProgressCircle.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/ProgressCircle.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Radio.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Radio.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Rating.tsx (97%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Select.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Select.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Skeleton.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Spinner.tsx (95%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Status.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Switch.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Switch.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Table.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Tabs.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Tabs.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Tag.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Tag.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Textarea.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Textarea.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Toast.tsx (96%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Tooltip.tsx (98%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Values.pw.tsx (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/Values.tsx (99%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Alert.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Alert.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Badge.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Badge.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Button.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Button.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Button.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Button.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Checkbox.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Checkbox.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Checkbox.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Checkbox.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Field.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Field.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/IconButton.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/IconButton.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/IconButton.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/IconButton.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Input.pw.tsx_default_default-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Link.pw.tsx_default_default-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Pagination.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Pagination.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Pagination.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Pagination.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/ProgressCircle.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/ProgressCircle.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Radio.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Radio.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Radio.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Radio.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Select.pw.tsx_default_default-dark-mode-2.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Switch.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Switch.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Switch.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Switch.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tabs.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tabs.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tabs.pw.tsx_default_color-theme-overrides-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tabs.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tag.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Tag.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Textarea.pw.tsx_dark-color-mode_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Textarea.pw.tsx_default_default-dark-mode-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system/tabs}/__screenshots__/Values.pw.tsx_default_default-1.png (100%) rename {ui/showcases => client/toolkit/pages/design-system}/utils.ts (100%) delete mode 100644 configs/app/features/easter-egg-badge.ts delete mode 100644 configs/app/features/easter-egg-puzzle-badge.ts create mode 100644 configs/app/features/easter-eggs.ts rename {lib => nextjs}/monitoring/metrics.ts (95%) rename playwright/mocks/{lib/growthbook => client/shared/feature-flags}/useFeatureValue.js (100%) rename playwright/mocks/{ui/shared/recaptcha => client/shared/re-captcha}/useReCaptcha.js (100%) delete mode 100644 types/api/charts.ts delete mode 100644 types/api/fheOperations.ts delete mode 100644 types/api/reward.ts delete mode 100644 types/api/txAction.ts delete mode 100644 types/views/nft.ts delete mode 100644 ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png delete mode 100644 ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png delete mode 100644 ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png delete mode 100644 ui/pages/__screenshots__/Address.pw.tsx_default_degradation-view-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_bridged-token-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_scam-token-1.png delete mode 100644 ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png delete mode 100644 ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-1.png delete mode 100644 ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-2.png delete mode 100644 ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-3.png delete mode 100644 ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-failed-1.png delete mode 100644 ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_with-search-mobile-dark-mode-1.png delete mode 100644 ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-1.png delete mode 100644 ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-2.png delete mode 100644 ui/pages/__screenshots__/Tokens.pw.tsx_default_with-search-mobile-dark-mode-1.png delete mode 100644 ui/pages/__screenshots__/Tokens.pw.tsx_mobile_with-search-mobile-dark-mode-1.png diff --git a/.agents/rules/code-quality.mdc b/.agents/rules/code-quality.mdc index 9971165cb5..f40037a743 100644 --- a/.agents/rules/code-quality.mdc +++ b/.agents/rules/code-quality.mdc @@ -132,7 +132,7 @@ Remove commented-out code blocks. The git history preserves anything that might ### Date and time -- Import `dayjs` only via `lib/date/dayjs.ts` — never directly from the `dayjs` package. +- Import `dayjs` only via `client/shared/date-and-time/dayjs.ts` — never directly from the `dayjs` package. - Render all dates and times through the shared `Time` or `TimeWithTooltip` components. Do not format timestamps inline. ### Strict comparison diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md index 143dbfca09..081f1c485b 100644 --- a/client/MIGRATION_TASKS.md +++ b/client/MIGRATION_TASKS.md @@ -209,7 +209,7 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-2 · [x] Feature: `data-availability` · [#3453](https://github.com/blockscout/frontend/issues/3453) -### 6-3 · [~] Features: `multichain` and `multichain-button` · [#3479](https://github.com/blockscout/frontend/issues/3479) +### 6-3 · [x] Features: `multichain` and `multichain-button` · [#3479](https://github.com/blockscout/frontend/issues/3479) **Scope:** Includes `lib/multichain/`, `lib/contexts/multichain.tsx`, `ui/snippets/networkMenu/` → `client/features/multichain/` ### 6-4 · [x] Feature: `name-services` (domains and clusters) · [#3458](https://github.com/blockscout/frontend/issues/3458) @@ -217,35 +217,35 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-5 · [x] Feature: `account` · [#3456](https://github.com/blockscout/frontend/issues/3456) **Scope:** Includes `lib/hooks/useGetCsrfToken`, `ui/snippets/auth/`, `ui/snippets/user/` → `client/features/account/` -### 6-6 · [ ] Feature: `stats` +### 6-6 · [x] Feature: `stats` **Scope:** `lib/stats/` and stats UI → `client/features/stats/` -### 6-7 · [ ] Feature: `gas-tracker` +### 6-7 · [x] Feature: `gas-tracker` **Scope:** Config-gated tracker page (`NEXT_PUBLIC_GAS_TRACKER_ENABLED`). Depends on task 3-10 (`slices/gas/`) being merged first. - `ui/gasTracker/` + `ui/pages/GasTracker.tsx` → `client/features/gas-tracker/` - Imports gas-price primitives from `client/slices/gas/` -### 6-8 · [~] Feature: `validators` · [#3474](https://github.com/blockscout/frontend/issues/3474) +### 6-8 · [x] Feature: `validators` · [#3474](https://github.com/blockscout/frontend/issues/3474) -### 6-9 · [~] Feature: `marketplace` · [#3466](https://github.com/blockscout/frontend/issues/3466) +### 6-9 · [x] Feature: `marketplace` · [#3466](https://github.com/blockscout/frontend/issues/3466) **Scope:** Includes `lib/contexts/marketplace.tsx`, `lib/hooks/useGraphLinks` → `client/features/marketplace/` -### 6-10 · [~] Feature: `rewards` · [#3462](https://github.com/blockscout/frontend/issues/3462) +### 6-10 · [x] Feature: `rewards` · [#3462](https://github.com/blockscout/frontend/issues/3462) **Scope:** Includes `lib/contexts/rewards.tsx`, `lib/hooks/useRewardsActivity` → `client/features/rewards/` -### 6-11 · [~] Feature: `advanced-filter` · [#3468](https://github.com/blockscout/frontend/issues/3468) +### 6-11 · [x] Feature: `advanced-filter` · [#3468](https://github.com/blockscout/frontend/issues/3468) ### 6-12 · [ ] Feature: `ad-banner` **Scope:** Includes `lib/hooks/useAdblockDetect` → `client/features/ad-banner/`. Covers both `adsBanner.ts` and `adsText.ts` configs. -### 6-13 · [~] Features: `cross-chain-txs`, `gas-tracker`, `sol2uml` · [#3483](https://github.com/blockscout/frontend/issues/3483) +### 6-13 · [x] Features: `cross-chain-txs`, `gas-tracker`, `sol2uml` · [#3483](https://github.com/blockscout/frontend/issues/3483) -### 6-14 · [ ] Feature: `metasuites` +### 6-14 · [x] Feature: `metasuites` **Scope:** Includes `lib/hooks/useNotifyOnNavigation` → `client/features/metasuites/` ### 6-15 · [x] Feature: `csv-export` -### 6-16 · [~] Feature: `pools` · [#3463](https://github.com/blockscout/frontend/issues/3463) +### 6-16 · [x] Feature: `pools` · [#3463](https://github.com/blockscout/frontend/issues/3463) ### 6-17 · [x] Feature: `hot-contracts` @@ -257,12 +257,12 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-20 · [x] Feature: `visualize` **Scope:** Solidity-to-UML diagrams. See `configs/app/features/sol2uml.ts`. -### 6-21 · [~] Feature: `tx-interpretation` · [#3471](https://github.com/blockscout/frontend/issues/3471) +### 6-21 · [x] Feature: `tx-interpretation` · [#3471](https://github.com/blockscout/frontend/issues/3471) -### 6-22 · [~] Feature: `public-tags` (merge with `address-metadata`) · [#3477](https://github.com/blockscout/frontend/issues/3477) +### 6-22 · [x] Feature: `public-tags` (merge with `address-metadata`) · [#3477](https://github.com/blockscout/frontend/issues/3477) **Scope:** Community address labels. See `configs/app/features/publicTagsSubmission.ts`. -### 6-23 · [ ] Feature: `address-widgets` +### 6-23 · [x] Feature: `address-widgets` **Scope:** Third-party widgets on address pages. See `configs/app/features/address3rdPartyWidgets.ts`. ### 6-24 · [x] Feature: `address-metadata` @@ -272,13 +272,13 @@ migrated to `client/shared/` in 1-2 and do not appear here. ### 6-26 · [x] Feature: `bridged-tokens` -### 6-27 · [~] Features: `web3-wallet` and `connect-wallet` · [#3480](https://github.com/blockscout/frontend/issues/3480) +### 6-27 · [x] Features: `web3-wallet` and `connect-wallet` · [#3480](https://github.com/blockscout/frontend/issues/3480) **Scope:** Includes `blockchainInteraction.ts` config. ### 6-28 · [ ] Feature: `alternative-explorers` **Scope:** The "Verify with other explorers" menu shown on tx, block, address, and token pages. Move `ui/shared/NetworkExplorers.tsx` (and its `.pw.tsx` test) → `client/features/alternative-explorers/`. The util `client/features/alternative-explorers/utils/explorers.ts` already exists (landed in 1-2). Config-gated via `NEXT_PUBLIC_NETWORK_EXPLORERS`. -### 6-29 · [ ] Feature: `get-gas-button` +### 6-29 · [x] Feature: `get-gas-button` **Scope:** Gas refuel CTA — independently config-gated (`NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG`), no dependency on `gas-tracker` being enabled. - `ui/snippets/topBar/GetGasButton.tsx` → `client/features/get-gas-button/` - `types/client/gasRefuelProviderConfig.ts` → `client/features/get-gas-button/types/` diff --git a/client/api/services/general/misc.ts b/client/api/services/general/misc.ts index f12b5c0b2c..f296ce1500 100644 --- a/client/api/services/general/misc.ts +++ b/client/api/services/general/misc.ts @@ -33,14 +33,13 @@ import type { TxInterpretationResponse } from 'client/features/tx-interpretation import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'client/features/tx-interpretation/noves/types/api'; import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'client/features/user-ops/types/api'; import type { Block } from 'client/slices/block/types/api'; -import type { HomeStats } from 'client/slices/home/types/api'; +import type { IndexingStatus } from 'client/slices/chain/indexing-status/types'; +import type { HomeStats, ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'client/slices/home/types/api'; import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'client/slices/search/types/api'; import type { Transaction, } from 'client/slices/tx/types/api'; -import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig } from 'types/api/configs'; -import type { IndexingStatus } from 'types/api/indexingStatus'; export const GENERAL_API_MISC_RESOURCES = { // WITHDRAWALS diff --git a/client/api/services/general/tx.ts b/client/api/services/general/tx.ts index 4ba1f4831d..3e14b0d1b9 100644 --- a/client/api/services/general/tx.ts +++ b/client/api/services/general/tx.ts @@ -3,6 +3,7 @@ import type { ApiResource } from '../../types'; import type { TransactionsResponseWatchlist } from 'client/features/account/types/api'; import type { TransactionsResponseWithBlobs, TxsWithBlobsFilters, TxBlobs } from 'client/features/data-availability/types/api'; +import type { FheOperationsResponse } from 'client/features/fhe-operations/types/api'; import type { TxInterpretationResponse } from 'client/features/tx-interpretation/common/types/api'; import type { InternalTransactionFilters, InternalTransactionsResponse } from 'client/slices/internal-tx/types/api'; import type { LogsResponseTx } from 'client/slices/log/types/api'; @@ -16,7 +17,6 @@ import type { TxStateChanges, TxRawTracesResponse, } from 'client/slices/tx/types/api'; -import type { FheOperationsResponse } from 'types/api/fheOperations'; export const GENERAL_API_TX_RESOURCES = { txs_stats: { diff --git a/client/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx b/client/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx index f615d9b1bb..ef6fe471b4 100644 --- a/client/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx +++ b/client/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx @@ -9,9 +9,8 @@ import type { UserInfo } from 'client/features/account/types/api'; import useSignInWithWallet from 'client/features/account/hooks/useSignInWithWallet'; import type * as mixpanel from 'client/shared/analytics/mixpanel'; - -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; interface Props { onSuccess: (screen: ScreenSuccess) => void; diff --git a/client/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx b/client/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx index 4e9742e81f..33226213ce 100644 --- a/client/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx +++ b/client/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx @@ -12,12 +12,12 @@ import useApiFetch from 'client/api/hooks/useApiFetch'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import getErrorMessage from 'client/shared/errors/get-error-message'; import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; import { FormFieldEmail } from 'toolkit/components/forms/fields/FormFieldEmail'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; interface Props { onSubmit: (screen: Screen) => void; diff --git a/client/features/account/components/auth-modal/screens/AuthModalScreenOtpCode.tsx b/client/features/account/components/auth-modal/screens/AuthModalScreenOtpCode.tsx index 6b4b53a90a..704ade8d2d 100644 --- a/client/features/account/components/auth-modal/screens/AuthModalScreenOtpCode.tsx +++ b/client/features/account/components/auth-modal/screens/AuthModalScreenOtpCode.tsx @@ -12,12 +12,12 @@ import useApiFetch from 'client/api/hooks/useApiFetch'; import getErrorMessage from 'client/shared/errors/get-error-message'; import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; import IconSvg from 'ui/shared/IconSvg'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import AuthModalFieldOtpCode from '../fields/AuthModalFieldOtpCode'; diff --git a/ui/pages/Login.pw.tsx b/client/features/account/pages/login/Login.pw.tsx similarity index 100% rename from ui/pages/Login.pw.tsx rename to client/features/account/pages/login/Login.pw.tsx diff --git a/ui/pages/Login.tsx b/client/features/account/pages/login/Login.tsx similarity index 100% rename from ui/pages/Login.tsx rename to client/features/account/pages/login/Login.tsx diff --git a/client/features/account/pages/profile/MyProfileEmail.tsx b/client/features/account/pages/profile/MyProfileEmail.tsx index b0cb777b94..22b3863dbf 100644 --- a/client/features/account/pages/profile/MyProfileEmail.tsx +++ b/client/features/account/pages/profile/MyProfileEmail.tsx @@ -16,6 +16,8 @@ import AuthModal from 'client/features/account/components/auth-modal/AuthModal'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import getErrorMessage from 'client/shared/errors/get-error-message'; import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import config from 'configs/app'; import { Button } from 'toolkit/chakra/button'; @@ -23,8 +25,6 @@ import { Heading } from 'toolkit/chakra/heading'; import { toaster } from 'toolkit/chakra/toaster'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import MyProfileFieldsEmail from './fields/MyProfileFieldsEmail'; diff --git a/ui/shared/texts/AdminSupportText.tsx b/client/features/account/pages/verified-addresses/AdminSupportText.tsx similarity index 100% rename from ui/shared/texts/AdminSupportText.tsx rename to client/features/account/pages/verified-addresses/AdminSupportText.tsx diff --git a/client/features/account/pages/verified-addresses/VerifiedAddresses.tsx b/client/features/account/pages/verified-addresses/VerifiedAddresses.tsx index a612b95736..526f5ab56f 100644 --- a/client/features/account/pages/verified-addresses/VerifiedAddresses.tsx +++ b/client/features/account/pages/verified-addresses/VerifiedAddresses.tsx @@ -29,7 +29,8 @@ import { BackToButton } from 'toolkit/components/buttons/BackToButton'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; -import AdminSupportText from 'ui/shared/texts/AdminSupportText'; + +import AdminSupportText from './AdminSupportText'; const VerifiedAddresses = () => { useRedirectForInvalidAuthToken(); diff --git a/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepAddress.tsx b/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepAddress.tsx index 7ec229839f..d167962ab8 100644 --- a/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepAddress.tsx +++ b/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepAddress.tsx @@ -23,7 +23,8 @@ import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddress'; -import AdminSupportText from 'ui/shared/texts/AdminSupportText'; + +import AdminSupportText from '../../AdminSupportText'; type Fields = RootFields & AddressVerificationFormFirstStepFields; diff --git a/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx b/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx index 1d26aca1fd..24635ed74c 100644 --- a/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx +++ b/client/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx @@ -30,7 +30,8 @@ import { Radio, RadioGroup } from 'toolkit/chakra/radio'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; import { SIGNATURE_REGEXP } from 'toolkit/components/forms/validators/signature'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import AdminSupportText from 'ui/shared/texts/AdminSupportText'; + +import AdminSupportText from '../../AdminSupportText'; type Fields = RootFields & AddressVerificationFormSecondStepFields; diff --git a/client/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx b/client/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx index f79d724450..246a49e2b6 100644 --- a/client/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx +++ b/client/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx @@ -7,7 +7,8 @@ import type { TokenInfoApplication, VerifiedAddress } from 'client/features/acco import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { IconButton } from 'toolkit/chakra/icon-button'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/account/pages/verified-addresses/index/VerifiedAddressesTableItem.tsx b/client/features/account/pages/verified-addresses/index/VerifiedAddressesTableItem.tsx index 09dee12c98..32d3551246 100644 --- a/client/features/account/pages/verified-addresses/index/VerifiedAddressesTableItem.tsx +++ b/client/features/account/pages/verified-addresses/index/VerifiedAddressesTableItem.tsx @@ -7,7 +7,8 @@ import type { TokenInfoApplication, VerifiedAddress } from 'client/features/acco import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { IconButton } from 'toolkit/chakra/icon-button'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx b/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx index 597e8f36bb..87da9a9567 100644 --- a/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx +++ b/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx @@ -8,7 +8,8 @@ import useApiFetch from 'client/api/hooks/useFetch'; import type { ResourceError } from 'client/api/resources'; import config from 'configs/app'; -import { WIDGET_CONFIG } from 'stubs/address3rdPartyWidgets'; + +import { WIDGET_CONFIG } from '../../stubs'; const feature = config.features.address3rdPartyWidgets; const configUrl = (feature.isEnabled && feature.configUrl) || ''; diff --git a/stubs/address3rdPartyWidgets.ts b/client/features/address-3rd-party-widgets/stubs.ts similarity index 100% rename from stubs/address3rdPartyWidgets.ts rename to client/features/address-3rd-party-widgets/stubs.ts diff --git a/ui/shared/AppActionButton/AppActionButton.pw.tsx b/client/features/address-metadata/components/AppActionButton.pw.tsx similarity index 90% rename from ui/shared/AppActionButton/AppActionButton.pw.tsx rename to client/features/address-metadata/components/AppActionButton.pw.tsx index ca52b3156d..7d0abda549 100644 --- a/ui/shared/AppActionButton/AppActionButton.pw.tsx +++ b/client/features/address-metadata/components/AppActionButton.pw.tsx @@ -1,11 +1,11 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; +import type { AddressMetadataTagFormatted } from '../types/client'; -import * as actionButtonMetadataMock from 'mocks/metadata/appActionButton'; import { test, expect } from 'playwright/lib'; +import * as actionButtonMetadataMock from '../mocks/app-action-button'; import AppActionButton from './AppActionButton'; test.beforeEach(async({ mockAssetResponse }) => { diff --git a/ui/shared/AppActionButton/AppActionButton.tsx b/client/features/address-metadata/components/AppActionButton.tsx similarity index 100% rename from ui/shared/AppActionButton/AppActionButton.tsx rename to client/features/address-metadata/components/AppActionButton.tsx diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_button-without-styles-dark-mode-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_button-without-styles-dark-mode-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_button-without-styles-dark-mode-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_button-without-styles-dark-mode-1.png diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_link-without-styles-dark-mode-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_link-without-styles-dark-mode-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_link-without-styles-dark-mode-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_dark-color-mode_link-without-styles-dark-mode-1.png diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_button-with-styles-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_button-with-styles-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_button-with-styles-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_button-with-styles-1.png diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_button-without-styles-dark-mode-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_button-without-styles-dark-mode-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_button-without-styles-dark-mode-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_button-without-styles-dark-mode-1.png diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_link-with-styles-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_link-with-styles-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_link-with-styles-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_link-with-styles-1.png diff --git a/ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_link-without-styles-dark-mode-1.png b/client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_link-without-styles-dark-mode-1.png similarity index 100% rename from ui/shared/AppActionButton/__screenshots__/AppActionButton.pw.tsx_default_link-without-styles-dark-mode-1.png rename to client/features/address-metadata/components/__screenshots__/AppActionButton.pw.tsx_default_link-without-styles-dark-mode-1.png diff --git a/ui/shared/AppActionButton/useAppActionData.tsx b/client/features/address-metadata/hooks/useAppActionData.tsx similarity index 85% rename from ui/shared/AppActionButton/useAppActionData.tsx rename to client/features/address-metadata/hooks/useAppActionData.tsx index 97103a9c27..6c87e9c79b 100644 --- a/ui/shared/AppActionButton/useAppActionData.tsx +++ b/client/features/address-metadata/hooks/useAppActionData.tsx @@ -2,10 +2,10 @@ import { useMemo } from 'react'; -import useAddressMetadataInfoQuery from 'client/features/address-metadata/hooks/useAddressMetadataInfoQuery'; - import config from 'configs/app'; +import useAddressMetadataInfoQuery from './useAddressMetadataInfoQuery'; + export default function useAppActionData(address: string | undefined = '', isEnabled = true) { const memoizedArray = useMemo(() => address ? [ address ] : [], [ address ]); const { data } = useAddressMetadataInfoQuery(memoizedArray, isEnabled); diff --git a/mocks/metadata/appActionButton.ts b/client/features/address-metadata/mocks/app-action-button.ts similarity index 100% rename from mocks/metadata/appActionButton.ts rename to client/features/address-metadata/mocks/app-action-button.ts diff --git a/mocks/metadata/publicTagTypes.ts b/client/features/address-metadata/mocks/tag-types.ts similarity index 100% rename from mocks/metadata/publicTagTypes.ts rename to client/features/address-metadata/mocks/tag-types.ts diff --git a/mocks/metadata/address.ts b/client/features/address-metadata/mocks/tags.ts similarity index 100% rename from mocks/metadata/address.ts rename to client/features/address-metadata/mocks/tags.ts diff --git a/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx index 390d549435..0ce2fe0782 100644 --- a/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.pw.tsx @@ -1,8 +1,9 @@ import React from 'react'; +import { publicTagTypes as configMock } from 'client/features/address-metadata/mocks/tag-types'; + import { base as useInfoMock } from 'client/features/account/mocks/user-profile'; -import { publicTagTypes as configMock } from 'mocks/metadata/publicTagTypes'; import { expect, test } from 'playwright/lib'; import * as mocks from './mocks'; diff --git a/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx index dccfcdb4d4..7e138d2736 100644 --- a/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx +++ b/client/features/address-metadata/pages/tag-submition/TagSubmitionForm.tsx @@ -15,6 +15,8 @@ import useApiFetch from 'client/api/hooks/useApiFetch'; import getErrorObj from 'client/shared/errors/get-error-obj'; import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; import useIsMobile from 'client/shared/hooks/useIsMobile'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import appConfig from 'configs/app'; import { Button } from 'toolkit/chakra/button'; @@ -23,8 +25,6 @@ import { FormFieldEmail } from 'toolkit/components/forms/fields/FormFieldEmail'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; import { FormFieldUrl } from 'toolkit/components/forms/fields/FormFieldUrl'; import { Hint } from 'toolkit/components/Hint/Hint'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import TagSubmitionFieldAddresses from './fields/TagSubmitionFieldAddresses'; import TagSubmitionFieldTags from './fields/TagSubmitionFieldTags'; diff --git a/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx b/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx index a552926847..f86578fe5e 100644 --- a/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx +++ b/client/features/advanced-filter/components/filters/BaseAgeFilter.tsx @@ -7,7 +7,8 @@ import React from 'react'; import { ADVANCED_FILTER_AGES, type AdvancedFilterAge } from '../../types/api'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Input } from 'toolkit/chakra/input'; import { PopoverCloseTriggerWrapper } from 'toolkit/chakra/popover'; import { ndash } from 'toolkit/utils/htmlEntities'; diff --git a/client/features/advanced-filter/pages/index/AdvancedFilter.tsx b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx index 6f1f3b0ef7..7fe606712a 100644 --- a/client/features/advanced-filter/pages/index/AdvancedFilter.tsx +++ b/client/features/advanced-filter/pages/index/AdvancedFilter.tsx @@ -22,12 +22,12 @@ import { AddressHighlightProvider } from 'client/slices/address/contexts/address import CsvExport from 'client/features/csv-export/components/CsvExport'; import { useMultichainContext } from 'client/features/multichain/context'; +import dayjs from 'client/shared/date-and-time/dayjs'; import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from-query'; -import dayjs from 'lib/date/dayjs'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; import { TableBody, TableCell, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; diff --git a/client/features/advanced-filter/utils/lib.ts b/client/features/advanced-filter/utils/lib.ts index bd4c4b00ee..b06299c557 100644 --- a/client/features/advanced-filter/utils/lib.ts +++ b/client/features/advanced-filter/utils/lib.ts @@ -6,7 +6,8 @@ import type { AdvancedFilterAge, AdvancedFilterParams } from '../types/api'; import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { getTokenTypes } from 'client/slices/token/utils/token-types'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { HOUR, DAY, MONTH } from 'toolkit/utils/consts'; import { FILTER_PARAM_NAMES } from './consts'; diff --git a/client/features/chain-stats/components/ChartWidgetContainer.tsx b/client/features/chain-stats/components/ChartWidgetContainer.tsx index da1670b6ed..08dc4a3ddb 100644 --- a/client/features/chain-stats/components/ChartWidgetContainer.tsx +++ b/client/features/chain-stats/components/ChartWidgetContainer.tsx @@ -7,9 +7,10 @@ import type { StatsIntervalIds } from '../types/client'; import { route, type Route } from 'nextjs-routes'; +import { useChartsConfig } from 'client/shared/stats/line-chart-config'; + import { ChartResolution } from 'toolkit/components/charts'; import { LineChartWidget } from 'toolkit/components/charts/line/LineChartWidget'; -import { useChartsConfig } from 'ui/shared/chart/config'; import useChartQuery from '../hooks/useChartQuery'; import { getChartUrl } from '../utils/chart'; diff --git a/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx index 9d02907927..3c8fea2206 100644 --- a/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx +++ b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx @@ -13,6 +13,7 @@ import { useMultichainContext } from 'client/features/multichain/context'; import useRoutedChainSelect from 'client/features/multichain/hooks/useRoutedChainSelect'; import * as mixpanel from 'client/shared/analytics/mixpanel'; +import { useChartsConfig } from 'client/shared/stats/line-chart-config'; import type { OnValueChangeHandler, SelectOption } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select'; @@ -21,7 +22,6 @@ import { ChartResetZoomButton } from 'toolkit/components/charts/components/Chart import { LineChartContent } from 'toolkit/components/charts/line/LineChartContent'; import LineChartMenu from 'toolkit/components/charts/line/parts/LineChartMenu'; import { useLineChartZoom } from 'toolkit/components/charts/line/utils/useLineChartZoom'; -import { useChartsConfig } from 'ui/shared/chart/config'; import ChartIntervalSelect from '../../components/ChartIntervalSelect'; import { DEFAULT_RESOLUTION } from '../../utils/resolution'; diff --git a/client/features/chain-stats/pages/index/ChainStatsCounters.tsx b/client/features/chain-stats/pages/index/ChainStatsCounters.tsx index 3464d51cd6..bf5282f115 100644 --- a/client/features/chain-stats/pages/index/ChainStatsCounters.tsx +++ b/client/features/chain-stats/pages/index/ChainStatsCounters.tsx @@ -5,9 +5,10 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; import { CHAIN_STATS_COUNTER } from '../../stubs/counters'; diff --git a/client/features/chain-stats/utils/interval.ts b/client/features/chain-stats/utils/interval.ts index 2653ee616b..24d2b10da6 100644 --- a/client/features/chain-stats/utils/interval.ts +++ b/client/features/chain-stats/utils/interval.ts @@ -6,8 +6,7 @@ import { StatsIntervalId, type StatsIntervalIds } from '../types/client'; import { ChartResolution } from 'toolkit/components/charts/types'; import getQueryParamString from 'client/shared/router/get-query-param-string'; - -import { formatDate } from 'ui/shared/chart/utils'; +import { formatDate } from 'client/shared/stats/line-chart-utils'; export const STATS_INTERVALS: { [key in StatsIntervalIds]: { title: string; shortTitle: string; start?: Date } } = { all: { diff --git a/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx index 8fd9f328d1..ed54240c64 100644 --- a/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx +++ b/client/features/chain-variants/blackfort/pages/validator-index/ValidatorsCounters.tsx @@ -7,8 +7,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { VALIDATORS_BLACKFORT_COUNTERS } from 'client/features/chain-variants/blackfort/stubs/validators'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; const ValidatorsCounters = () => { const countersQuery = useApiQuery('general:validators_blackfort_counters', { diff --git a/client/features/chain-variants/celo/pages/epoch-index/EpochsListItem.tsx b/client/features/chain-variants/celo/pages/epoch-index/EpochsListItem.tsx index f50e8be3a8..18ab2d2719 100644 --- a/client/features/chain-variants/celo/pages/epoch-index/EpochsListItem.tsx +++ b/client/features/chain-variants/celo/pages/epoch-index/EpochsListItem.tsx @@ -8,7 +8,8 @@ import type { CeloEpochListItem } from 'client/features/chain-variants/celo/type import CeloEpochStatus from 'client/features/chain-variants/celo/components/CeloEpochStatus'; import EpochEntity from 'client/features/chain-variants/celo/components/entity/EpochEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/client/features/chain-variants/mega-eth/pages/uptime/UptimeCharts.tsx b/client/features/chain-variants/mega-eth/pages/uptime/UptimeCharts.tsx index 4659e55e59..c1bb5ca22f 100644 --- a/client/features/chain-variants/mega-eth/pages/uptime/UptimeCharts.tsx +++ b/client/features/chain-variants/mega-eth/pages/uptime/UptimeCharts.tsx @@ -7,11 +7,12 @@ import React from 'react'; import type { UptimeHistoryFull, UptimeHistoryItem } from 'client/features/chain-variants/mega-eth/types/api'; import type { LineChartAxesConfigFn } from 'toolkit/components/charts/line/types'; +import { useChartsConfig } from 'client/shared/stats/line-chart-config'; + import { useSettingsContext } from 'lib/contexts/settings'; import { Heading } from 'toolkit/chakra/heading'; import { LineChartWidget } from 'toolkit/components/charts/line'; import { DAY, HOUR, SECOND } from 'toolkit/utils/consts'; -import { useChartsConfig } from 'ui/shared/chart/config'; import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect'; const INTERVALS = [ diff --git a/client/features/chain-variants/mega-eth/pages/uptime/UptimeStats.tsx b/client/features/chain-variants/mega-eth/pages/uptime/UptimeStats.tsx index 87bb44fd2c..898327d820 100644 --- a/client/features/chain-variants/mega-eth/pages/uptime/UptimeStats.tsx +++ b/client/features/chain-variants/mega-eth/pages/uptime/UptimeStats.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { UptimeRealTimeData } from 'client/features/chain-variants/mega-eth/types/api'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; interface Props { realtimeData: UptimeRealTimeData | null; diff --git a/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx b/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx index 305a8337db..ea2e6dc2a3 100644 --- a/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx +++ b/client/features/chain-variants/stability/pages/validator-index/ValidatorsCounters.tsx @@ -7,8 +7,9 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { VALIDATORS_STABILITY_COUNTERS } from 'client/features/chain-variants/stability/stubs/validators'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; const ValidatorsCounters = () => { const countersQuery = useApiQuery('general:validators_stability_counters', { diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx index 403fe1d9f7..684534fbcb 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainAgeFilter.tsx @@ -5,7 +5,8 @@ import type { ZetaChainCCTXFilterParams } from 'client/features/chain-variants/z import BaseAgeFilter, { type DateConverter } from 'client/features/advanced-filter/components/filters/BaseAgeFilter'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { SECOND } from 'toolkit/utils/consts'; const FILTER_PARAM_FROM = 'start_timestamp'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXListItem.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXListItem.tsx index 87cc0b0b1b..4f100616a7 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXListItem.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXListItem.tsx @@ -10,7 +10,8 @@ import TxEntityZetaChainCC from 'client/features/chain-variants/zeta-chain/compo import ZetaChainCCTXReducedStatus from 'client/features/chain-variants/zeta-chain/components/ZetaChainCCTXReducedStatus'; import ZetaChainCCTXValue from 'client/features/chain-variants/zeta-chain/components/ZetaChainCCTXValue'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { SECOND } from 'toolkit/utils/consts'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsStats.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsStats.tsx index 646d2d822c..9126bdad53 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsStats.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsStats.tsx @@ -8,9 +8,10 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import { TXS_STATS_MICROSERVICE } from 'client/slices/tx/stubs/tx'; +import getStatsLabelFromTitle from 'client/shared/stats/get-stats-label-from-title'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import getStatsLabelFromTitle from 'lib/stats/getStatsLabelFromTitle'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; interface Props extends BoxProps {} diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx index 8a27c9300e..cd326be085 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainCCTXsTab.tsx @@ -14,12 +14,12 @@ import { getDurationFromAge } from 'client/features/advanced-filter/utils/lib'; import { ZETA_CHAIN_CCTX_LIST_ITEM } from 'client/features/chain-variants/zeta-chain/stubs'; import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; +import dayjs from 'client/shared/date-and-time/dayjs'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from-query'; -import dayjs from 'lib/date/dayjs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterTags.tsx b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterTags.tsx index 1db78988a3..cadb2e46fa 100644 --- a/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterTags.tsx +++ b/client/features/chain-variants/zeta-chain/pages/cctx-index/ZetaChainFilterTags.tsx @@ -8,10 +8,10 @@ import type { ZetaChainCCTXFilterParams } from 'client/features/chain-variants/z import useZetaChainConfig from 'client/features/chain-variants/zeta-chain/hooks/useZetaChainConfig'; +import dayjs from 'client/shared/date-and-time/dayjs'; import shortenString from 'client/shared/text/shorten-string'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Link } from 'toolkit/chakra/link'; import { Tag } from 'toolkit/chakra/tag'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/client/features/contract-audit-reports/components/ContractSecurityAudits.tsx b/client/features/contract-audit-reports/components/ContractSecurityAudits.tsx index e2d77e7541..0ef2d86335 100644 --- a/client/features/contract-audit-reports/components/ContractSecurityAudits.tsx +++ b/client/features/contract-audit-reports/components/ContractSecurityAudits.tsx @@ -7,7 +7,8 @@ import type { SmartContractSecurityAuditSubmission } from 'client/features/contr import useApiQuery from 'client/api/hooks/useApiQuery'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/client/features/contract-audit-reports/components/ContractSubmitAuditForm.tsx b/client/features/contract-audit-reports/components/ContractSubmitAuditForm.tsx index 40756c3d66..81e9e7355e 100644 --- a/client/features/contract-audit-reports/components/ContractSubmitAuditForm.tsx +++ b/client/features/contract-audit-reports/components/ContractSubmitAuditForm.tsx @@ -10,7 +10,8 @@ import type { SmartContractSecurityAuditSubmission } from 'client/features/contr import useApiFetch from 'client/api/hooks/useApiFetch'; import type { ResourceError } from 'client/api/resources'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; import { FormFieldCheckbox } from 'toolkit/components/forms/fields/FormFieldCheckbox'; diff --git a/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx index 2ea4250569..236efc8f63 100644 --- a/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx +++ b/client/features/cross-chain-txs/components/token-transfers/TokenTransfersCrossChainListItem.tsx @@ -8,8 +8,9 @@ import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; diff --git a/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx index f3e5493c48..7546ef3152 100644 --- a/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx +++ b/client/features/cross-chain-txs/components/txs/TransactionsCrossChainListItem.tsx @@ -11,8 +11,9 @@ import { route } from 'nextjs-routes'; import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; diff --git a/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx index 88dcf83f67..9e06028298 100644 --- a/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx +++ b/client/features/cross-chain-txs/pages/txs/TransactionsCrossChainStats.tsx @@ -5,7 +5,7 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; import { INTERCHAIN_STATS_DAILY } from '../../stubs/messages'; diff --git a/client/features/csv-export/components/CsvExport.tsx b/client/features/csv-export/components/CsvExport.tsx index 79e30e0e7f..ffad1a4e69 100644 --- a/client/features/csv-export/components/CsvExport.tsx +++ b/client/features/csv-export/components/CsvExport.tsx @@ -19,21 +19,21 @@ import type { ResourceName, ResourcePathParams } from 'client/api/resources'; import { useMultichainContext } from 'client/features/multichain/context'; +import dayjs from 'client/shared/date-and-time/dayjs'; import getErrorMessage from 'client/shared/errors/get-error-message'; import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { IconButton } from 'toolkit/chakra/icon-button'; import { toaster } from 'toolkit/chakra/toaster'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { downloadBlob } from 'toolkit/utils/file'; import IconSvg from 'ui/shared/IconSvg'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import { useCsvExportContext } from '../utils/context'; import getFileName from '../utils/get-file-name'; diff --git a/client/features/csv-export/components/dialog/CsvExportDialog.tsx b/client/features/csv-export/components/dialog/CsvExportDialog.tsx index a37cde3b70..ea5f5d83e6 100644 --- a/client/features/csv-export/components/dialog/CsvExportDialog.tsx +++ b/client/features/csv-export/components/dialog/CsvExportDialog.tsx @@ -6,7 +6,8 @@ import { FormProvider, useForm } from 'react-hook-form'; import type { FormFields } from './types'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Button } from 'toolkit/chakra/button'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure'; diff --git a/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx b/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx index 7026f9e102..347f38a4e1 100644 --- a/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx +++ b/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx @@ -6,7 +6,8 @@ import type { UseFormReturn } from 'react-hook-form'; import type { FormFields } from './types'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; interface Props { diff --git a/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx b/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx index 6928305ed2..6daeb33a05 100644 --- a/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx +++ b/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx @@ -8,11 +8,11 @@ import { useInView } from 'react-intersection-observer'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import dayjs from 'client/shared/date-and-time/dayjs'; import shortenString from 'client/shared/text/shorten-string'; import config from 'configs/app'; import multichainConfig from 'configs/multichain'; -import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { Status } from 'toolkit/chakra/status'; diff --git a/client/features/csv-export/utils/context.tsx b/client/features/csv-export/utils/context.tsx index 062ece1083..2132e92c5e 100644 --- a/client/features/csv-export/utils/context.tsx +++ b/client/features/csv-export/utils/context.tsx @@ -9,10 +9,10 @@ import type { CsvExportItemResponse } from '../types/api'; import useApiFetch from 'client/api/hooks/useApiFetch'; import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import dayjs from 'client/shared/date-and-time/dayjs'; import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; import multichainConfig from 'configs/multichain'; -import dayjs from 'lib/date/dayjs'; import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/client/features/csv-export/utils/get-file-name.ts b/client/features/csv-export/utils/get-file-name.ts index 76f4343f5e..2e1cb9dee3 100644 --- a/client/features/csv-export/utils/get-file-name.ts +++ b/client/features/csv-export/utils/get-file-name.ts @@ -2,8 +2,9 @@ import type { CsvExportType } from '../types/client'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import type config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import getPrefixByFilter from './get-prefix-by-filter'; diff --git a/ui/snippets/topBar/DeFiDropdown.tsx b/client/features/defi-dropdown/components/DeFiDropdown.tsx similarity index 100% rename from ui/snippets/topBar/DeFiDropdown.tsx rename to client/features/defi-dropdown/components/DeFiDropdown.tsx diff --git a/ui/snippets/topBar/DeFiDropdownItem.tsx b/client/features/defi-dropdown/components/DeFiDropdownItem.tsx similarity index 91% rename from ui/snippets/topBar/DeFiDropdownItem.tsx rename to client/features/defi-dropdown/components/DeFiDropdownItem.tsx index 71538bacf8..f6489b2f33 100644 --- a/ui/snippets/topBar/DeFiDropdownItem.tsx +++ b/client/features/defi-dropdown/components/DeFiDropdownItem.tsx @@ -3,7 +3,7 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; -import type { DeFiDropdownItem as TDeFiDropdownItem } from 'types/client/deFiDropdown'; +import type { DeFiDropdownItem as TDeFiDropdownItem } from '../types/client'; import { route } from 'nextjs-routes'; diff --git a/types/client/deFiDropdown.ts b/client/features/defi-dropdown/types/client.ts similarity index 100% rename from types/client/deFiDropdown.ts rename to client/features/defi-dropdown/types/client.ts diff --git a/ui/games/Confetti.tsx b/client/features/easter-eggs/components/puzzle-15/Confetti.tsx similarity index 100% rename from ui/games/Confetti.tsx rename to client/features/easter-eggs/components/puzzle-15/Confetti.tsx diff --git a/ui/games/Puzzle15.tsx b/client/features/easter-eggs/components/puzzle-15/Puzzle15.tsx similarity index 96% rename from ui/games/Puzzle15.tsx rename to client/features/easter-eggs/components/puzzle-15/Puzzle15.tsx index be5e02a786..fbe591ecb7 100644 --- a/ui/games/Puzzle15.tsx +++ b/client/features/easter-eggs/components/puzzle-15/Puzzle15.tsx @@ -9,7 +9,7 @@ import { Link } from 'toolkit/chakra/link'; import Confetti from './Confetti'; -const easterEggPuzzleBadgeFeature = config.features.easterEggPuzzleBadge; +const easterEggsFeature = config.features.easterEggs; const getPossibleMoves = (emptyIndex: number): Array => { @@ -198,13 +198,14 @@ const Puzzle15 = () => { Click on a square to move it ) } - { isWon && easterEggPuzzleBadgeFeature.isEnabled && ( + { isWon && easterEggsFeature.isEnabled && easterEggsFeature.puzzle && ( You unlocked a hidden badge! Congratulations! You're eligible to claim an epic hidden badge! diff --git a/ui/games/CapybaraRunner.tsx b/client/features/easter-eggs/components/runner/CapybaraRunner.tsx similarity index 92% rename from ui/games/CapybaraRunner.tsx rename to client/features/easter-eggs/components/runner/CapybaraRunner.tsx index f274050d26..c08d329b24 100644 --- a/ui/games/CapybaraRunner.tsx +++ b/client/features/easter-eggs/components/runner/CapybaraRunner.tsx @@ -11,7 +11,7 @@ import config from 'configs/app'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; -const easterEggBadgeFeature = config.features.easterEggBadge; +const easterEggsFeature = config.features.easterEggs; const CapybaraRunner = () => { const [ hasReachedHighScore, setHasReachedHighScore ] = React.useState(false); @@ -52,12 +52,12 @@ const CapybaraRunner = () => { - { easterEggBadgeFeature.isEnabled && hasReachedHighScore && ( + { easterEggsFeature.isEnabled && easterEggsFeature.runner && hasReachedHighScore && ( You unlocked a hidden badge! Congratulations! You’re eligible to claim an epic hidden badge! diff --git a/types/client/externalTxsConfig.ts b/client/features/external-txs/types/client.ts similarity index 100% rename from types/client/externalTxsConfig.ts rename to client/features/external-txs/types/client.ts diff --git a/client/features/fhe-operations/pages/tx/TxFheOperations.tsx b/client/features/fhe-operations/pages/tx/TxFheOperations.tsx index 370313e1f9..2aa20bde78 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperations.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperations.tsx @@ -9,10 +9,10 @@ import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; -import { FHE_OPERATIONS_RESPONSE } from 'stubs/fheOperations'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay'; +import { FHE_OPERATIONS_RESPONSE } from '../../stubs'; import TxFHEOperationsList from './TxFheOperationsList'; import TxFHEOperationsStats from './TxFheOperationsStats'; import TxFHEOperationsTable from './TxFheOperationsTable'; diff --git a/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx index 20111b62d9..fad7a458d1 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { FheOperation } from 'types/api/fheOperations'; +import type { FheOperation } from '../../types/api'; import TxFHEOperationsListItem from './TxFheOperationsListItem'; diff --git a/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx index 62c08ec071..cdc7248c45 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx @@ -4,7 +4,7 @@ import { Box, Flex, Grid, Text } from '@chakra-ui/react'; import { capitalize } from 'es-toolkit'; import React from 'react'; -import type { FheOperation } from 'types/api/fheOperations'; +import type { FheOperation } from '../../types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx index 7470f85378..2560120c0a 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; interface Props { totalHcu: number; diff --git a/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx index 507d9d5539..495952e160 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx @@ -3,7 +3,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { FheOperation } from 'types/api/fheOperations'; +import type { FheOperation } from '../../types/api'; import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; diff --git a/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx index eb9ddaef53..8b7f3333c8 100644 --- a/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx @@ -4,7 +4,7 @@ import { Text } from '@chakra-ui/react'; import { capitalize } from 'es-toolkit'; import React from 'react'; -import type { FheOperation } from 'types/api/fheOperations'; +import type { FheOperation } from '../../types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; diff --git a/stubs/fheOperations.ts b/client/features/fhe-operations/stubs.ts similarity index 87% rename from stubs/fheOperations.ts rename to client/features/fhe-operations/stubs.ts index 1d70927534..5c34865d49 100644 --- a/stubs/fheOperations.ts +++ b/client/features/fhe-operations/stubs.ts @@ -1,4 +1,4 @@ -import type { FheOperation, FheOperationsResponse } from 'types/api/fheOperations'; +import type { FheOperation, FheOperationsResponse } from './types/api'; import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; diff --git a/client/features/fhe-operations/types/api.ts b/client/features/fhe-operations/types/api.ts index 478f008691..26aa609c5d 100644 --- a/client/features/fhe-operations/types/api.ts +++ b/client/features/fhe-operations/types/api.ts @@ -1,5 +1,59 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { AddressParam } from 'client/slices/address/types/api'; export interface TransactionFheOperations { fhe_operations_count?: number; } + +export type FheOperationType = + | 'arithmetic' | + 'bitwise' | + 'comparison' | + 'unary' | + 'control' | + 'encryption' | + 'random'; + +export type FheType = + | 'Bool' | + 'Uint8' | + 'Uint16' | + 'Uint32' | + 'Uint64' | + 'Uint128' | + 'Uint160' | + 'Uint256' | + 'Bytes64' | + 'Bytes128' | + 'Bytes256'; + +export interface FheOperationInputs { + lhs?: string; + rhs?: string; + ct?: string; + control?: string; + if_true?: string; + if_false?: string; + plaintext?: number; +} + +export interface FheOperation { + log_index: number; + operation: string; + type: FheOperationType; + fhe_type: FheType; + is_scalar: boolean; + hcu_cost: number; + hcu_depth: number; + caller: AddressParam | null; + inputs: FheOperationInputs; + result: string; + block_number: number; +} + +export interface FheOperationsResponse { + items: Array; + total_hcu: number; + max_depth_hcu: number; + operation_count: number; +} diff --git a/client/features/fhe-operations/utils/utils.ts b/client/features/fhe-operations/utils/utils.ts index 3948dfd91e..f956340a4a 100644 --- a/client/features/fhe-operations/utils/utils.ts +++ b/client/features/fhe-operations/utils/utils.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { FheOperationType } from 'types/api/fheOperations'; +import type { FheOperationType } from '../types/api'; import type { BadgeProps } from 'toolkit/chakra/badge'; diff --git a/client/features/flashblocks/pages/index/FlashblocksStats.tsx b/client/features/flashblocks/pages/index/FlashblocksStats.tsx index 691824dafe..791f089174 100644 --- a/client/features/flashblocks/pages/index/FlashblocksStats.tsx +++ b/client/features/flashblocks/pages/index/FlashblocksStats.tsx @@ -4,9 +4,10 @@ import { Box } from '@chakra-ui/react'; import { upperFirst } from 'es-toolkit'; import React from 'react'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; import { SECOND } from 'toolkit/utils/consts'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; const flashblocksFeature = config.features.flashblocks; diff --git a/ui/snippets/topBar/GetGasButton.tsx b/client/features/get-gas-button/components/GetGasButton.tsx similarity index 100% rename from ui/snippets/topBar/GetGasButton.tsx rename to client/features/get-gas-button/components/GetGasButton.tsx diff --git a/types/client/gasRefuelProviderConfig.ts b/client/features/get-gas-button/types/client.ts similarity index 100% rename from types/client/gasRefuelProviderConfig.ts rename to client/features/get-gas-button/types/client.ts diff --git a/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx b/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx index c8010c4119..055249b2f5 100644 --- a/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx +++ b/client/features/marketplace/pages/dapp/MarketplaceAppTopBar.tsx @@ -7,6 +7,8 @@ import type { MarketplaceApp } from 'client/features/marketplace/types/client'; import { route } from 'nextjs-routes'; +import NetworkIcon from 'client/slices/chain/logo/NetworkIcon'; + import UserProfileDesktop from 'client/features/account/components/user-profile/UserProfileDesktop'; import RewardsButton from 'client/features/rewards/components/RewardsButton'; @@ -18,7 +20,6 @@ import { useAppContext } from 'lib/contexts/app'; import { Link } from 'toolkit/chakra/link'; import { BackToButton } from 'toolkit/components/buttons/BackToButton'; import { makePrettyLink } from 'toolkit/utils/url'; -import NetworkIcon from 'ui/snippets/networkLogo/NetworkIcon'; import Rating from '../../components/rating/MarketplaceRating'; import MarketplaceAppInfo from './info/MarketplaceAppInfo'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx index 5189024d13..1c53cf4852 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Approvals.tsx @@ -2,8 +2,8 @@ import { Box, Flex } from '@chakra-ui/react'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx index 85cd0f6f2e..873eeb78fa 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsListItem.tsx @@ -3,13 +3,14 @@ import { Text } from '@chakra-ui/react'; import { useCallback, useState } from 'react'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Button } from 'toolkit/chakra/button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx index aaec1119f7..d59c4feb00 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx @@ -2,8 +2,8 @@ import React from 'react'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx index 46af9f90f4..7839246ec9 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTableItem.tsx @@ -3,8 +3,8 @@ import { Flex } from '@chakra-ui/react'; import { useCallback, useState } from 'react'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx index a8b4c7b6cd..47fec3621a 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx @@ -2,13 +2,13 @@ import React from 'react'; import { hash as addressHash } from 'client/slices/address/mocks/address'; -import * as revokeMock from 'client/features/marketplace/mocks/revoke-dapp'; import * as chainDataMock from 'client/features/multichain/mocks/chains'; import type { TestFnArgs } from 'playwright/lib'; import { test, expect } from 'playwright/lib'; import type useApprovalsQuery from '../hooks/useApprovalsQuery'; +import * as revokeMock from '../mocks'; import Content from './Content'; test('base view +@dark-mode +@mobile', async({ render, mockAssetResponse }: TestFnArgs) => { diff --git a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx index 1e06047b20..0831d2deac 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/components/Content.tsx @@ -3,8 +3,8 @@ import { Flex, Text, Separator } from '@chakra-ui/react'; import React, { useState, useCallback, useMemo } from 'react'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import { route } from 'nextjs/routes'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx index a5820af539..9d6e2a1b5b 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx @@ -8,10 +8,9 @@ import { isAddress, getAbiItem } from 'viem'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import { ALLOWANCES } from 'stubs/essentialDapps/revoke'; - import createPublicClient from '../lib/createPublicClient'; import getLogs from '../lib/getLogs'; +import { ALLOWANCES } from '../stubs'; import useSearchErc20Allowances from './useSearchErc20Allowances'; import useSearchNftAllowances from './useSearchNftAllowances'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx index ef0beefb06..3e1dc42511 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx @@ -7,8 +7,8 @@ import { useCallback } from 'react'; import { waitForTransactionReceipt } from 'viem/actions'; import { useAccount, useWriteContract, useSwitchChain } from 'wagmi'; +import type { AllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; -import type { AllowanceType } from 'types/client/revoke'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx index 4c0bacd37a..daed32136d 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx @@ -6,10 +6,10 @@ import { useCallback } from 'react'; import { getAddress, formatUnits, slice } from 'viem'; import type { PublicClient, Log } from 'viem'; +import type { AllowanceType, ContractAllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { AddressTokenBalancesResponse } from 'client/slices/address/types/api'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; import useApiFetch from 'client/api/hooks/useApiFetch'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx index dc60cadd13..0679875dac 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx +++ b/client/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx @@ -5,9 +5,9 @@ import { useCallback } from 'react'; import { getAbiItem, getAddress, slice } from 'viem'; import type { PublicClient, GetLogsParameters, Log } from 'viem'; +import type { AllowanceType, ContractAllowanceType } from '../types'; import type { EssentialDappsChainConfig } from 'client/features/marketplace/types/client'; import type { TokenInfo } from 'client/slices/token/types/api'; -import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; import useApiFetch from 'client/api/hooks/useApiFetch'; diff --git a/client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts b/client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts index 0ce02f38e0..aea37def72 100644 --- a/client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts +++ b/client/features/marketplace/pages/essential-dapp/revoke/lib/formatAllowance.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { AllowanceType } from 'types/client/revoke'; +import type { AllowanceType } from '../types'; export default function formatAllowance(approval: AllowanceType) { if (!approval.allowance) return 'N/A'; diff --git a/client/features/marketplace/mocks/revoke-dapp.ts b/client/features/marketplace/pages/essential-dapp/revoke/mocks.ts similarity index 96% rename from client/features/marketplace/mocks/revoke-dapp.ts rename to client/features/marketplace/pages/essential-dapp/revoke/mocks.ts index dbeb6d2444..7d60dfe9a2 100644 --- a/client/features/marketplace/mocks/revoke-dapp.ts +++ b/client/features/marketplace/pages/essential-dapp/revoke/mocks.ts @@ -1,4 +1,4 @@ -import type { AllowanceType } from 'types/client/revoke'; +import type { AllowanceType } from './types'; import * as contractsMock from 'client/slices/contract/mocks/list'; diff --git a/stubs/essentialDapps/revoke.ts b/client/features/marketplace/pages/essential-dapp/revoke/stubs.ts similarity index 88% rename from stubs/essentialDapps/revoke.ts rename to client/features/marketplace/pages/essential-dapp/revoke/stubs.ts index 38c9362a32..b610adc266 100644 --- a/stubs/essentialDapps/revoke.ts +++ b/client/features/marketplace/pages/essential-dapp/revoke/stubs.ts @@ -1,4 +1,4 @@ -import type { AllowanceType } from 'types/client/revoke'; +import type { AllowanceType } from './types'; import { ZERO_ADDRESS } from 'toolkit/utils/consts'; diff --git a/types/client/revoke.ts b/client/features/marketplace/pages/essential-dapp/revoke/types.ts similarity index 100% rename from types/client/revoke.ts rename to client/features/marketplace/pages/essential-dapp/revoke/types.ts diff --git a/lib/hooks/useNotifyOnNavigation.tsx b/client/features/metasuites/hooks/useNotifyOnNavigation.tsx similarity index 100% rename from lib/hooks/useNotifyOnNavigation.tsx rename to client/features/metasuites/hooks/useNotifyOnNavigation.tsx diff --git a/client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx index 30a0a08a39..37448392c7 100644 --- a/client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx @@ -9,11 +9,11 @@ import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/u import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NativeTokenTag from 'client/slices/token/components/NativeTokenTag'; import multichainConfig from 'configs/multichain'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; -import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; diff --git a/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx index a67a3da48e..6292953ada 100644 --- a/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx +++ b/client/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx @@ -9,11 +9,11 @@ import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/u import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NativeTokenTag from 'client/slices/token/components/NativeTokenTag'; import multichainConfig from 'configs/multichain'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; -import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; import SimpleValue from 'ui/shared/value/SimpleValue'; diff --git a/client/features/multichain/pages/home/ChainWidget.tsx b/client/features/multichain/pages/home/ChainWidget.tsx index e1ca6e5a2f..3ff87502f8 100644 --- a/client/features/multichain/pages/home/ChainWidget.tsx +++ b/client/features/multichain/pages/home/ChainWidget.tsx @@ -7,6 +7,7 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'client/features/multichain/types/client'; import { WALLETS_INFO } from 'client/features/web3-wallet/types/wallets'; +import RollupStageBadge from 'client/features/rollup/common/components/RollupStageBadge'; import useAddChainClick from 'client/features/web3-wallet/hooks/useAddChainClick'; import useProvider from 'client/features/web3-wallet/hooks/useProvider'; @@ -20,7 +21,6 @@ import { Tooltip } from 'toolkit/chakra/tooltip'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import IconSvg from 'ui/shared/IconSvg'; -import RollupStageBadge from 'ui/snippets/navigation/RollupStageBadge'; interface Props { data: ClusterChainConfig; diff --git a/client/features/multichain/pages/home/Stats.tsx b/client/features/multichain/pages/home/Stats.tsx index 2950e628cb..fc8b8535e2 100644 --- a/client/features/multichain/pages/home/Stats.tsx +++ b/client/features/multichain/pages/home/Stats.tsx @@ -10,7 +10,7 @@ import { sortHomeStatsItems, isHomeStatsItemEnabled } from 'client/slices/home/u import { HOMEPAGE_STATS } from 'client/features/multichain/stubs'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; import ChainIndicators from './ChainIndicators'; diff --git a/client/features/multichain/pages/home/useChartDataQuery.ts b/client/features/multichain/pages/home/useChartDataQuery.ts index f85b7c052c..7f72ffa75d 100644 --- a/client/features/multichain/pages/home/useChartDataQuery.ts +++ b/client/features/multichain/pages/home/useChartDataQuery.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query'; -import type { ChartMarketResponse } from 'types/api/charts'; +import type { ChartMarketResponse } from 'client/slices/home/types/api'; import useApiQuery from 'client/api/hooks/useApiQuery'; diff --git a/ui/shared/ClusterIcon.tsx b/client/features/name-services/clusters/components/ClusterIcon.tsx similarity index 100% rename from ui/shared/ClusterIcon.tsx rename to client/features/name-services/clusters/components/ClusterIcon.tsx diff --git a/client/features/name-services/clusters/components/SearchBarSuggestCluster.tsx b/client/features/name-services/clusters/components/SearchBarSuggestCluster.tsx index ec6a6d28ed..af49b27cfd 100644 --- a/client/features/name-services/clusters/components/SearchBarSuggestCluster.tsx +++ b/client/features/name-services/clusters/components/SearchBarSuggestCluster.tsx @@ -11,9 +11,10 @@ import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; import highlightText from 'client/shared/text/highlight-text'; -import ClusterIcon from 'ui/shared/ClusterIcon'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; +import ClusterIcon from './ClusterIcon'; + const SearchBarSuggestCluster = ({ data, searchTerm, addressFormat }: ItemsProps) => { const hash = data.filecoin_robust_address || (addressFormat === 'bech32' ? toBech32Address(data.address_hash) : data.address_hash); const isClickable = isEvmAddress(data.address_hash); diff --git a/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx b/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx index 3855abd48c..3d6c2c1f93 100644 --- a/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx +++ b/client/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx @@ -9,7 +9,8 @@ import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; import ClustersEntity from 'client/features/name-services/clusters/components/ClustersEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/client/features/name-services/domains/components/NameDomainExpiryStatus.tsx b/client/features/name-services/domains/components/NameDomainExpiryStatus.tsx index a9a97c9da0..718cd5f743 100644 --- a/client/features/name-services/domains/components/NameDomainExpiryStatus.tsx +++ b/client/features/name-services/domains/components/NameDomainExpiryStatus.tsx @@ -3,7 +3,7 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; interface Props { date: string | undefined; diff --git a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx index 61cbaabd5f..d95364f471 100644 --- a/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx +++ b/client/features/name-services/domains/components/SearchBarSuggestDomain.tsx @@ -11,9 +11,9 @@ import { toBech32Address } from 'client/slices/address/utils/bech32'; import * as EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; +import dayjs from 'client/shared/date-and-time/dayjs'; import highlightText from 'client/shared/text/highlight-text'; -import dayjs from 'lib/date/dayjs'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx index 215fa17af7..e9a79622ea 100644 --- a/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx +++ b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx @@ -13,7 +13,8 @@ import type { ResourceError } from 'client/api/resources'; import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; diff --git a/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx b/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx index daf14142f4..f71fd807de 100644 --- a/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx +++ b/client/features/name-services/domains/pages/details/info/NameDomainDetails.tsx @@ -11,6 +11,7 @@ import { route } from 'nextjs-routes'; import type { ResourceError } from 'client/api/resources'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import NameDomainExpiryStatus from 'client/features/name-services/domains/components/NameDomainExpiryStatus'; @@ -21,7 +22,6 @@ import { Tooltip } from 'toolkit/chakra/tooltip'; import { stripTrailingSlash } from 'toolkit/utils/url'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import IconSvg from 'ui/shared/IconSvg'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx b/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx index 5b3a5050d8..d8ebc9aa3f 100644 --- a/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx +++ b/client/features/name-services/domains/pages/index/NameDomainsListItem.tsx @@ -9,7 +9,8 @@ import AddressEntity from 'client/slices/address/components/entity/AddressEntity import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import NameDomainExpiryStatus from 'client/features/name-services/domains/components/NameDomainExpiryStatus'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx b/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx index 9a3bdaa46b..f5072a1949 100644 --- a/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx +++ b/client/features/name-services/domains/pages/index/NameDomainsTableItem.tsx @@ -8,7 +8,8 @@ import AddressEntity from 'client/slices/address/components/entity/AddressEntity import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { TableCell, TableRow } from 'toolkit/chakra/table'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx index 0b60c9e807..949d2f2c61 100644 --- a/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx +++ b/client/features/rewards/pages/dashboard/tabs/ActivityTab.tsx @@ -13,10 +13,10 @@ import useProfileQuery from 'client/features/account/hooks/useProfileQuery'; import { useRewardsContext } from 'client/features/rewards/context'; import { USER_ACTIVITY } from 'client/features/rewards/stubs'; +import dayjs from 'client/shared/date-and-time/dayjs'; import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; diff --git a/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx b/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx index e5af024c51..4e63765372 100644 --- a/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx +++ b/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx @@ -5,7 +5,8 @@ import React from 'react'; import type { ArbitrumL2TxnBatchDAAnytrust } from '../../types/api'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailsTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; diff --git a/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx b/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx index 049997889b..203c0b52f5 100644 --- a/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx +++ b/client/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx @@ -5,8 +5,9 @@ import React from 'react'; import type { ArbitrumL2TxnBatchDACelestia } from '../../types/api'; +import CeleniumLink from 'client/features/rollup/common/components/CeleniumLink'; + import config from 'configs/app'; -import CeleniumLink from 'ui/shared/batch/CeleniumLink'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; diff --git a/ui/shared/batch/CeleniumLink.tsx b/client/features/rollup/common/components/CeleniumLink.tsx similarity index 100% rename from ui/shared/batch/CeleniumLink.tsx rename to client/features/rollup/common/components/CeleniumLink.tsx diff --git a/ui/snippets/navigation/RollupStageBadge.tsx b/client/features/rollup/common/components/RollupStageBadge.tsx similarity index 100% rename from ui/snippets/navigation/RollupStageBadge.tsx rename to client/features/rollup/common/components/RollupStageBadge.tsx diff --git a/client/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx b/client/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx index 20fb4d7160..a74ec10b36 100644 --- a/client/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx +++ b/client/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx @@ -5,10 +5,10 @@ import React from 'react'; import type { OptimisticL2BlobTypeCelestia } from 'client/features/rollup/optimism/types/api'; +import CeleniumLink from 'client/features/rollup/common/components/CeleniumLink'; import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; import { layerLabels } from 'client/features/rollup/common/utils/layer'; -import CeleniumLink from 'ui/shared/batch/CeleniumLink'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; diff --git a/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx b/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx index 2c7d551afc..d5b122973e 100644 --- a/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx +++ b/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx @@ -10,8 +10,9 @@ import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; import { layerLabels } from 'client/features/rollup/common/utils/layer'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsTableItem.tsx b/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsTableItem.tsx index e66bd6beb1..9d729ab9ab 100644 --- a/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsTableItem.tsx +++ b/client/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsTableItem.tsx @@ -9,8 +9,9 @@ import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/lib/hooks/useIsSafeAddress.tsx b/client/features/safe/hooks/useIsSafeAddress.tsx similarity index 100% rename from lib/hooks/useIsSafeAddress.tsx rename to client/features/safe/hooks/useIsSafeAddress.tsx diff --git a/client/features/tx-actions/pages/tx/TxDetailsAction.tsx b/client/features/tx-actions/pages/tx/TxDetailsAction.tsx index a5790b47f9..f00163022d 100644 --- a/client/features/tx-actions/pages/tx/TxDetailsAction.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsAction.tsx @@ -4,13 +4,13 @@ import { Flex, chakra } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { TxAction, TxActionGeneral } from 'types/api/txAction'; +import type { TxAction, TxActionGeneral } from '../../types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import config from 'configs/app'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import IconSvg from 'ui/shared/IconSvg'; interface Props { diff --git a/client/features/tx-actions/pages/tx/TxDetailsActions.tsx b/client/features/tx-actions/pages/tx/TxDetailsActions.tsx index 2813c883e4..98ef1609b5 100644 --- a/client/features/tx-actions/pages/tx/TxDetailsActions.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsActions.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { TxAction } from 'types/api/txAction'; +import type { TxAction } from '../../types/api'; import config from 'configs/app'; diff --git a/client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx b/client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx index edc898d9e4..49ff859eb1 100644 --- a/client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx @@ -2,7 +2,7 @@ import React from 'react'; -import type { TxAction } from 'types/api/txAction'; +import type { TxAction } from '../../types/api'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoActionsWrapper from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; diff --git a/client/features/tx-actions/types/api.ts b/client/features/tx-actions/types/api.ts index 431b0ed0dd..a045a50b8a 100644 --- a/client/features/tx-actions/types/api.ts +++ b/client/features/tx-actions/types/api.ts @@ -1,6 +1,31 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { TxAction } from 'types/api/txAction'; +export interface TxActionGeneral { + type: 'mint' | 'burn' | 'collect' | 'swap'; + data: { + amount0: string; + symbol0: string; + address0: string; + amount1: string; + symbol1: string; + address1: string; + }; +} + +export interface TxActionNft { + type: 'mint_nft'; + data: { + name: string; + symbol: string; + address: string; + to: string; + ids: Array; + }; +} + +export type TxAction = { + protocol: 'uniswap_v3'; +} & (TxActionGeneral | TxActionNft); export interface TransactionActions { actions?: Array; diff --git a/ui/shared/statusTag/TxAuthorizationStatus.tsx b/client/features/tx-authorization/components/TxAuthorizationStatus.tsx similarity index 93% rename from ui/shared/statusTag/TxAuthorizationStatus.tsx rename to client/features/tx-authorization/components/TxAuthorizationStatus.tsx index 6d3b613cb5..c11514a4cf 100644 --- a/ui/shared/statusTag/TxAuthorizationStatus.tsx +++ b/client/features/tx-authorization/components/TxAuthorizationStatus.tsx @@ -6,8 +6,7 @@ import React from 'react'; import type { TxAuthorization } from 'client/features/tx-authorization/types/api'; import type { BadgeProps } from 'toolkit/chakra/badge'; - -import StatusTag from './StatusTag'; +import StatusTag from 'ui/shared/statusTag/StatusTag'; export interface Props extends BadgeProps { status: TxAuthorization['status']; diff --git a/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx index 431008b422..d5844d2ea8 100644 --- a/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx @@ -7,10 +7,11 @@ import type { TxAuthorization } from 'client/features/tx-authorization/types/api import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxAuthorizationStatus from 'client/features/tx-authorization/components/TxAuthorizationStatus'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import TxAuthorizationStatus from 'ui/shared/statusTag/TxAuthorizationStatus'; interface Props extends TxAuthorization { isLoading?: boolean; diff --git a/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx index 4c90ed55aa..d926a0af85 100644 --- a/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx @@ -7,10 +7,11 @@ import type { TxAuthorization } from 'client/features/tx-authorization/types/api import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxAuthorizationStatus from 'client/features/tx-authorization/components/TxAuthorizationStatus'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableRow, TableCell } from 'toolkit/chakra/table'; -import TxAuthorizationStatus from 'ui/shared/statusTag/TxAuthorizationStatus'; interface Props extends TxAuthorization { isLoading?: boolean; diff --git a/client/features/tx-interpretation/common/components/TxInterpretation.tsx b/client/features/tx-interpretation/common/components/TxInterpretation.tsx index 67ef7d7672..aff9d6c0df 100644 --- a/client/features/tx-interpretation/common/components/TxInterpretation.tsx +++ b/client/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -22,9 +22,9 @@ import EnsEntity from 'client/features/name-services/domains/components/EnsEntit import * as mixpanel from 'client/shared/analytics/mixpanel'; import { currencyUnits } from 'client/shared/chain/units'; +import dayjs from 'client/shared/date-and-time/dayjs'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import { Badge } from 'toolkit/chakra/badge'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { Image } from 'toolkit/chakra/image'; diff --git a/lib/xStarScore/useFetchXStarScore.ts b/client/features/x-star-score/hooks/useFetchXStarScore.ts similarity index 100% rename from lib/xStarScore/useFetchXStarScore.ts rename to client/features/x-star-score/hooks/useFetchXStarScore.ts diff --git a/ui/shared/GoogleAnalytics.tsx b/client/shared/analytics/google/GoogleAnalytics.tsx similarity index 100% rename from ui/shared/GoogleAnalytics.tsx rename to client/shared/analytics/google/GoogleAnalytics.tsx diff --git a/client/shared/analytics/mixpanel/useMixpanelInit.tsx b/client/shared/analytics/mixpanel/useMixpanelInit.tsx index 8edca32e5b..111158c623 100644 --- a/client/shared/analytics/mixpanel/useMixpanelInit.tsx +++ b/client/shared/analytics/mixpanel/useMixpanelInit.tsx @@ -7,11 +7,11 @@ import { useRouter } from 'next/router'; import React from 'react'; import { deviceType } from 'react-device-detect'; +import dayjs from 'client/shared/date-and-time/dayjs'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import * as cookies from 'client/shared/storage/cookies'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; import * as userProfile from './user-profile'; diff --git a/lib/date/dayjs.ts b/client/shared/date-and-time/dayjs.ts similarity index 100% rename from lib/date/dayjs.ts rename to client/shared/date-and-time/dayjs.ts diff --git a/client/shared/date-and-time/useTimeAgoIncrement.tsx b/client/shared/date-and-time/useTimeAgoIncrement.tsx index 9a025755b8..76319ac15b 100644 --- a/client/shared/date-and-time/useTimeAgoIncrement.tsx +++ b/client/shared/date-and-time/useTimeAgoIncrement.tsx @@ -2,7 +2,8 @@ import React from 'react'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { DAY, HOUR, MINUTE, SECOND } from 'toolkit/utils/consts'; function getUnits(diff: number) { diff --git a/lib/bigint/compareBns.ts b/client/shared/numbers/compareBns.ts similarity index 100% rename from lib/bigint/compareBns.ts rename to client/shared/numbers/compareBns.ts diff --git a/lib/bigint/sumBnReducer.ts b/client/shared/numbers/sumBnReducer.ts similarity index 100% rename from lib/bigint/sumBnReducer.ts rename to client/shared/numbers/sumBnReducer.ts diff --git a/ui/shared/reCaptcha/ReCaptcha.tsx b/client/shared/re-captcha/ReCaptcha.tsx similarity index 100% rename from ui/shared/reCaptcha/ReCaptcha.tsx rename to client/shared/re-captcha/ReCaptcha.tsx diff --git a/ui/shared/reCaptcha/useReCaptcha.tsx b/client/shared/re-captcha/useReCaptcha.tsx similarity index 100% rename from ui/shared/reCaptcha/useReCaptcha.tsx rename to client/shared/re-captcha/useReCaptcha.tsx diff --git a/ui/shared/chart/ChartWidget.pw.tsx b/client/shared/stats/LineChartWidget.pw.tsx similarity index 91% rename from ui/shared/chart/ChartWidget.pw.tsx rename to client/shared/stats/LineChartWidget.pw.tsx index b3a46eacf7..130069f9d8 100644 --- a/ui/shared/chart/ChartWidget.pw.tsx +++ b/client/shared/stats/LineChartWidget.pw.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { test, expect } from 'playwright/lib'; import type { LineChartWidgetProps } from 'toolkit/components/charts/line'; -import ChartWidget from './ChartWidget.pwstory'; +import LineChartWidget from './LineChartWidget.pwstory'; test.use({ viewport: { width: 400, height: 300 } }); @@ -37,7 +37,7 @@ const props: LineChartWidgetProps = { }; test('base view +@dark-mode', async({ render, page }) => { - const component = await render(); + const component = await render(); await page.waitForFunction(() => { return document.querySelector('path[data-name="native-coin-circulating-supply-small"]')?.getAttribute('opacity') === '1'; @@ -60,12 +60,12 @@ test('base view +@dark-mode', async({ render, page }) => { }); test('loading', async({ render }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot(); }); test('error', async({ render }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot(); }); @@ -92,7 +92,7 @@ test('small values', async({ render, page }) => { ], }; - const component = await render(); + const component = await render(); await page.waitForFunction(() => { return document.querySelector('path[data-name="native-coin-circulating-supply-small"]')?.getAttribute('opacity') === '1'; }); @@ -122,7 +122,7 @@ test('small variations in big values', async({ render, page }) => { ], }; - const component = await render(); + const component = await render(); await page.waitForFunction(() => { return document.querySelector('path[data-name="native-coin-circulating-supply-small"]')?.getAttribute('opacity') === '1'; }); @@ -143,7 +143,7 @@ test('incomplete day', async({ render, page }) => { ], }; - const component = await render(); + const component = await render(); await page.waitForFunction(() => { return document.querySelector('path[data-name="native-coin-circulating-supply-small"]')?.getAttribute('opacity') === '1'; }); @@ -192,7 +192,7 @@ test('multiple charts', async({ render, page }) => { ], }; - const component = await render(); + const component = await render(); await page.waitForFunction(() => { return document.querySelector('path[data-name="native-coin-circulating-supply-small"]')?.getAttribute('opacity') === '1'; }); diff --git a/ui/shared/chart/ChartWidget.pwstory.tsx b/client/shared/stats/LineChartWidget.pwstory.tsx similarity index 76% rename from ui/shared/chart/ChartWidget.pwstory.tsx rename to client/shared/stats/LineChartWidget.pwstory.tsx index 910e832bf2..484019a213 100644 --- a/ui/shared/chart/ChartWidget.pwstory.tsx +++ b/client/shared/stats/LineChartWidget.pwstory.tsx @@ -5,9 +5,9 @@ import React from 'react'; import type { LineChartWidgetProps } from 'toolkit/components/charts/line'; import { LineChartWidget } from 'toolkit/components/charts/line'; -import { useChartsConfig } from './config'; +import { useChartsConfig } from './line-chart-config'; -const ChartWidgetPwStory = ({ charts, ...props }: LineChartWidgetProps) => { +const LineChartWidgetPwStory = ({ charts, ...props }: LineChartWidgetProps) => { const chartsConfig = useChartsConfig(); const modifiedCharts = React.useMemo(() => { @@ -25,4 +25,4 @@ const ChartWidgetPwStory = ({ charts, ...props }: LineChartWidgetProps) => { return ; }; -export default React.memo(ChartWidgetPwStory); +export default React.memo(LineChartWidgetPwStory); diff --git a/ui/shared/stats/StatsWidget.pw.tsx b/client/shared/stats/StatsWidget.pw.tsx similarity index 100% rename from ui/shared/stats/StatsWidget.pw.tsx rename to client/shared/stats/StatsWidget.pw.tsx diff --git a/ui/shared/stats/StatsWidget.tsx b/client/shared/stats/StatsWidget.tsx similarity index 100% rename from ui/shared/stats/StatsWidget.tsx rename to client/shared/stats/StatsWidget.tsx diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-2.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-3.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_dark-color-mode_base-view-dark-mode-4.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-2.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-2.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-2.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-2.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-3.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-3.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-3.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-3.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-4.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-4.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_base-view-dark-mode-4.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_base-view-dark-mode-4.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_error-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_error-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_error-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_error-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-2.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-2.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_incomplete-day-2.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_incomplete-day-2.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_loading-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_loading-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_loading-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_loading-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-2.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-2.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-2.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-2.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-3.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-3.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_multiple-charts-3.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_multiple-charts-3.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-values-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-values-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-values-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-values-1.png diff --git a/ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-variations-in-big-values-1.png b/client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-variations-in-big-values-1.png similarity index 100% rename from ui/shared/chart/__screenshots__/ChartWidget.pw.tsx_default_small-variations-in-big-values-1.png rename to client/shared/stats/__screenshots__/LineChartWidget.pw.tsx_default_small-variations-in-big-values-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_dark-color-mode_with-positive-diff-dark-mode-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_dark-color-mode_with-positive-diff-dark-mode-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_dark-color-mode_with-positive-diff-dark-mode-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_dark-color-mode_with-positive-diff-dark-mode-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_loading-state-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_loading-state-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_loading-state-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_loading-state-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-long-value-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-long-value-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-long-value-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-long-value-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-negative-diff-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-negative-diff-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-negative-diff-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-negative-diff-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-period-only-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-period-only-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-period-only-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-period-only-1.png diff --git a/ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-positive-diff-dark-mode-1.png b/client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-positive-diff-dark-mode-1.png similarity index 100% rename from ui/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-positive-diff-dark-mode-1.png rename to client/shared/stats/__screenshots__/StatsWidget.pw.tsx_default_with-positive-diff-dark-mode-1.png diff --git a/lib/stats/getStatsLabelFromTitle.ts b/client/shared/stats/get-stats-label-from-title.ts similarity index 100% rename from lib/stats/getStatsLabelFromTitle.ts rename to client/shared/stats/get-stats-label-from-title.ts diff --git a/ui/shared/chart/config.ts b/client/shared/stats/line-chart-config.ts similarity index 100% rename from ui/shared/chart/config.ts rename to client/shared/stats/line-chart-config.ts diff --git a/ui/shared/chart/utils.ts b/client/shared/stats/line-chart-utils.ts similarity index 100% rename from ui/shared/chart/utils.ts rename to client/shared/stats/line-chart-utils.ts diff --git a/client/slices/address/components/entity/AddressEntity.pw.tsx b/client/slices/address/components/entity/AddressEntity.pw.tsx index 4660f93ee7..ca67542a73 100644 --- a/client/slices/address/components/entity/AddressEntity.pw.tsx +++ b/client/slices/address/components/entity/AddressEntity.pw.tsx @@ -6,10 +6,11 @@ import { AddressHighlightProvider } from 'client/slices/address/contexts/address import * as addressMock from 'client/slices/address/mocks/address'; import * as implementationsMock from 'client/slices/address/mocks/implementations'; +import * as metadataMock from 'client/features/address-metadata/mocks/tags'; + import * as cookies from 'client/shared/storage/cookies'; import config from 'configs/app'; -import * as metadataMock from 'mocks/metadata/address'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/address/pages/details/Address.tsx b/client/slices/address/pages/details/Address.tsx index c2b376fff5..c6083ecf45 100644 --- a/client/slices/address/pages/details/Address.tsx +++ b/client/slices/address/pages/details/Address.tsx @@ -46,19 +46,19 @@ import AddressClusters from 'client/features/name-services/clusters/pages/addres import EnsEntity from 'client/features/name-services/domains/components/EnsEntity'; import useCheckDomainNameParam from 'client/features/name-services/domains/hooks/useCheckDomainNameParam'; import AddressEnsDomains from 'client/features/name-services/domains/pages/address/AddressEnsDomains'; +import useIsSafeAddress from 'client/features/safe/hooks/useIsSafeAddress'; import SolidityscanReport from 'client/features/solidity-scan/components/SolidityscanReport'; import AddressAccountHistory from 'client/features/tx-interpretation/noves/pages/address/AddressAccountHistory'; import AddressUserOps from 'client/features/user-ops/pages/address/AddressUserOps'; import { USER_OPS_ACCOUNT } from 'client/features/user-ops/stubs'; import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; +import useFetchXStarScore from 'client/features/x-star-score/hooks/useFetchXStarScore'; import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; import config from 'configs/app'; -import useIsSafeAddress from 'lib/hooks/useIsSafeAddress'; -import useFetchXStarScore from 'lib/xStarScore/useFetchXStarScore'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import TextAd from 'ui/shared/ad/TextAd'; diff --git a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx index 2d3968dffb..69948ef13d 100644 --- a/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx @@ -6,10 +6,10 @@ import React from 'react'; import useApiQuery from 'client/api/hooks/useApiQuery'; import { currencyUnits } from 'client/shared/chain/units'; +import { useChartsConfig } from 'client/shared/stats/line-chart-config'; import config from 'configs/app'; import { LineChartWidget } from 'toolkit/components/charts/line/LineChartWidget'; -import { useChartsConfig } from 'ui/shared/chart/config'; interface Props { addressHash: string; diff --git a/client/slices/address/pages/details/info/AddressAlerts.pw.tsx b/client/slices/address/pages/details/info/AddressAlerts.pw.tsx index ef3176f67c..b488c7e5d2 100644 --- a/client/slices/address/pages/details/info/AddressAlerts.pw.tsx +++ b/client/slices/address/pages/details/info/AddressAlerts.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as metadataMock from 'mocks/metadata/address'; +import * as metadataMock from 'client/features/address-metadata/mocks/tags'; + import { test, expect } from 'playwright/lib'; import AddressAlerts from './AddressAlerts'; diff --git a/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx index 524368c431..a08a6dc427 100644 --- a/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx @@ -9,13 +9,13 @@ import { isConfidentialTokenType, isFungibleTokenType } from 'client/slices/toke import { route } from 'nextjs/routes'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NativeTokenTag from 'client/slices/token/components/NativeTokenTag'; import type { TokenEnhancedData } from 'client/slices/token/pages/address/utils'; import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { Link } from 'toolkit/chakra/link'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; -import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; interface Props { diff --git a/client/slices/block/hooks/useBlockTxsQuery.ts b/client/slices/block/hooks/useBlockTxsQuery.ts index 36ef6b914c..f8f53b82aa 100644 --- a/client/slices/block/hooks/useBlockTxsQuery.ts +++ b/client/slices/block/hooks/useBlockTxsQuery.ts @@ -15,9 +15,9 @@ import { TX } from 'client/slices/tx/stubs/tx'; import { publicClient } from 'client/features/connect-wallet/utils/public-client'; +import dayjs from 'client/shared/date-and-time/dayjs'; import hexToDecimal from 'client/shared/transformers/hex-to-decimal'; -import dayjs from 'lib/date/dayjs'; import { GET_BLOCK_WITH_TRANSACTIONS } from 'stubs/RPC'; import { generateListStub } from 'stubs/utils'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/client/slices/block/pages/countdown-details/BlockCountdown.tsx b/client/slices/block/pages/countdown-details/BlockCountdown.tsx index 12e03cb2c1..8554a66057 100644 --- a/client/slices/block/pages/countdown-details/BlockCountdown.tsx +++ b/client/slices/block/pages/countdown-details/BlockCountdown.tsx @@ -8,12 +8,14 @@ import { route } from 'nextjs/routes'; import useApiQuery from 'client/api/hooks/useApiQuery'; +import CapybaraRunner from 'client/features/easter-eggs/components/runner/CapybaraRunner'; import { useMultichainContext } from 'client/features/multichain/context'; +import dayjs from 'client/shared/date-and-time/dayjs'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'client/shared/router/get-query-param-string'; +import StatsWidget from 'client/shared/stats/StatsWidget'; -import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Image } from 'toolkit/chakra/image'; @@ -21,10 +23,8 @@ import { Link } from 'toolkit/chakra/link'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; import { downloadBlob } from 'toolkit/utils/file'; -import CapybaraRunner from 'ui/games/CapybaraRunner'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import IconSvg from 'ui/shared/IconSvg'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; import Time from 'ui/shared/time/Time'; import BlockCountdownTimer from './BlockCountdownTimer'; diff --git a/client/slices/block/pages/countdown-details/create-google-calendar-link.ts b/client/slices/block/pages/countdown-details/create-google-calendar-link.ts index 693f84617a..884c066d53 100644 --- a/client/slices/block/pages/countdown-details/create-google-calendar-link.ts +++ b/client/slices/block/pages/countdown-details/create-google-calendar-link.ts @@ -4,8 +4,9 @@ import { route } from 'nextjs/routes'; import type { TMultichainContext } from 'client/features/multichain/context'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; interface Params { timeFromNow: number; diff --git a/client/slices/block/pages/countdown-details/create-ics-file-blob.ts b/client/slices/block/pages/countdown-details/create-ics-file-blob.ts index b0f9474ef7..225c72a7a5 100644 --- a/client/slices/block/pages/countdown-details/create-ics-file-blob.ts +++ b/client/slices/block/pages/countdown-details/create-ics-file-blob.ts @@ -4,8 +4,9 @@ import { route } from 'nextjs/routes'; import type { TMultichainContext } from 'client/features/multichain/context'; +import type dayjs from 'client/shared/date-and-time/dayjs'; + import config from 'configs/app'; -import type dayjs from 'lib/date/dayjs'; interface Params { date: dayjs.Dayjs; diff --git a/client/slices/block/types/api.ts b/client/slices/block/types/api.ts index 69395a194b..400d282cb2 100644 --- a/client/slices/block/types/api.ts +++ b/client/slices/block/types/api.ts @@ -10,10 +10,14 @@ import type { BlockZkSync } from 'client/features/rollup/zk-sync/types/api'; import type { AddressParam } from 'client/slices/address/types/api'; import type { InternalTransaction } from 'client/slices/internal-tx/types/api'; import type { Transaction } from 'client/slices/tx/types/api'; -import type { Reward } from 'types/api/reward'; export type BlockType = 'block' | 'reorg' | 'uncle'; +export interface BlockReward { + reward: string; + type: 'Miner Reward' | 'Validator Reward' | 'Emission Reward' | 'Chore Reward' | 'Uncle Reward' | 'POA Mania Reward'; +} + export interface Block extends BlockArbitrum, BlockOptimism, BlockZkSync, BlockCelo, BlockZilliqa, BlockRootstock, BlockDataAvailability { height: number; timestamp: string; @@ -33,7 +37,7 @@ export interface Block extends BlockArbitrum, BlockOptimism, BlockZkSync, BlockC priority_fee: string | null; extra_data: string | null; state_root: string | null; - rewards?: Array; + rewards?: Array; gas_target_percentage: number | null; gas_used_percentage: number | null; burnt_fees_percentage: number | null; diff --git a/types/views/block.ts b/client/slices/block/types/config.ts similarity index 100% rename from types/views/block.ts rename to client/slices/block/types/config.ts diff --git a/client/slices/block/utils/format-rpc-data.ts b/client/slices/block/utils/format-rpc-data.ts index b522674d56..7981a42513 100644 --- a/client/slices/block/utils/format-rpc-data.ts +++ b/client/slices/block/utils/format-rpc-data.ts @@ -6,7 +6,7 @@ import type { Block } from 'client/slices/block/types/api'; import { unknownAddress } from 'client/slices/address/utils/consts'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; export default function formatRpcData(block: GetBlockReturnType | null): Block | null { if (!block) { diff --git a/ui/snippets/navigation/TestnetBadge.tsx b/client/slices/chain/TestnetBadge.tsx similarity index 100% rename from ui/snippets/navigation/TestnetBadge.tsx rename to client/slices/chain/TestnetBadge.tsx diff --git a/ui/snippets/header/alerts/IndexingBlocksAlert.tsx b/client/slices/chain/indexing-status/IndexingStatusBlocks.tsx similarity index 94% rename from ui/snippets/header/alerts/IndexingBlocksAlert.tsx rename to client/slices/chain/indexing-status/IndexingStatusBlocks.tsx index 0e6e3fecd3..4007c33c33 100644 --- a/ui/snippets/header/alerts/IndexingBlocksAlert.tsx +++ b/client/slices/chain/indexing-status/IndexingStatusBlocks.tsx @@ -3,8 +3,8 @@ import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; +import type { IndexingStatus } from './types'; import type { SocketMessage } from 'client/api/socket/types'; -import type { IndexingStatus } from 'types/api/indexingStatus'; import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; import useSocketChannel from 'client/api/socket/useSocketChannel'; @@ -18,7 +18,7 @@ import { Alert } from 'toolkit/chakra/alert'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { nbsp, ndash } from 'toolkit/utils/htmlEntities'; -const IndexingBlocksAlert = () => { +const IndexingStatusBlocks = () => { const appProps = useAppContext(); const cookiesString = appProps.cookies; const [ hasAlertCookie ] = React.useState(cookies.get(cookies.NAMES.INDEXING_ALERT, cookiesString) === 'true'); @@ -83,4 +83,4 @@ const IndexingBlocksAlert = () => { ); }; -export default React.memo(IndexingBlocksAlert); +export default React.memo(IndexingStatusBlocks); diff --git a/ui/snippets/footer/IntTxsIndexingStatus.tsx b/client/slices/chain/indexing-status/IndexingStatusInternalTxs.tsx similarity index 95% rename from ui/snippets/footer/IntTxsIndexingStatus.tsx rename to client/slices/chain/indexing-status/IndexingStatusInternalTxs.tsx index ea8a79cd78..2cd672a3ed 100644 --- a/ui/snippets/footer/IntTxsIndexingStatus.tsx +++ b/client/slices/chain/indexing-status/IndexingStatusInternalTxs.tsx @@ -4,8 +4,8 @@ import { Flex, Text } from '@chakra-ui/react'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; +import type { IndexingStatus } from './types'; import type { SocketMessage } from 'client/api/socket/types'; -import type { IndexingStatus } from 'types/api/indexingStatus'; import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; import useSocketChannel from 'client/api/socket/useSocketChannel'; @@ -16,7 +16,7 @@ import { Tooltip } from 'toolkit/chakra/tooltip'; import { apos, nbsp, ndash } from 'toolkit/utils/htmlEntities'; import IconSvg from 'ui/shared/IconSvg'; -const IntTxsIndexingStatus = () => { +const IndexingStatusInternalTxs = () => { const { data, isError, isPending } = useApiQuery('general:homepage_indexing_status', { queryOptions: { @@ -90,4 +90,4 @@ const IntTxsIndexingStatus = () => { ); }; -export default IntTxsIndexingStatus; +export default IndexingStatusInternalTxs; diff --git a/types/api/indexingStatus.ts b/client/slices/chain/indexing-status/types.ts similarity index 85% rename from types/api/indexingStatus.ts rename to client/slices/chain/indexing-status/types.ts index e88b20ce8a..c7c6efd74f 100644 --- a/types/api/indexingStatus.ts +++ b/client/slices/chain/indexing-status/types.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -export type IndexingStatus = { +export interface IndexingStatus { finished_indexing: boolean; finished_indexing_blocks: boolean; indexed_blocks_ratio: string; diff --git a/ui/snippets/networkLogo/NetworkIcon.pw.tsx b/client/slices/chain/logo/NetworkIcon.pw.tsx similarity index 100% rename from ui/snippets/networkLogo/NetworkIcon.pw.tsx rename to client/slices/chain/logo/NetworkIcon.pw.tsx diff --git a/ui/snippets/networkLogo/NetworkIcon.tsx b/client/slices/chain/logo/NetworkIcon.tsx similarity index 100% rename from ui/snippets/networkLogo/NetworkIcon.tsx rename to client/slices/chain/logo/NetworkIcon.tsx diff --git a/ui/snippets/networkLogo/NetworkLogo.pw.tsx b/client/slices/chain/logo/NetworkLogo.pw.tsx similarity index 100% rename from ui/snippets/networkLogo/NetworkLogo.pw.tsx rename to client/slices/chain/logo/NetworkLogo.pw.tsx diff --git a/ui/snippets/networkLogo/NetworkLogo.tsx b/client/slices/chain/logo/NetworkLogo.tsx similarity index 100% rename from ui/snippets/networkLogo/NetworkLogo.tsx rename to client/slices/chain/logo/NetworkLogo.tsx diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-with-dark-option---default-dark-mode-base-view-1.png b/client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-with-dark-option---default-dark-mode-base-view-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-with-dark-option---default-dark-mode-base-view-1.png rename to client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_custom-icon-with-dark-option---default-dark-mode-base-view-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_placeholder-icon-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_placeholder-icon-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_placeholder-icon-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_dark-color-mode_placeholder-icon-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_default_custom-icon-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_default_custom-icon-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_default_custom-icon-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_default_custom-icon-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_default_placeholder-icon-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_default_placeholder-icon-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkIcon.pw.tsx_default_placeholder-icon-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkIcon.pw.tsx_default_placeholder-icon-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-with-dark-option---default-dark-mode-base-view-1.png b/client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-with-dark-option---default-dark-mode-base-view-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-with-dark-option---default-dark-mode-base-view-1.png rename to client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_custom-logo-with-dark-option---default-dark-mode-base-view-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_placeholder-logo-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_placeholder-logo-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_placeholder-logo-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_dark-color-mode_placeholder-logo-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_default_custom-logo-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_default_custom-logo-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_default_custom-logo-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_default_custom-logo-dark-mode-1.png diff --git a/ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_default_placeholder-logo-dark-mode-1.png b/client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_default_placeholder-logo-dark-mode-1.png similarity index 100% rename from ui/snippets/networkLogo/__screenshots__/NetworkLogo.pw.tsx_default_placeholder-logo-dark-mode-1.png rename to client/slices/chain/logo/__screenshots__/NetworkLogo.pw.tsx_default_placeholder-logo-dark-mode-1.png diff --git a/ui/snippets/networkLogo/consts.ts b/client/slices/chain/logo/consts.ts similarity index 100% rename from ui/snippets/networkLogo/consts.ts rename to client/slices/chain/logo/consts.ts diff --git a/client/slices/contract/pages/index/VerifiedContractsCounters.tsx b/client/slices/contract/pages/index/VerifiedContractsCounters.tsx index 1780943446..2870ac0497 100644 --- a/client/slices/contract/pages/index/VerifiedContractsCounters.tsx +++ b/client/slices/contract/pages/index/VerifiedContractsCounters.tsx @@ -9,8 +9,9 @@ import { VERIFIED_CONTRACTS_COUNTERS, VERIFIED_CONTRACTS_COUNTERS_MICROSERVICE } import { useMultichainContext } from 'client/features/multichain/context'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; const VerifiedContractsCounters = () => { const multichainContext = useMultichainContext(); diff --git a/client/slices/gas/components/GasInfoUpdateTimer.tsx b/client/slices/gas/components/GasInfoUpdateTimer.tsx index 0bda4a43df..fb9d6dd490 100644 --- a/client/slices/gas/components/GasInfoUpdateTimer.tsx +++ b/client/slices/gas/components/GasInfoUpdateTimer.tsx @@ -2,7 +2,8 @@ import React from 'react'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import type { ProgressCircleRootProps } from 'toolkit/chakra/progress-circle'; import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle'; diff --git a/mocks/stats/index.ts b/client/slices/home/mocks/indexing-status.ts similarity index 100% rename from mocks/stats/index.ts rename to client/slices/home/mocks/indexing-status.ts diff --git a/client/slices/home/pages/index/charts/ChainIndicatorChartContent.tsx b/client/slices/home/pages/index/charts/ChainIndicatorChartContent.tsx index 86ea64b37d..0a14d72cee 100644 --- a/client/slices/home/pages/index/charts/ChainIndicatorChartContent.tsx +++ b/client/slices/home/pages/index/charts/ChainIndicatorChartContent.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { LineChartData } from 'toolkit/components/charts/line/types'; +import { useDefaultGradient, useDefaultLineColor } from 'client/shared/stats/line-chart-config'; + import { LineChartArea, LineChartLine, LineChartOverlay, LineChartTooltip, useLineChartController } from 'toolkit/components/charts/line'; -import { useDefaultGradient, useDefaultLineColor } from 'ui/shared/chart/config'; interface Props { data: LineChartData; diff --git a/client/slices/home/pages/index/stats/LatestBatchStatsWidget.tsx b/client/slices/home/pages/index/stats/LatestBatchStatsWidget.tsx index cd01677157..dbd81aef41 100644 --- a/client/slices/home/pages/index/stats/LatestBatchStatsWidget.tsx +++ b/client/slices/home/pages/index/stats/LatestBatchStatsWidget.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { useHomeDataContext } from 'client/slices/home/contexts/home-data-context'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; type Props = { className?: string; diff --git a/client/slices/home/pages/index/stats/LatestBlockStatsWidget.tsx b/client/slices/home/pages/index/stats/LatestBlockStatsWidget.tsx index 995667480d..8cbf6b59c8 100644 --- a/client/slices/home/pages/index/stats/LatestBlockStatsWidget.tsx +++ b/client/slices/home/pages/index/stats/LatestBlockStatsWidget.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { useHomeDataContext } from 'client/slices/home/contexts/home-data-context'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; +import StatsWidget from 'client/shared/stats/StatsWidget'; type Props = { className?: string; diff --git a/client/slices/home/pages/index/stats/Stats.tsx b/client/slices/home/pages/index/stats/Stats.tsx index 0b11fbdffb..21937b6dbf 100644 --- a/client/slices/home/pages/index/stats/Stats.tsx +++ b/client/slices/home/pages/index/stats/Stats.tsx @@ -16,9 +16,10 @@ import { homeStatsWidgetCommonStyles, isHomeStatsItemEnabled, sortHomeStatsItems import { HOMEPAGE_STATS_MICROSERVICE } from 'client/features/chain-stats/stubs/home'; import { layerLabels } from 'client/features/rollup/common/utils/layer'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; import IconSvg from 'ui/shared/IconSvg'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; import { WEI } from 'ui/shared/value/utils'; import LatestBatchStatsWidget from './LatestBatchStatsWidget'; diff --git a/client/slices/home/pages/index/stats/StatsDegraded.tsx b/client/slices/home/pages/index/stats/StatsDegraded.tsx index 579f5aa2e6..a567cd4228 100644 --- a/client/slices/home/pages/index/stats/StatsDegraded.tsx +++ b/client/slices/home/pages/index/stats/StatsDegraded.tsx @@ -12,10 +12,11 @@ import { homeStatsWidgetCommonStyles, isHomeStatsItemEnabled, sortHomeStatsItems import { publicClient } from 'client/features/connect-wallet/utils/public-client'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import { mdash } from 'toolkit/utils/htmlEntities'; import FallbackRpcIcon from 'ui/shared/fallbacks/FallbackRpcIcon'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; import { GWEI } from 'ui/shared/value/utils'; const StatsDegraded = () => { diff --git a/client/slices/home/types/api.ts b/client/slices/home/types/api.ts index 6cec82dacc..2c2cb79813 100644 --- a/client/slices/home/types/api.ts +++ b/client/slices/home/types/api.ts @@ -2,7 +2,7 @@ import type { GasPrices } from 'client/slices/gas/types/api'; -export type HomeStats = { +export interface HomeStats { total_blocks: string; total_addresses: string; total_transactions: string; @@ -28,3 +28,29 @@ export type HomeStats = { epoch_number: number; }; }; + +export interface ChartTransactionItem { + date: string; + transactions_count: number | null; +} + +export interface ChartMarketItem { + date: string; + closing_price: string | null; + market_cap?: string | null; + tvl?: string | null; +} + +export interface ChartTransactionResponse { + chart_data: Array; +} + +export interface ChartMarketResponse { + available_supply: string; + chart_data: Array; +} + +export interface ChartSecondaryCoinPriceResponse { + available_supply: string; + chart_data: Array; +} diff --git a/client/slices/home/types/client.ts b/client/slices/home/types/client.ts index e4d1ee5b9a..315f19252b 100644 --- a/client/slices/home/types/client.ts +++ b/client/slices/home/types/client.ts @@ -5,7 +5,7 @@ import type { ReactElement } from 'react'; import type { ChainIndicatorId, HomeStatsWidgetId } from 'client/slices/home/types/config'; -import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget'; +import type { Props as StatsWidgetProps } from 'client/shared/stats/StatsWidget'; export interface HighlightsBannerConfig { title: string; diff --git a/client/slices/home/utils/chart.ts b/client/slices/home/utils/chart.ts index 1890776120..afe15bf404 100644 --- a/client/slices/home/utils/chart.ts +++ b/client/slices/home/utils/chart.ts @@ -2,9 +2,10 @@ import type { ChainIndicatorId } from 'client/slices/home/types/config'; +import { sortByDateAsc } from 'client/shared/stats/line-chart-utils'; + import config from 'configs/app'; import type { LineChartData, LineChartDataItem, LineChartItemRaw, LineChartItem } from 'toolkit/components/charts/line'; -import { sortByDateAsc } from 'ui/shared/chart/utils'; const CHART_ITEMS: Record> = { daily_txs: { diff --git a/client/slices/home/utils/stats.ts b/client/slices/home/utils/stats.ts index 9c649d809b..a51c6f231b 100644 --- a/client/slices/home/utils/stats.ts +++ b/client/slices/home/utils/stats.ts @@ -4,8 +4,9 @@ import type { ReactElement } from 'react'; import type { HomeStatsWidgetId } from 'client/slices/home/types/config'; +import type { Props as StatsWidgetProps } from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget'; export type HomeStatsComponentItem = { id: HomeStatsWidgetId; component: ReactElement }; export type HomeStatsWidgetItem = StatsWidgetProps & { id: HomeStatsWidgetId; component?: undefined }; diff --git a/client/slices/internal-tx/pages/tx/TxInternals.tsx b/client/slices/internal-tx/pages/tx/TxInternals.tsx index f090e216a6..1453c53d70 100644 --- a/client/slices/internal-tx/pages/tx/TxInternals.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternals.tsx @@ -10,7 +10,8 @@ import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; -import compareBns from 'lib/bigint/compareBns'; +import compareBns from 'client/shared/numbers/compareBns'; + // import { apos } from 'toolkit/utils/htmlEntities'; import { generateListStub } from 'stubs/utils'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; diff --git a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx index a0f4a986ef..2da5a28c4d 100644 --- a/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx +++ b/client/slices/search/components/search-bar/SearchBarSuggest/SearchBarSuggestAddress.tsx @@ -14,9 +14,9 @@ import SearchResultEntityTag from 'client/slices/search/pages/search-results/Sea import * as contract from 'client/features/multichain/utils/contract'; +import dayjs from 'client/shared/date-and-time/dayjs'; import highlightText from 'client/shared/text/highlight-text'; -import dayjs from 'lib/date/dayjs'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; diff --git a/client/slices/search/pages/search-results/SearchResultListItem.tsx b/client/slices/search/pages/search-results/SearchResultListItem.tsx index 872dc6afef..68f548f4f2 100644 --- a/client/slices/search/pages/search-results/SearchResultListItem.tsx +++ b/client/slices/search/pages/search-results/SearchResultListItem.tsx @@ -26,9 +26,9 @@ import * as EnsEntity from 'client/features/name-services/domains/components/Ens import * as UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import * as mixpanel from 'client/shared/analytics/mixpanel'; +import dayjs from 'client/shared/date-and-time/dayjs'; import highlightText from 'client/shared/text/highlight-text'; -import dayjs from 'lib/date/dayjs'; import { useColorMode } from 'toolkit/chakra/color-mode'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; diff --git a/client/slices/search/pages/search-results/SearchResultTableItem.tsx b/client/slices/search/pages/search-results/SearchResultTableItem.tsx index a64dcc552f..a635787bb6 100644 --- a/client/slices/search/pages/search-results/SearchResultTableItem.tsx +++ b/client/slices/search/pages/search-results/SearchResultTableItem.tsx @@ -26,9 +26,9 @@ import * as EnsEntity from 'client/features/name-services/domains/components/Ens import * as UserOpEntity from 'client/features/user-ops/components/entity/UserOpEntity'; import * as mixpanel from 'client/shared/analytics/mixpanel'; +import dayjs from 'client/shared/date-and-time/dayjs'; import highlightText from 'client/shared/text/highlight-text'; -import dayjs from 'lib/date/dayjs'; import { useColorMode } from 'toolkit/chakra/color-mode'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; diff --git a/client/slices/token-transfer/components/list/TokenTransferListItem.tsx b/client/slices/token-transfer/components/list/TokenTransferListItem.tsx index 79c664ac83..3b4904298b 100644 --- a/client/slices/token-transfer/components/list/TokenTransferListItem.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferListItem.tsx @@ -8,13 +8,13 @@ import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/utils/token-types'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import TxAdditionalInfo from 'client/slices/tx/components/TxAdditionalInfo'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx b/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx index c888cf10bc..5bbf861135 100644 --- a/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx +++ b/client/slices/token-transfer/components/list/TokenTransferTableItem.tsx @@ -8,6 +8,7 @@ import type { TokenTransfer } from 'client/slices/token-transfer/types/api'; import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/utils/token-types'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import TxAdditionalInfo from 'client/slices/tx/components/TxAdditionalInfo'; @@ -15,7 +16,6 @@ import TxAdditionalInfo from 'client/slices/tx/components/TxAdditionalInfo'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/client/slices/token-transfer/components/snippet/TokenTransferSnippetNft.tsx b/client/slices/token-transfer/components/snippet/TokenTransferSnippetNft.tsx index 7b8df96f46..274ab57e12 100644 --- a/client/slices/token-transfer/components/snippet/TokenTransferSnippetNft.tsx +++ b/client/slices/token-transfer/components/snippet/TokenTransferSnippetNft.tsx @@ -5,10 +5,9 @@ import React from 'react'; import type { TokenInfo, TokenInstance } from 'client/slices/token/types/api'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; - interface Props { token: TokenInfo; value: string; diff --git a/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx b/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx index 2d7e7d6433..c7b18fbfdc 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfersListItem.tsx @@ -8,10 +8,10 @@ import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } fr import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import { Badge } from 'toolkit/chakra/badge'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import ConfidentialTokenValue from 'ui/shared/value/ConfidentialTokenValue'; diff --git a/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx b/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx index 4c298e0e1b..a560bb80ed 100644 --- a/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx +++ b/client/slices/token-transfer/pages/index/TokenTransfersTableItem.tsx @@ -8,12 +8,12 @@ import { isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import ConfidentialTokenValue from 'ui/shared/value/ConfidentialTokenValue'; diff --git a/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx b/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx index 2827eb112b..bbf3beaf00 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferListItem.tsx @@ -9,12 +9,12 @@ import type { TokenInstance } from 'client/slices/token/types/api'; import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx b/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx index bdf33eabda..a805ae6a36 100644 --- a/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx +++ b/client/slices/token-transfer/pages/token/TokenTransferTableItem.tsx @@ -9,12 +9,12 @@ import type { TokenInstance } from 'client/slices/token/types/api'; import { hasTokenTransferValue, isConfidentialTokenType, NFT_TOKEN_TYPE_IDS } from 'client/slices/token/utils/token-types'; import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/ui/shared/celo/NativeTokenTag.tsx b/client/slices/token/components/NativeTokenTag.tsx similarity index 100% rename from ui/shared/celo/NativeTokenTag.tsx rename to client/slices/token/components/NativeTokenTag.tsx diff --git a/ui/shared/entities/nft/NftEntity.pw.tsx b/client/slices/token/components/entity/NftEntity.pw.tsx similarity index 100% rename from ui/shared/entities/nft/NftEntity.pw.tsx rename to client/slices/token/components/entity/NftEntity.pw.tsx diff --git a/ui/shared/entities/nft/NftEntity.tsx b/client/slices/token/components/entity/NftEntity.tsx similarity index 95% rename from ui/shared/entities/nft/NftEntity.tsx rename to client/slices/token/components/entity/NftEntity.tsx index acce30c91a..1b0dd009e1 100644 --- a/ui/shared/entities/nft/NftEntity.tsx +++ b/client/slices/token/components/entity/NftEntity.tsx @@ -10,9 +10,9 @@ import { route } from 'nextjs/routes'; import { useMultichainContext } from 'client/features/multichain/context'; import * as EntityBase from 'ui/shared/entities/base/components'; -import NftMedia from 'ui/shared/nft/NftMedia'; +import { distributeEntityProps, getIconProps } from 'ui/shared/entities/base/utils'; -import { distributeEntityProps, getIconProps } from '../base/utils'; +import NftMedia from '../nft-media/NftMedia'; const Container = EntityBase.Container; diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_customization-1.png b/client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_customization-1.png similarity index 100% rename from ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_customization-1.png rename to client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_customization-1.png diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_loading-1.png b/client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_loading-1.png similarity index 100% rename from ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_loading-1.png rename to client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_loading-1.png diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png b/client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png similarity index 100% rename from ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png rename to client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_variants-content-1.png b/client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_variants-content-1.png similarity index 100% rename from ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_variants-content-1.png rename to client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_variants-content-1.png diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_variants-subheading-1.png b/client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_variants-subheading-1.png similarity index 100% rename from ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_variants-subheading-1.png rename to client/slices/token/components/entity/__screenshots__/NftEntity.pw.tsx_default_variants-subheading-1.png diff --git a/ui/shared/nft/NftFallback.tsx b/client/slices/token/components/nft-media/NftFallback.tsx similarity index 100% rename from ui/shared/nft/NftFallback.tsx rename to client/slices/token/components/nft-media/NftFallback.tsx diff --git a/ui/shared/nft/NftHtml.tsx b/client/slices/token/components/nft-media/NftHtml.tsx similarity index 100% rename from ui/shared/nft/NftHtml.tsx rename to client/slices/token/components/nft-media/NftHtml.tsx diff --git a/ui/shared/nft/NftImage.tsx b/client/slices/token/components/nft-media/NftImage.tsx similarity index 100% rename from ui/shared/nft/NftImage.tsx rename to client/slices/token/components/nft-media/NftImage.tsx diff --git a/ui/shared/nft/NftMedia.pw.tsx b/client/slices/token/components/nft-media/NftMedia.pw.tsx similarity index 100% rename from ui/shared/nft/NftMedia.pw.tsx rename to client/slices/token/components/nft-media/NftMedia.pw.tsx diff --git a/ui/shared/nft/NftMedia.tsx b/client/slices/token/components/nft-media/NftMedia.tsx similarity index 100% rename from ui/shared/nft/NftMedia.tsx rename to client/slices/token/components/nft-media/NftMedia.tsx diff --git a/ui/shared/nft/NftMediaFullscreenModal.tsx b/client/slices/token/components/nft-media/NftMediaFullscreenModal.tsx similarity index 100% rename from ui/shared/nft/NftMediaFullscreenModal.tsx rename to client/slices/token/components/nft-media/NftMediaFullscreenModal.tsx diff --git a/ui/shared/nft/NftVideo.tsx b/client/slices/token/components/nft-media/NftVideo.tsx similarity index 100% rename from ui/shared/nft/NftVideo.tsx rename to client/slices/token/components/nft-media/NftVideo.tsx diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-with-thumbnails-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-with-thumbnails-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-with-thumbnails-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_image-preview-with-thumbnails-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png b/client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png rename to client/slices/token/components/nft-media/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png diff --git a/ui/shared/nft/useLoadImageViaIpfs.tsx b/client/slices/token/components/nft-media/useLoadImageViaIpfs.tsx similarity index 100% rename from ui/shared/nft/useLoadImageViaIpfs.tsx rename to client/slices/token/components/nft-media/useLoadImageViaIpfs.tsx diff --git a/ui/shared/nft/useNftMediaInfo.tsx b/client/slices/token/components/nft-media/useNftMediaInfo.tsx similarity index 100% rename from ui/shared/nft/useNftMediaInfo.tsx rename to client/slices/token/components/nft-media/useNftMediaInfo.tsx diff --git a/ui/shared/nft/utils.ts b/client/slices/token/components/nft-media/utils.ts similarity index 100% rename from ui/shared/nft/utils.ts rename to client/slices/token/components/nft-media/utils.ts diff --git a/client/slices/token/pages/address/AddressCollections.tsx b/client/slices/token/pages/address/AddressCollections.tsx index 562d5f7649..7dbf2030a0 100644 --- a/client/slices/token/pages/address/AddressCollections.tsx +++ b/client/slices/token/pages/address/AddressCollections.tsx @@ -8,6 +8,7 @@ import type { NFTTokenType } from 'client/slices/token/types/api'; import { route } from 'nextjs/routes'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NftFallback from 'client/slices/token/components/nft-media/NftFallback'; import { useMultichainContext } from 'client/features/multichain/context'; @@ -17,7 +18,6 @@ import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import NftFallback from 'ui/shared/nft/NftFallback'; import Pagination from 'ui/shared/pagination/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; diff --git a/client/slices/token/pages/address/ERC20TokensListItem.tsx b/client/slices/token/pages/address/ERC20TokensListItem.tsx index 1cd776b4c1..ac65d0115e 100644 --- a/client/slices/token/pages/address/ERC20TokensListItem.tsx +++ b/client/slices/token/pages/address/ERC20TokensListItem.tsx @@ -9,6 +9,7 @@ import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/u import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NativeTokenTag from 'client/slices/token/components/NativeTokenTag'; import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; @@ -16,7 +17,6 @@ import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; -import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; diff --git a/client/slices/token/pages/address/ERC20TokensTableItem.tsx b/client/slices/token/pages/address/ERC20TokensTableItem.tsx index ddfe9e4744..7da5fe3c24 100644 --- a/client/slices/token/pages/address/ERC20TokensTableItem.tsx +++ b/client/slices/token/pages/address/ERC20TokensTableItem.tsx @@ -9,6 +9,7 @@ import { getTokenTypeName, isConfidentialTokenType } from 'client/slices/token/u import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NativeTokenTag from 'client/slices/token/components/NativeTokenTag'; import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; @@ -16,7 +17,6 @@ import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; -import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; import SimpleValue from 'ui/shared/value/SimpleValue'; diff --git a/client/slices/token/pages/address/NFTItem.tsx b/client/slices/token/pages/address/NFTItem.tsx index e444d1580f..5044db1e0e 100644 --- a/client/slices/token/pages/address/NFTItem.tsx +++ b/client/slices/token/pages/address/NFTItem.tsx @@ -9,13 +9,13 @@ import { getTokenTypeName } from 'client/slices/token/utils/token-types'; import { route } from 'nextjs/routes'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; +import NftMedia from 'client/slices/token/components/nft-media/NftMedia'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; -import NftMedia from 'ui/shared/nft/NftMedia'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import NFTItemContainer from './NFTItemContainer'; diff --git a/client/slices/token/pages/address/utils.ts b/client/slices/token/pages/address/utils.ts index 42acac20b4..6241b580f6 100644 --- a/client/slices/token/pages/address/utils.ts +++ b/client/slices/token/pages/address/utils.ts @@ -5,8 +5,9 @@ import BigNumber from 'bignumber.js'; import type { AddressTokenBalance } from 'client/slices/address/types/api'; import { isFungibleTokenType } from 'client/slices/token/utils/token-types'; +import sumBnReducer from 'client/shared/numbers/sumBnReducer'; + import config from 'configs/app'; -import sumBnReducer from 'lib/bigint/sumBnReducer'; import { ZERO } from 'toolkit/utils/consts'; const isNativeToken = (token: TokenEnhancedData) => diff --git a/client/slices/token/pages/details/info/TokenDetails.tsx b/client/slices/token/pages/details/info/TokenDetails.tsx index f1ad93315e..5e2fff6ff4 100644 --- a/client/slices/token/pages/details/info/TokenDetails.tsx +++ b/client/slices/token/pages/details/info/TokenDetails.tsx @@ -17,6 +17,8 @@ import AddressEntity from 'client/slices/address/components/entity/AddressEntity import type { TokenTabs } from 'client/slices/token/pages/details/Token'; import { TOKEN_COUNTERS } from 'client/slices/token/stubs'; +import AppActionButton from 'client/features/address-metadata/components/AppActionButton'; +import useAppActionData from 'client/features/address-metadata/hooks/useAppActionData'; import { useMultichainContext } from 'client/features/multichain/context'; import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; @@ -25,8 +27,6 @@ import useIsMounted from 'client/shared/hooks/useIsMounted'; import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; -import useAppActionData from 'ui/shared/AppActionButton/useAppActionData'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx b/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx index 890c713466..04142c251c 100644 --- a/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx +++ b/client/slices/token/pages/details/info/TokenNftMarketplaces.tsx @@ -5,12 +5,13 @@ import React from 'react'; import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/client'; +import AppActionButton from 'client/features/address-metadata/components/AppActionButton'; + import config from 'configs/app'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx b/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx index 47cc6fc43f..aa49293d37 100644 --- a/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx +++ b/client/slices/token/pages/details/inventory/TokenInventoryItem.tsx @@ -8,6 +8,7 @@ import type { TokenInfo, TokenInstance } from 'client/slices/token/types/api'; import { route } from 'nextjs/routes'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import NftMedia from 'client/slices/token/components/nft-media/NftMedia'; import { useMultichainContext } from 'client/features/multichain/context'; @@ -16,7 +17,6 @@ import useIsMobile from 'client/shared/hooks/useIsMobile'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TruncatedTextTooltip } from 'toolkit/components/truncation/TruncatedTextTooltip'; -import NftMedia from 'ui/shared/nft/NftMedia'; type Props = { item: TokenInstance; token: TokenInfo; isLoading: boolean }; diff --git a/client/slices/token/pages/instance/TokenInstanceMetadataFetcher.tsx b/client/slices/token/pages/instance/TokenInstanceMetadataFetcher.tsx index d3946a8c43..36ff9ca82c 100644 --- a/client/slices/token/pages/instance/TokenInstanceMetadataFetcher.tsx +++ b/client/slices/token/pages/instance/TokenInstanceMetadataFetcher.tsx @@ -13,14 +13,14 @@ import useSocketChannel from 'client/api/socket/useSocketChannel'; import useSocketMessage from 'client/api/socket/useSocketMessage'; import getErrorMessage from 'client/shared/errors/get-error-message'; +import ReCaptcha from 'client/shared/re-captcha/ReCaptcha'; +import useReCaptcha from 'client/shared/re-captcha/useReCaptcha'; import config from 'configs/app'; import { Alert } from 'toolkit/chakra/alert'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import { toaster } from 'toolkit/chakra/toaster'; import { MINUTE, SECOND } from 'toolkit/utils/consts'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import { useMetadataUpdateContext } from './metadata-update-context'; diff --git a/client/slices/token/pages/instance/info/TokenInstanceDetails.pw.tsx b/client/slices/token/pages/instance/info/TokenInstanceDetails.pw.tsx index a7604e00db..3dd859a6e2 100644 --- a/client/slices/token/pages/instance/info/TokenInstanceDetails.pw.tsx +++ b/client/slices/token/pages/instance/info/TokenInstanceDetails.pw.tsx @@ -7,8 +7,9 @@ import { tokenInfoERC721a } from 'client/slices/token/mocks/info'; import * as tokenInstanceMock from 'client/slices/token/mocks/instance'; import { MetadataUpdateProvider } from 'client/slices/token/pages/instance/metadata-update-context'; +import { protocolTagWithMeta } from 'client/features/address-metadata/mocks/tags'; + import config from 'configs/app'; -import { protocolTagWithMeta } from 'mocks/metadata/address'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect, devices } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/client/slices/token/pages/instance/info/TokenInstanceDetails.tsx b/client/slices/token/pages/instance/info/TokenInstanceDetails.tsx index 403d92a3ee..0243e6779a 100644 --- a/client/slices/token/pages/instance/info/TokenInstanceDetails.tsx +++ b/client/slices/token/pages/instance/info/TokenInstanceDetails.tsx @@ -6,19 +6,20 @@ import React from 'react'; import type { TokenInfo, TokenInstance } from 'client/slices/token/types/api'; import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import NftMedia from 'client/slices/token/components/nft-media/NftMedia'; import TokenNftMarketplaces from 'client/slices/token/pages/details/info/TokenNftMarketplaces'; +import AppActionButton from 'client/features/address-metadata/components/AppActionButton'; +import useAppActionData from 'client/features/address-metadata/hooks/useAppActionData'; + import useIsMounted from 'client/shared/hooks/useIsMounted'; import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; -import useAppActionData from 'ui/shared/AppActionButton/useAppActionData'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; -import NftMedia from 'ui/shared/nft/NftMedia'; import TokenInstanceCreatorAddress from './TokenInstanceCreatorAddress'; import TokenInstanceMetadataInfo from './TokenInstanceMetadataInfo'; diff --git a/client/slices/token/types/client.ts b/client/slices/token/types/client.ts index 88939d699c..0e5b911665 100644 --- a/client/slices/token/types/client.ts +++ b/client/slices/token/types/client.ts @@ -16,3 +16,10 @@ export interface AdditionalTokenType { id: string; name: string; } + +export interface NftMarketplaceItem { + name: string; + collection_url?: string; + instance_url?: string; + logo_url: string; +} diff --git a/client/slices/token/utils/metadata/attributes-parser.ts b/client/slices/token/utils/metadata/attributes-parser.ts index a9a4143dc7..d605641a53 100644 --- a/client/slices/token/utils/metadata/attributes-parser.ts +++ b/client/slices/token/utils/metadata/attributes-parser.ts @@ -4,7 +4,8 @@ import { upperFirst } from 'es-toolkit'; import type { Metadata, MetadataAttributes } from 'client/slices/token/types/client'; -import dayjs from 'lib/date/dayjs'; +import dayjs from 'client/shared/date-and-time/dayjs'; + import { SECOND } from 'toolkit/utils/consts'; function formatValue(value: string | number, display: string | undefined, trait: string | undefined): Pick { diff --git a/client/slices/tx/mocks/tx.ts b/client/slices/tx/mocks/tx.ts index b05bc4565c..b566cb2a57 100644 --- a/client/slices/tx/mocks/tx.ts +++ b/client/slices/tx/mocks/tx.ts @@ -9,8 +9,7 @@ import * as decodedInputDataMock from 'client/slices/log/mocks/decoded-input'; import * as tokenTransferMock from 'client/slices/token-transfer/mocks'; import { publicTag, privateTag, watchlistName } from 'client/features/account/mocks/address-tags'; - -import { protocolTag } from 'mocks/metadata/address'; +import { protocolTag } from 'client/features/address-metadata/mocks/tags'; export const base: Transaction = { base_fee_per_gas: '10000000000', diff --git a/client/slices/tx/pages/details/TxSubHeading.pw.tsx b/client/slices/tx/pages/details/TxSubHeading.pw.tsx index 7503ccac7b..9ddadc2dcd 100644 --- a/client/slices/tx/pages/details/TxSubHeading.pw.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.pw.tsx @@ -6,11 +6,11 @@ import * as addressMock from 'client/slices/address/mocks/address'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; import * as txMock from 'client/slices/tx/mocks/tx'; +import { protocolTagWithMeta } from 'client/features/address-metadata/mocks/tags'; import { txInterpretation } from 'client/features/tx-interpretation/blockscout/mocks'; import { transaction as novesTransaction } from 'client/features/tx-interpretation/noves/mocks'; import config from 'configs/app'; -import { protocolTagWithMeta } from 'mocks/metadata/address'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/client/slices/tx/pages/details/TxSubHeading.tsx b/client/slices/tx/pages/details/TxSubHeading.tsx index 6181650483..8a22063985 100644 --- a/client/slices/tx/pages/details/TxSubHeading.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.tsx @@ -10,6 +10,8 @@ import useApiQuery from 'client/api/hooks/useApiQuery'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import AppActionButton from 'client/features/address-metadata/components/AppActionButton'; +import useAppActionData from 'client/features/address-metadata/hooks/useAppActionData'; import { useMultichainContext } from 'client/features/multichain/context'; import { TX_INTERPRETATION } from 'client/features/tx-interpretation/blockscout/stubs'; import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; @@ -19,8 +21,6 @@ import { createNovesSummaryObject } from 'client/features/tx-interpretation/nove import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; -import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; -import useAppActionData from 'ui/shared/AppActionButton/useAppActionData'; import { TX_ACTIONS_BLOCK_ID } from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; import NetworkExplorers from 'ui/shared/NetworkExplorers'; diff --git a/client/slices/tx/pages/details/state/TxStateTokenIdList.tsx b/client/slices/tx/pages/details/state/TxStateTokenIdList.tsx index d47e79ebe4..60f3202448 100644 --- a/client/slices/tx/pages/details/state/TxStateTokenIdList.tsx +++ b/client/slices/tx/pages/details/state/TxStateTokenIdList.tsx @@ -3,8 +3,9 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; + import { CollapsibleList } from 'toolkit/chakra/collapsible'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; interface Props { items: Array<{ total: { token_id: string | null } }>; diff --git a/client/slices/tx/pages/details/state/utils.tsx b/client/slices/tx/pages/details/state/utils.tsx index f4df0427f0..4b7d224e01 100644 --- a/client/slices/tx/pages/details/state/utils.tsx +++ b/client/slices/tx/pages/details/state/utils.tsx @@ -6,6 +6,7 @@ import React from 'react'; import type { TxStateChange } from 'client/slices/tx/types/api'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; @@ -17,7 +18,6 @@ import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { ZERO_ADDRESS } from 'toolkit/utils/consts'; import { nbsp, space } from 'toolkit/utils/htmlEntities'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; import TxStateTokenIdList from './TxStateTokenIdList'; diff --git a/client/slices/tx/pages/index/stats/TxsStats.tsx b/client/slices/tx/pages/index/stats/TxsStats.tsx index 683ed46c81..3ea3663478 100644 --- a/client/slices/tx/pages/index/stats/TxsStats.tsx +++ b/client/slices/tx/pages/index/stats/TxsStats.tsx @@ -11,10 +11,11 @@ import { TXS_STATS, TXS_STATS_MICROSERVICE } from 'client/slices/tx/stubs/tx'; import { useMultichainContext } from 'client/features/multichain/context'; +import getStatsLabelFromTitle from 'client/shared/stats/get-stats-label-from-title'; +import StatsWidget from 'client/shared/stats/StatsWidget'; + import config from 'configs/app'; -import getStatsLabelFromTitle from 'lib/stats/getStatsLabelFromTitle'; import { thinsp } from 'toolkit/utils/htmlEntities'; -import StatsWidget from 'ui/shared/stats/StatsWidget'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; interface Props extends BoxProps {} diff --git a/client/slices/tx/utils/format-rpc-data.ts b/client/slices/tx/utils/format-rpc-data.ts index 0e6ce1eaff..3b53de5464 100644 --- a/client/slices/tx/utils/format-rpc-data.ts +++ b/client/slices/tx/utils/format-rpc-data.ts @@ -6,10 +6,9 @@ import type { Transaction } from 'client/slices/tx/types/api'; import { unknownAddress } from 'client/slices/address/utils/consts'; +import dayjs from 'client/shared/date-and-time/dayjs'; import hexToDecimal from 'client/shared/transformers/hex-to-decimal'; -import dayjs from 'lib/date/dayjs'; - export default function formatRpcData( tx: GetTransactionReturnType, receipt: TransactionReceipt | null, diff --git a/client/slices/tx/utils/sort-txs.ts b/client/slices/tx/utils/sort-txs.ts index c4e2b2b18d..7de129607c 100644 --- a/client/slices/tx/utils/sort-txs.ts +++ b/client/slices/tx/utils/sort-txs.ts @@ -2,7 +2,7 @@ import type { Transaction, TransactionsSortingValue } from 'client/slices/tx/types/api'; -import compareBns from 'lib/bigint/compareBns'; +import compareBns from 'client/shared/numbers/compareBns'; export default function sortTxs(sorting: TransactionsSortingValue | undefined) { return function sortingFn(tx1: Transaction, tx2: Transaction) { diff --git a/ui/pages/Sprite.tsx b/client/sprite/pages/Sprite.tsx similarity index 100% rename from ui/pages/Sprite.tsx rename to client/sprite/pages/Sprite.tsx diff --git a/ui/pages/Chakra.tsx b/client/toolkit/pages/design-system/DesignSystem.tsx similarity index 67% rename from ui/pages/Chakra.tsx rename to client/toolkit/pages/design-system/DesignSystem.tsx index e3995c45f7..b9270063d5 100644 --- a/ui/pages/Chakra.tsx +++ b/client/toolkit/pages/design-system/DesignSystem.tsx @@ -8,41 +8,42 @@ import { useColorMode } from 'toolkit/chakra/color-mode'; import { Switch } from 'toolkit/chakra/switch'; import { TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; import PageTitle from 'ui/shared/Page/PageTitle'; -import AccordionsShowcase from 'ui/showcases/Accordion'; -import AlertShowcase from 'ui/showcases/Alert'; -import BadgeShowcase from 'ui/showcases/Badge'; -import ButtonShowcase from 'ui/showcases/Button'; -import CheckboxShowcase from 'ui/showcases/Checkbox'; -import ClipboardShowcase from 'ui/showcases/Clipboard'; -import CloseButtonShowcase from 'ui/showcases/CloseButton'; -import CollapsibleShowcase from 'ui/showcases/Collapsible'; -import ContentLoaderShowcase from 'ui/showcases/ContentLoader'; -import DialogShowcase from 'ui/showcases/Dialog'; -import EmptyStateShowcase from 'ui/showcases/EmptyState'; -import FieldShowcase from 'ui/showcases/Field'; -import IconButtonShowcase from 'ui/showcases/IconButton'; -import InputShowcase from 'ui/showcases/Input'; -import LinkShowcase from 'ui/showcases/Link'; -import MenuShowcase from 'ui/showcases/Menu'; -import PaginationShowcase from 'ui/showcases/Pagination'; -import PinInputShowcase from 'ui/showcases/PinInput'; -import PopoverShowcase from 'ui/showcases/Popover'; -import ProgressShowcase from 'ui/showcases/Progress'; -import ProgressCircleShowcase from 'ui/showcases/ProgressCircle'; -import RadioShowcase from 'ui/showcases/Radio'; -import RatingShowcase from 'ui/showcases/Rating'; -import SelectShowcase from 'ui/showcases/Select'; -import SkeletonShowcase from 'ui/showcases/Skeleton'; -import SpinnerShowcase from 'ui/showcases/Spinner'; -import StatusShowcase from 'ui/showcases/Status'; -import SwitchShowcase from 'ui/showcases/Switch'; -import TableShowcase from 'ui/showcases/Table'; -import TabsShowcase from 'ui/showcases/Tabs'; -import TagShowcase from 'ui/showcases/Tag'; -import TextareaShowcase from 'ui/showcases/Textarea'; -import ToastShowcase from 'ui/showcases/Toast'; -import TooltipShowcase from 'ui/showcases/Tooltip'; -import ValuesShowcase from 'ui/showcases/Values'; + +import AccordionsShowcase from './tabs/Accordion'; +import AlertShowcase from './tabs/Alert'; +import BadgeShowcase from './tabs/Badge'; +import ButtonShowcase from './tabs/Button'; +import CheckboxShowcase from './tabs/Checkbox'; +import ClipboardShowcase from './tabs/Clipboard'; +import CloseButtonShowcase from './tabs/CloseButton'; +import CollapsibleShowcase from './tabs/Collapsible'; +import ContentLoaderShowcase from './tabs/ContentLoader'; +import DialogShowcase from './tabs/Dialog'; +import EmptyStateShowcase from './tabs/EmptyState'; +import FieldShowcase from './tabs/Field'; +import IconButtonShowcase from './tabs/IconButton'; +import InputShowcase from './tabs/Input'; +import LinkShowcase from './tabs/Link'; +import MenuShowcase from './tabs/Menu'; +import PaginationShowcase from './tabs/Pagination'; +import PinInputShowcase from './tabs/PinInput'; +import PopoverShowcase from './tabs/Popover'; +import ProgressShowcase from './tabs/Progress'; +import ProgressCircleShowcase from './tabs/ProgressCircle'; +import RadioShowcase from './tabs/Radio'; +import RatingShowcase from './tabs/Rating'; +import SelectShowcase from './tabs/Select'; +import SkeletonShowcase from './tabs/Skeleton'; +import SpinnerShowcase from './tabs/Spinner'; +import StatusShowcase from './tabs/Status'; +import SwitchShowcase from './tabs/Switch'; +import TableShowcase from './tabs/Table'; +import TabsShowcase from './tabs/Tabs'; +import TagShowcase from './tabs/Tag'; +import TextareaShowcase from './tabs/Textarea'; +import ToastShowcase from './tabs/Toast'; +import TooltipShowcase from './tabs/Tooltip'; +import ValuesShowcase from './tabs/Values'; const tabs = [ { label: 'Accordion', value: 'accordion', component: }, diff --git a/ui/showcases/parts.tsx b/client/toolkit/pages/design-system/parts.tsx similarity index 100% rename from ui/showcases/parts.tsx rename to client/toolkit/pages/design-system/parts.tsx diff --git a/ui/showcases/Accordion.tsx b/client/toolkit/pages/design-system/tabs/Accordion.tsx similarity index 99% rename from ui/showcases/Accordion.tsx rename to client/toolkit/pages/design-system/tabs/Accordion.tsx index 2b90254a6b..949eafdb09 100644 --- a/ui/showcases/Accordion.tsx +++ b/client/toolkit/pages/design-system/tabs/Accordion.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { AccordionItemContent, AccordionItemTrigger, AccordionItem, AccordionRoot } from 'toolkit/chakra/accordion'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const items = [ { value: 'first-item', title: 'First Item', text: 'Some value 1...' }, diff --git a/ui/showcases/Alert.pw.tsx b/client/toolkit/pages/design-system/tabs/Alert.pw.tsx similarity index 100% rename from ui/showcases/Alert.pw.tsx rename to client/toolkit/pages/design-system/tabs/Alert.pw.tsx diff --git a/ui/showcases/Alert.tsx b/client/toolkit/pages/design-system/tabs/Alert.tsx similarity index 99% rename from ui/showcases/Alert.tsx rename to client/toolkit/pages/design-system/tabs/Alert.tsx index 291847ffd0..206ce7c56b 100644 --- a/ui/showcases/Alert.tsx +++ b/client/toolkit/pages/design-system/tabs/Alert.tsx @@ -10,7 +10,7 @@ import { Link } from 'toolkit/chakra/link'; import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const AlertShowcase = () => { return ( diff --git a/ui/showcases/Badge.pw.tsx b/client/toolkit/pages/design-system/tabs/Badge.pw.tsx similarity index 100% rename from ui/showcases/Badge.pw.tsx rename to client/toolkit/pages/design-system/tabs/Badge.pw.tsx diff --git a/ui/showcases/Badge.tsx b/client/toolkit/pages/design-system/tabs/Badge.tsx similarity index 99% rename from ui/showcases/Badge.tsx rename to client/toolkit/pages/design-system/tabs/Badge.tsx index 4aaa7db0d3..d2b1c0c009 100644 --- a/ui/showcases/Badge.tsx +++ b/client/toolkit/pages/design-system/tabs/Badge.tsx @@ -7,7 +7,7 @@ import { Badge } from 'toolkit/chakra/badge'; import IconSvg from 'ui/shared/IconSvg'; import StatusTag from 'ui/shared/statusTag/StatusTag'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const BadgeShowcase = () => { return ( diff --git a/ui/showcases/Button.pw.tsx b/client/toolkit/pages/design-system/tabs/Button.pw.tsx similarity index 100% rename from ui/showcases/Button.pw.tsx rename to client/toolkit/pages/design-system/tabs/Button.pw.tsx diff --git a/ui/showcases/Button.tsx b/client/toolkit/pages/design-system/tabs/Button.tsx similarity index 99% rename from ui/showcases/Button.tsx rename to client/toolkit/pages/design-system/tabs/Button.tsx index 601bfba0be..7478773c47 100644 --- a/ui/showcases/Button.tsx +++ b/client/toolkit/pages/design-system/tabs/Button.tsx @@ -11,7 +11,7 @@ import { PopoverContent, PopoverRoot, PopoverTrigger, PopoverBody } from 'toolki import { Tooltip } from 'toolkit/chakra/tooltip'; import IconSvg from 'ui/shared/IconSvg'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const ButtonShowcase = () => { return ( diff --git a/ui/showcases/Checkbox.pw.tsx b/client/toolkit/pages/design-system/tabs/Checkbox.pw.tsx similarity index 100% rename from ui/showcases/Checkbox.pw.tsx rename to client/toolkit/pages/design-system/tabs/Checkbox.pw.tsx diff --git a/ui/showcases/Checkbox.tsx b/client/toolkit/pages/design-system/tabs/Checkbox.tsx similarity index 99% rename from ui/showcases/Checkbox.tsx rename to client/toolkit/pages/design-system/tabs/Checkbox.tsx index 8f93f05c3f..db8ac41e83 100644 --- a/ui/showcases/Checkbox.tsx +++ b/client/toolkit/pages/design-system/tabs/Checkbox.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const CheckboxShowcase = () => { diff --git a/ui/showcases/Clipboard.tsx b/client/toolkit/pages/design-system/tabs/Clipboard.tsx similarity index 98% rename from ui/showcases/Clipboard.tsx rename to client/toolkit/pages/design-system/tabs/Clipboard.tsx index 6dd8046f5e..7fd04ab297 100644 --- a/ui/showcases/Clipboard.tsx +++ b/client/toolkit/pages/design-system/tabs/Clipboard.tsx @@ -4,7 +4,7 @@ import React from 'react'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const ClipboardShowcase = () => { diff --git a/ui/showcases/CloseButton.tsx b/client/toolkit/pages/design-system/tabs/CloseButton.tsx similarity index 97% rename from ui/showcases/CloseButton.tsx rename to client/toolkit/pages/design-system/tabs/CloseButton.tsx index d2c416384a..08447d093a 100644 --- a/ui/showcases/CloseButton.tsx +++ b/client/toolkit/pages/design-system/tabs/CloseButton.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { CloseButton } from 'toolkit/chakra/close-button'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const CloseButtonShowcase = () => { diff --git a/ui/showcases/Collapsible.tsx b/client/toolkit/pages/design-system/tabs/Collapsible.tsx similarity index 96% rename from ui/showcases/Collapsible.tsx rename to client/toolkit/pages/design-system/tabs/Collapsible.tsx index f114915e0d..d823062bd4 100644 --- a/ui/showcases/Collapsible.tsx +++ b/client/toolkit/pages/design-system/tabs/Collapsible.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { CollapsibleDetails, CollapsibleList } from 'toolkit/chakra/collapsible'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; -import { TEXT } from './utils'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; +import { TEXT } from '../utils'; const CollapsibleShowcase = () => { return ( diff --git a/ui/showcases/ContentLoader.tsx b/client/toolkit/pages/design-system/tabs/ContentLoader.tsx similarity index 96% rename from ui/showcases/ContentLoader.tsx rename to client/toolkit/pages/design-system/tabs/ContentLoader.tsx index 94c9f24919..37dc2ed586 100644 --- a/ui/showcases/ContentLoader.tsx +++ b/client/toolkit/pages/design-system/tabs/ContentLoader.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const ContentLoaderShowcase = () => { diff --git a/ui/showcases/Dialog.tsx b/client/toolkit/pages/design-system/tabs/Dialog.tsx similarity index 98% rename from ui/showcases/Dialog.tsx rename to client/toolkit/pages/design-system/tabs/Dialog.tsx index 9befa16b22..2abe1e6f68 100644 --- a/ui/showcases/Dialog.tsx +++ b/client/toolkit/pages/design-system/tabs/Dialog.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { Button } from 'toolkit/chakra/button'; import { DialogActionTrigger, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTitle, DialogTrigger } from 'toolkit/chakra/dialog'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; -import { TEXT } from './utils'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; +import { TEXT } from '../utils'; const DialogShowcase = () => { diff --git a/ui/showcases/EmptyState.tsx b/client/toolkit/pages/design-system/tabs/EmptyState.tsx similarity index 97% rename from ui/showcases/EmptyState.tsx rename to client/toolkit/pages/design-system/tabs/EmptyState.tsx index 28cbe3aa32..3e0a77338e 100644 --- a/ui/showcases/EmptyState.tsx +++ b/client/toolkit/pages/design-system/tabs/EmptyState.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { EmptyState } from 'toolkit/chakra/empty-state'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const EmptyStateShowcase = () => { return ( diff --git a/ui/showcases/Field.pw.tsx b/client/toolkit/pages/design-system/tabs/Field.pw.tsx similarity index 100% rename from ui/showcases/Field.pw.tsx rename to client/toolkit/pages/design-system/tabs/Field.pw.tsx diff --git a/ui/showcases/Field.tsx b/client/toolkit/pages/design-system/tabs/Field.tsx similarity index 99% rename from ui/showcases/Field.tsx rename to client/toolkit/pages/design-system/tabs/Field.tsx index 211b637ce2..e2640b3dd2 100644 --- a/ui/showcases/Field.tsx +++ b/client/toolkit/pages/design-system/tabs/Field.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Field } from 'toolkit/chakra/field'; import { Input } from 'toolkit/chakra/input'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const FieldShowcase = () => { return ( diff --git a/ui/showcases/IconButton.pw.tsx b/client/toolkit/pages/design-system/tabs/IconButton.pw.tsx similarity index 100% rename from ui/showcases/IconButton.pw.tsx rename to client/toolkit/pages/design-system/tabs/IconButton.pw.tsx diff --git a/ui/showcases/IconButton.tsx b/client/toolkit/pages/design-system/tabs/IconButton.tsx similarity index 99% rename from ui/showcases/IconButton.tsx rename to client/toolkit/pages/design-system/tabs/IconButton.tsx index b8ac9060f0..0ca18ee579 100644 --- a/ui/showcases/IconButton.tsx +++ b/client/toolkit/pages/design-system/tabs/IconButton.tsx @@ -7,7 +7,7 @@ import { IconButton } from 'toolkit/chakra/icon-button'; import { PopoverBody, PopoverContent, PopoverTrigger, PopoverRoot } from 'toolkit/chakra/popover'; import IconSvg from 'ui/shared/IconSvg'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const IconButtonShowcase = () => { return ( diff --git a/ui/showcases/Input.pw.tsx b/client/toolkit/pages/design-system/tabs/Input.pw.tsx similarity index 100% rename from ui/showcases/Input.pw.tsx rename to client/toolkit/pages/design-system/tabs/Input.pw.tsx diff --git a/ui/showcases/Input.tsx b/client/toolkit/pages/design-system/tabs/Input.tsx similarity index 98% rename from ui/showcases/Input.tsx rename to client/toolkit/pages/design-system/tabs/Input.tsx index 7efed3f6d1..2635534db4 100644 --- a/ui/showcases/Input.tsx +++ b/client/toolkit/pages/design-system/tabs/Input.tsx @@ -8,7 +8,7 @@ import { InputGroup } from 'toolkit/chakra/input-group'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import IconSvg from 'ui/shared/IconSvg'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const InputShowcase = () => { return ( diff --git a/ui/showcases/Link.pw.tsx b/client/toolkit/pages/design-system/tabs/Link.pw.tsx similarity index 100% rename from ui/showcases/Link.pw.tsx rename to client/toolkit/pages/design-system/tabs/Link.pw.tsx diff --git a/ui/showcases/Link.tsx b/client/toolkit/pages/design-system/tabs/Link.tsx similarity index 99% rename from ui/showcases/Link.tsx rename to client/toolkit/pages/design-system/tabs/Link.tsx index be2314a767..aff14a4843 100644 --- a/ui/showcases/Link.tsx +++ b/client/toolkit/pages/design-system/tabs/Link.tsx @@ -10,6 +10,7 @@ import * as addressMock from 'client/slices/address/mocks/address'; import * as implementationsMock from 'client/slices/address/mocks/implementations'; import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import * as blockMock from 'client/slices/block/mocks/block'; +import NftEntity from 'client/slices/token/components/entity/NftEntity'; import TokenEntity from 'client/slices/token/components/entity/TokenEntity'; import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import * as txMock from 'client/slices/tx/mocks/tx'; @@ -22,9 +23,8 @@ import EnsEntity from 'client/features/name-services/domains/components/EnsEntit import * as ensMock from 'client/features/name-services/domains/mocks/domain'; import { Link, LinkBox, LinkOverlay } from 'toolkit/chakra/link'; -import NftEntity from 'ui/shared/entities/nft/NftEntity'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; export const TOKEN = { address_hash: '0xdAC17F958D2ee523a2206206994597C13D831ec7', diff --git a/ui/showcases/Menu.tsx b/client/toolkit/pages/design-system/tabs/Menu.tsx similarity index 98% rename from ui/showcases/Menu.tsx rename to client/toolkit/pages/design-system/tabs/Menu.tsx index e49881bb1c..92626f4ca9 100644 --- a/ui/showcases/Menu.tsx +++ b/client/toolkit/pages/design-system/tabs/Menu.tsx @@ -6,7 +6,7 @@ import { IconButton } from 'toolkit/chakra/icon-button'; import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu'; import IconSvg from 'ui/shared/IconSvg'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const MenuShowcase = () => { diff --git a/ui/showcases/Pagination.pw.tsx b/client/toolkit/pages/design-system/tabs/Pagination.pw.tsx similarity index 100% rename from ui/showcases/Pagination.pw.tsx rename to client/toolkit/pages/design-system/tabs/Pagination.pw.tsx diff --git a/ui/showcases/Pagination.tsx b/client/toolkit/pages/design-system/tabs/Pagination.tsx similarity index 97% rename from ui/showcases/Pagination.tsx rename to client/toolkit/pages/design-system/tabs/Pagination.tsx index 3285f7d2ec..092156a6f1 100644 --- a/ui/showcases/Pagination.tsx +++ b/client/toolkit/pages/design-system/tabs/Pagination.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Pagination from 'ui/shared/pagination/Pagination'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const PaginationShowcase = () => { const [ page, setPage ] = React.useState(1); diff --git a/ui/showcases/PinInput.tsx b/client/toolkit/pages/design-system/tabs/PinInput.tsx similarity index 98% rename from ui/showcases/PinInput.tsx rename to client/toolkit/pages/design-system/tabs/PinInput.tsx index 418d9310b6..6082cd3476 100644 --- a/ui/showcases/PinInput.tsx +++ b/client/toolkit/pages/design-system/tabs/PinInput.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { PinInput } from 'toolkit/chakra/pin-input'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const PinInputShowcase = () => { diff --git a/ui/showcases/Popover.tsx b/client/toolkit/pages/design-system/tabs/Popover.tsx similarity index 97% rename from ui/showcases/Popover.tsx rename to client/toolkit/pages/design-system/tabs/Popover.tsx index e3e6a52a0a..e3fa4a471c 100644 --- a/ui/showcases/Popover.tsx +++ b/client/toolkit/pages/design-system/tabs/Popover.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Button } from 'toolkit/chakra/button'; import { PopoverRoot, PopoverTrigger, PopoverContent, PopoverBody } from 'toolkit/chakra/popover'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const PopoverShowcase = () => { return ( diff --git a/ui/showcases/Progress.tsx b/client/toolkit/pages/design-system/tabs/Progress.tsx similarity index 97% rename from ui/showcases/Progress.tsx rename to client/toolkit/pages/design-system/tabs/Progress.tsx index b540be533f..ecfde22405 100644 --- a/ui/showcases/Progress.tsx +++ b/client/toolkit/pages/design-system/tabs/Progress.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Progress } from 'toolkit/chakra/progress'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const ProgressShowcase = () => { diff --git a/ui/showcases/ProgressCircle.pw.tsx b/client/toolkit/pages/design-system/tabs/ProgressCircle.pw.tsx similarity index 100% rename from ui/showcases/ProgressCircle.pw.tsx rename to client/toolkit/pages/design-system/tabs/ProgressCircle.pw.tsx diff --git a/ui/showcases/ProgressCircle.tsx b/client/toolkit/pages/design-system/tabs/ProgressCircle.tsx similarity index 98% rename from ui/showcases/ProgressCircle.tsx rename to client/toolkit/pages/design-system/tabs/ProgressCircle.tsx index 4333162c6b..11da527e32 100644 --- a/ui/showcases/ProgressCircle.tsx +++ b/client/toolkit/pages/design-system/tabs/ProgressCircle.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { ProgressCircleRoot, ProgressCircleRing, ProgressCircleValueText } from 'toolkit/chakra/progress-circle'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const ProgressCircleShowcase = () => { diff --git a/ui/showcases/Radio.pw.tsx b/client/toolkit/pages/design-system/tabs/Radio.pw.tsx similarity index 100% rename from ui/showcases/Radio.pw.tsx rename to client/toolkit/pages/design-system/tabs/Radio.pw.tsx diff --git a/ui/showcases/Radio.tsx b/client/toolkit/pages/design-system/tabs/Radio.tsx similarity index 99% rename from ui/showcases/Radio.tsx rename to client/toolkit/pages/design-system/tabs/Radio.tsx index a05248631b..bd71da4e9c 100644 --- a/ui/showcases/Radio.tsx +++ b/client/toolkit/pages/design-system/tabs/Radio.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Radio, RadioGroup } from 'toolkit/chakra/radio'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const RadioShowcase = () => { diff --git a/ui/showcases/Rating.tsx b/client/toolkit/pages/design-system/tabs/Rating.tsx similarity index 97% rename from ui/showcases/Rating.tsx rename to client/toolkit/pages/design-system/tabs/Rating.tsx index 3f3cfca4f2..c9c16990ee 100644 --- a/ui/showcases/Rating.tsx +++ b/client/toolkit/pages/design-system/tabs/Rating.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Rating } from 'toolkit/chakra/rating'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const RatingShowcase = () => { return ( diff --git a/ui/showcases/Select.pw.tsx b/client/toolkit/pages/design-system/tabs/Select.pw.tsx similarity index 100% rename from ui/showcases/Select.pw.tsx rename to client/toolkit/pages/design-system/tabs/Select.pw.tsx diff --git a/ui/showcases/Select.tsx b/client/toolkit/pages/design-system/tabs/Select.tsx similarity index 99% rename from ui/showcases/Select.tsx rename to client/toolkit/pages/design-system/tabs/Select.tsx index df5affbd8f..445ad050f8 100644 --- a/ui/showcases/Select.tsx +++ b/client/toolkit/pages/design-system/tabs/Select.tsx @@ -14,7 +14,7 @@ import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio'; import IconSvg from 'ui/shared/IconSvg'; import Sort from 'ui/shared/sort/Sort'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const frameworks = createListCollection({ items: [ diff --git a/ui/showcases/Skeleton.tsx b/client/toolkit/pages/design-system/tabs/Skeleton.tsx similarity index 96% rename from ui/showcases/Skeleton.tsx rename to client/toolkit/pages/design-system/tabs/Skeleton.tsx index 81ad4e3641..e272e0de4b 100644 --- a/ui/showcases/Skeleton.tsx +++ b/client/toolkit/pages/design-system/tabs/Skeleton.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const SkeletonShowcase = () => { diff --git a/ui/showcases/Spinner.tsx b/client/toolkit/pages/design-system/tabs/Spinner.tsx similarity index 95% rename from ui/showcases/Spinner.tsx rename to client/toolkit/pages/design-system/tabs/Spinner.tsx index d1d0c24c1d..47fa67242c 100644 --- a/ui/showcases/Spinner.tsx +++ b/client/toolkit/pages/design-system/tabs/Spinner.tsx @@ -3,7 +3,7 @@ import { Spinner } from '@chakra-ui/react'; import React from 'react'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const SpinnerShowcase = () => { diff --git a/ui/showcases/Status.tsx b/client/toolkit/pages/design-system/tabs/Status.tsx similarity index 98% rename from ui/showcases/Status.tsx rename to client/toolkit/pages/design-system/tabs/Status.tsx index 2a2b1b13d2..7d6f394013 100644 --- a/ui/showcases/Status.tsx +++ b/client/toolkit/pages/design-system/tabs/Status.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Status } from 'toolkit/chakra/status'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const StatusShowcase = () => { diff --git a/ui/showcases/Switch.pw.tsx b/client/toolkit/pages/design-system/tabs/Switch.pw.tsx similarity index 100% rename from ui/showcases/Switch.pw.tsx rename to client/toolkit/pages/design-system/tabs/Switch.pw.tsx diff --git a/ui/showcases/Switch.tsx b/client/toolkit/pages/design-system/tabs/Switch.tsx similarity index 98% rename from ui/showcases/Switch.tsx rename to client/toolkit/pages/design-system/tabs/Switch.tsx index 4d801f0ffa..74b4849fae 100644 --- a/ui/showcases/Switch.tsx +++ b/client/toolkit/pages/design-system/tabs/Switch.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Switch } from 'toolkit/chakra/switch'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const SwitchShowcase = () => { diff --git a/ui/showcases/Table.tsx b/client/toolkit/pages/design-system/tabs/Table.tsx similarity index 99% rename from ui/showcases/Table.tsx rename to client/toolkit/pages/design-system/tabs/Table.tsx index 8481742d04..3f81fafdad 100644 --- a/ui/showcases/Table.tsx +++ b/client/toolkit/pages/design-system/tabs/Table.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { TableColumnHeader, TableHeaderSticky, TableRoot, TableRow, TableCell, TableBody, TableColumnHeaderSortable } from 'toolkit/chakra/table'; import getNextSortValue from 'ui/shared/sort/getNextSortValue'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const ITEMS = [ { id: 1, name: 'Laptop', category: 'Electronics', price: 999.99 }, diff --git a/ui/showcases/Tabs.pw.tsx b/client/toolkit/pages/design-system/tabs/Tabs.pw.tsx similarity index 100% rename from ui/showcases/Tabs.pw.tsx rename to client/toolkit/pages/design-system/tabs/Tabs.pw.tsx diff --git a/ui/showcases/Tabs.tsx b/client/toolkit/pages/design-system/tabs/Tabs.tsx similarity index 98% rename from ui/showcases/Tabs.tsx rename to client/toolkit/pages/design-system/tabs/Tabs.tsx index bb0587c3e4..a8aefa7138 100644 --- a/ui/showcases/Tabs.tsx +++ b/client/toolkit/pages/design-system/tabs/Tabs.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const TabsShowcase = () => { const tabs = [ diff --git a/ui/showcases/Tag.pw.tsx b/client/toolkit/pages/design-system/tabs/Tag.pw.tsx similarity index 100% rename from ui/showcases/Tag.pw.tsx rename to client/toolkit/pages/design-system/tabs/Tag.pw.tsx diff --git a/ui/showcases/Tag.tsx b/client/toolkit/pages/design-system/tabs/Tag.tsx similarity index 96% rename from ui/showcases/Tag.tsx rename to client/toolkit/pages/design-system/tabs/Tag.tsx index 15ea9493ae..aa0045155c 100644 --- a/ui/showcases/Tag.tsx +++ b/client/toolkit/pages/design-system/tabs/Tag.tsx @@ -2,11 +2,12 @@ import React from 'react'; -import * as addressMetadataMock from 'mocks/metadata/address'; +import * as addressMetadataMock from 'client/features/address-metadata/mocks/tags'; + import { Tag } from 'toolkit/chakra/tag'; import EntityTag from 'ui/shared/EntityTags/EntityTag'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; const TagShowcase = () => { return ( diff --git a/ui/showcases/Textarea.pw.tsx b/client/toolkit/pages/design-system/tabs/Textarea.pw.tsx similarity index 100% rename from ui/showcases/Textarea.pw.tsx rename to client/toolkit/pages/design-system/tabs/Textarea.pw.tsx diff --git a/ui/showcases/Textarea.tsx b/client/toolkit/pages/design-system/tabs/Textarea.tsx similarity index 96% rename from ui/showcases/Textarea.tsx rename to client/toolkit/pages/design-system/tabs/Textarea.tsx index e9fca20113..accab3609a 100644 --- a/ui/showcases/Textarea.tsx +++ b/client/toolkit/pages/design-system/tabs/Textarea.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { Field } from 'toolkit/chakra/field'; import { Textarea } from 'toolkit/chakra/textarea'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; -import { TEXT } from './utils'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; +import { TEXT } from '../utils'; const TextareaShowcase = () => { diff --git a/ui/showcases/Toast.tsx b/client/toolkit/pages/design-system/tabs/Toast.tsx similarity index 96% rename from ui/showcases/Toast.tsx rename to client/toolkit/pages/design-system/tabs/Toast.tsx index cebc034645..811c619ffd 100644 --- a/ui/showcases/Toast.tsx +++ b/client/toolkit/pages/design-system/tabs/Toast.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; -import { TEXT } from './utils'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; +import { TEXT } from '../utils'; const ToastShowcase = () => { return ( diff --git a/ui/showcases/Tooltip.tsx b/client/toolkit/pages/design-system/tabs/Tooltip.tsx similarity index 98% rename from ui/showcases/Tooltip.tsx rename to client/toolkit/pages/design-system/tabs/Tooltip.tsx index d32145b824..aa98d29c9d 100644 --- a/ui/showcases/Tooltip.tsx +++ b/client/toolkit/pages/design-system/tabs/Tooltip.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Tooltip } from 'toolkit/chakra/tooltip'; import Utilization from 'ui/shared/Utilization/Utilization'; -import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts'; +import { Section, Container, SectionHeader, SamplesStack, Sample } from '../parts'; const TooltipShowcase = () => { return ( diff --git a/ui/showcases/Values.pw.tsx b/client/toolkit/pages/design-system/tabs/Values.pw.tsx similarity index 100% rename from ui/showcases/Values.pw.tsx rename to client/toolkit/pages/design-system/tabs/Values.pw.tsx diff --git a/ui/showcases/Values.tsx b/client/toolkit/pages/design-system/tabs/Values.tsx similarity index 99% rename from ui/showcases/Values.tsx rename to client/toolkit/pages/design-system/tabs/Values.tsx index a6daef5b5a..48a1df6f56 100644 --- a/ui/showcases/Values.tsx +++ b/client/toolkit/pages/design-system/tabs/Values.tsx @@ -9,8 +9,8 @@ import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; import SimpleValue from 'ui/shared/value/SimpleValue'; import TokenValue from 'ui/shared/value/TokenValue'; +import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from '../parts'; import { TOKEN } from './Link'; -import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; const ValuesShowcase = () => { return ( diff --git a/ui/showcases/__screenshots__/Alert.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Alert.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Alert.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Alert.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Alert.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Alert.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Alert.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Alert.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Badge.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Badge.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Badge.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Badge.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Badge.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Badge.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Badge.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Badge.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Button.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Button.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Button.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Button.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Button.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Button.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Button.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Button.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Button.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Checkbox.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Checkbox.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Checkbox.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Checkbox.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Checkbox.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Checkbox.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Checkbox.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Checkbox.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Checkbox.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Field.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Field.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Field.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Field.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Field.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Field.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Field.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Field.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/IconButton.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/IconButton.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/IconButton.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/IconButton.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/IconButton.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/IconButton.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/IconButton.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/IconButton.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/IconButton.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Input.pw.tsx_default_default-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Input.pw.tsx_default_default-1.png similarity index 100% rename from ui/showcases/__screenshots__/Input.pw.tsx_default_default-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Input.pw.tsx_default_default-1.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_dark-color-mode_default-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Link.pw.tsx_default_default-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_default-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Link.pw.tsx_default_default-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Link.pw.tsx_default_default-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Pagination.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Pagination.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Pagination.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Pagination.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Pagination.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Pagination.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Pagination.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Pagination.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Pagination.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/ProgressCircle.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/ProgressCircle.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/ProgressCircle.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/ProgressCircle.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/ProgressCircle.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Radio.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Radio.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Radio.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Radio.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Radio.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Radio.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Radio.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Radio.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Radio.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_dark-color-mode_default-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_color-theme-overrides-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Select.pw.tsx_default_default-dark-mode-2.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_default-dark-mode-2.png similarity index 100% rename from ui/showcases/__screenshots__/Select.pw.tsx_default_default-dark-mode-2.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Select.pw.tsx_default_default-dark-mode-2.png diff --git a/ui/showcases/__screenshots__/Switch.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Switch.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Switch.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Switch.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Switch.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Switch.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Switch.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Switch.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Switch.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tabs.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tabs.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_dark-color-mode_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tabs.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tabs.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tabs.pw.tsx_default_color-theme-overrides-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_default_color-theme-overrides-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tabs.pw.tsx_default_color-theme-overrides-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_default_color-theme-overrides-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tabs.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tabs.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tabs.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tag.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tag.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tag.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tag.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Tag.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Tag.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Tag.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Tag.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Textarea.pw.tsx_dark-color-mode_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Textarea.pw.tsx_dark-color-mode_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Textarea.pw.tsx_dark-color-mode_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Textarea.pw.tsx_dark-color-mode_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Textarea.pw.tsx_default_default-dark-mode-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Textarea.pw.tsx_default_default-dark-mode-1.png similarity index 100% rename from ui/showcases/__screenshots__/Textarea.pw.tsx_default_default-dark-mode-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Textarea.pw.tsx_default_default-dark-mode-1.png diff --git a/ui/showcases/__screenshots__/Values.pw.tsx_default_default-1.png b/client/toolkit/pages/design-system/tabs/__screenshots__/Values.pw.tsx_default_default-1.png similarity index 100% rename from ui/showcases/__screenshots__/Values.pw.tsx_default_default-1.png rename to client/toolkit/pages/design-system/tabs/__screenshots__/Values.pw.tsx_default_default-1.png diff --git a/ui/showcases/utils.ts b/client/toolkit/pages/design-system/utils.ts similarity index 100% rename from ui/showcases/utils.ts rename to client/toolkit/pages/design-system/utils.ts diff --git a/configs/app/features/defi-dropdown.ts b/configs/app/features/defi-dropdown.ts index 32f5630821..e5af5564fc 100644 --- a/configs/app/features/defi-dropdown.ts +++ b/configs/app/features/defi-dropdown.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { DeFiDropdownItem } from 'types/client/deFiDropdown'; +import type { DeFiDropdownItem } from 'client/features/defi-dropdown/types/client'; import app from '../app'; import { getEnvValue, parseEnvJson } from '../utils'; diff --git a/configs/app/features/easter-egg-badge.ts b/configs/app/features/easter-egg-badge.ts deleted file mode 100644 index 9f0c64655e..0000000000 --- a/configs/app/features/easter-egg-badge.ts +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { Feature } from './types'; - -import { getEnvValue } from '../utils'; - -const badgeClaimLink = getEnvValue('NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK'); - -const title = 'Easter egg badge'; - -const config: Feature<{ badgeClaimLink: string }> = (() => { - if (badgeClaimLink) { - return Object.freeze({ - title, - isEnabled: true, - badgeClaimLink, - }); - } - - return Object.freeze({ - title, - isEnabled: false, - }); -})(); - -export default config; diff --git a/configs/app/features/easter-egg-puzzle-badge.ts b/configs/app/features/easter-egg-puzzle-badge.ts deleted file mode 100644 index 31c67be381..0000000000 --- a/configs/app/features/easter-egg-puzzle-badge.ts +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { Feature } from './types'; - -import { getEnvValue } from '../utils'; - -const badgeClaimLink = getEnvValue('NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK'); - -const title = 'Easter egg puzzle badge'; - -const config: Feature<{ badgeClaimLink: string }> = (() => { - if (badgeClaimLink) { - return Object.freeze({ - title, - isEnabled: true, - badgeClaimLink, - }); - } - - return Object.freeze({ - title, - isEnabled: false, - }); -})(); - -export default config; diff --git a/configs/app/features/easter-eggs.ts b/configs/app/features/easter-eggs.ts new file mode 100644 index 0000000000..64e57f0db5 --- /dev/null +++ b/configs/app/features/easter-eggs.ts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; + +const runnerClaimLink = getEnvValue('NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK'); +const puzzleClaimLink = getEnvValue('NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK'); + +const title = 'Easter eggs'; + +const config: Feature<{ runner?: { claimLink: string }; puzzle?: { claimLink: string } }> = (() => { + if (runnerClaimLink || puzzleClaimLink) { + return Object.freeze({ + title, + isEnabled: true, + runner: runnerClaimLink ? { claimLink: runnerClaimLink } : undefined, + puzzle: puzzleClaimLink ? { claimLink: puzzleClaimLink } : undefined, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/external-txs.ts b/configs/app/features/external-txs.ts index 6336b64019..d43f0aabcd 100644 --- a/configs/app/features/external-txs.ts +++ b/configs/app/features/external-txs.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { TxExternalTxsConfig } from 'types/client/externalTxsConfig'; +import type { TxExternalTxsConfig } from 'client/features/external-txs/types/client'; import { getEnvValue, parseEnvJson } from '../utils'; diff --git a/configs/app/features/get-gas-button.ts b/configs/app/features/get-gas-button.ts index 68f6cce803..b653d685e6 100644 --- a/configs/app/features/get-gas-button.ts +++ b/configs/app/features/get-gas-button.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import type { Feature } from './types'; -import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig'; +import type { GasRefuelProviderConfig } from 'client/features/get-gas-button/types/client'; import chain from '../chain'; import { getEnvValue, parseEnvJson } from '../utils'; diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index fbc86d6cf1..2b8fc01524 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -16,8 +16,7 @@ export { default as crossChainTxs } from './cross-chain-txs'; export { default as csvExport } from './csv-export'; export { default as dataAvailability } from './data-availability'; export { default as deFiDropdown } from './defi-dropdown'; -export { default as easterEggBadge } from './easter-egg-badge'; -export { default as easterEggPuzzleBadge } from './easter-egg-puzzle-badge'; +export { default as easterEggs } from './easter-eggs'; export { default as externalTxs } from './external-txs'; export { default as faultProofSystem } from './fault-proof-system'; export { default as flashblocks } from './flashblocks'; diff --git a/configs/app/ui/views/block.ts b/configs/app/ui/views/block.ts index e921f0608f..bda3edf663 100644 --- a/configs/app/ui/views/block.ts +++ b/configs/app/ui/views/block.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { BlockFieldId } from 'types/views/block'; -import { BLOCK_FIELDS_IDS } from 'types/views/block'; +import type { BlockFieldId } from 'client/slices/block/types/config'; +import { BLOCK_FIELDS_IDS } from 'client/slices/block/types/config'; import { getEnvValue, parseEnvJson } from 'configs/app/utils'; diff --git a/configs/app/ui/views/nft.ts b/configs/app/ui/views/nft.ts index 6b7f65fad9..7e36c51d6f 100644 --- a/configs/app/ui/views/nft.ts +++ b/configs/app/ui/views/nft.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { NftMarketplaceItem } from 'types/views/nft'; +import type { NftMarketplaceItem } from 'client/slices/token/types/client'; import { getEnvValue, parseEnvJson } from 'configs/app/utils'; diff --git a/cspell.jsonc b/cspell.jsonc index c2ff4ccbe7..cbee145870 100644 --- a/cspell.jsonc +++ b/cspell.jsonc @@ -11,11 +11,11 @@ "pnpm-lock.yaml", "configs/envs", "client/features/chain-variants/zeta-chain/mocks/cctx.ts", - "mocks/metadata/address.ts", + "client/features/address-metadata/mocks/tags.ts", "playwright/mocks/file_mock_with_very_long_name.json", "playwright/fixtures/rewards.ts", "public/static/capybara/index.js", - "ui/showcases/utils.ts", + "client/toolkit/pages/design-system/utils.ts", "client/features/external-txs/components/TxExternalTxs.pw.tsx" ], "enableGlobDot": true, diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index e7881e433f..afe073b3b7 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -16,7 +16,7 @@ type AddressProfileAPIConfig = { tag_bg_color?: string; tag_text_color?: string; }; -import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig'; +import type { GasRefuelProviderConfig } from 'client/features/get-gas-button/types/client'; import { GAS_UNITS } from 'client/slices/gas/types/config'; import type { GasUnit } from 'client/slices/gas/types/config'; import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from 'client/features/tx-interpretation/common/types/config'; @@ -24,7 +24,7 @@ import { VALIDATORS_CHAIN_TYPE } from 'configs/app/features/validators'; import type { ValidatorsChainType } from 'configs/app/features/validators'; import type { WalletType } from 'client/features/web3-wallet/types/config'; import { SUPPORTED_WALLETS } from 'client/features/web3-wallet/types/config'; -import type { TxExternalTxsConfig } from 'types/client/externalTxsConfig'; +import type { TxExternalTxsConfig } from 'client/features/external-txs/types/client'; import { replaceQuotes } from 'configs/app/utils'; import { urlTest, protocols } from './utils'; diff --git a/deploy/tools/envs-validator/schemas/features/defiDropdown.ts b/deploy/tools/envs-validator/schemas/features/defiDropdown.ts index 98d048b328..e42ca46a95 100644 --- a/deploy/tools/envs-validator/schemas/features/defiDropdown.ts +++ b/deploy/tools/envs-validator/schemas/features/defiDropdown.ts @@ -1,5 +1,5 @@ import * as yup from 'yup'; -import type { DeFiDropdownItem } from 'types/client/deFiDropdown'; +import type { DeFiDropdownItem } from 'client/features/defi-dropdown/types/client'; import type { IconName } from 'public/icons/name'; import { urlTest } from '../../utils'; import { replaceQuotes } from 'configs/app/utils'; diff --git a/deploy/tools/envs-validator/schemas/ui.ts b/deploy/tools/envs-validator/schemas/ui.ts index 4a31e54ec6..9945a161ab 100644 --- a/deploy/tools/envs-validator/schemas/ui.ts +++ b/deploy/tools/envs-validator/schemas/ui.ts @@ -11,12 +11,12 @@ import type { ContractCodeIde } from 'client/slices/contract/types/config'; import { SMART_CONTRACT_EXTRA_VERIFICATION_METHODS, type SmartContractVerificationMethodExtra } from 'client/slices/contract/types/config'; import type { AddressFormat, AddressViewId } from 'client/slices/address/types/config'; import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'client/slices/address/types/config'; -import { BLOCK_FIELDS_IDS } from 'types/views/block'; -import type { BlockFieldId } from 'types/views/block'; +import { BLOCK_FIELDS_IDS } from 'client/slices/block/types/config'; +import type { BlockFieldId } from 'client/slices/block/types/config'; import type { TxAdditionalFieldsId, TxFieldsId, TxViewId } from 'client/slices/tx/types/config'; import { TX_ADDITIONAL_FIELDS_IDS, TX_FIELDS_IDS, TX_VIEWS_IDS } from 'client/slices/tx/types/config'; import * as regexp from 'toolkit/utils/regexp'; -import { NftMarketplaceItem } from 'types/views/nft'; +import { NftMarketplaceItem } from 'client/slices/token/types/client'; const heroBannerButtonStateSchema: yup.ObjectSchema = yup.object({ background: yup.array().max(2).of(yup.string()), diff --git a/docs/ENVS.md b/docs/ENVS.md index a75c3ead7d..cd7bf51b84 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -1102,19 +1102,12 @@ This feature enables cross-chain transaction tracking and visualization, allowin   -### Badge claim link +### Easter eggs | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK | `string` | Provide to enable the easter egg badge feature | - | - | `https://example.com` | v1.37.0+ | - -  - -### Puzzle game badge claim link - -| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | -| --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK | `string` | Provide to enable the easter egg puzzle badge feature | - | - | `https://example.com` | v2.2.0+ | +| NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK | `string` | Provide to enable the easter egg runner game feature | - | - | `https://example.com` | v1.37.0+ | +| NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK | `string` | Provide to enable the easter egg puzzle game feature | - | - | `https://example.com` | v2.2.0+ |   diff --git a/eslint.config.mjs b/eslint.config.mjs index 609ada0da1..d4a1eeed40 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -47,7 +47,7 @@ const spdxLicenseRule = { const RESTRICTED_MODULES = { paths: [ - { name: 'dayjs', message: 'Please use lib/date/dayjs.ts instead of directly importing dayjs' }, + { name: 'dayjs', message: 'Please use client/shared/date-and-time/dayjs.ts instead of directly importing dayjs' }, { name: '@chakra-ui/icons', message: 'Using @chakra-ui/icons is prohibited. Please use regular svg-icon instead (see examples in "icons/" folder)' }, { name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' }, { name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' }, diff --git a/nextjs/PageNextJs.tsx b/nextjs/PageNextJs.tsx index ca65593617..786950a418 100644 --- a/nextjs/PageNextJs.tsx +++ b/nextjs/PageNextJs.tsx @@ -6,12 +6,12 @@ import type { Route } from 'nextjs-routes'; import type { Props as PageProps } from 'nextjs/getServerSideProps/handlers'; import useGetCsrfToken from 'client/features/account/hooks/useGetCsrfToken'; +import useNotifyOnNavigation from 'client/features/metasuites/hooks/useNotifyOnNavigation'; import * as mixpanel from 'client/shared/analytics/mixpanel'; import useIsMounted from 'client/shared/hooks/useIsMounted'; import useAdblockDetect from 'lib/hooks/useAdblockDetect'; -import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation'; interface Props { pathname: Pathname; diff --git a/lib/monitoring/metrics.ts b/nextjs/monitoring/metrics.ts similarity index 95% rename from lib/monitoring/metrics.ts rename to nextjs/monitoring/metrics.ts index c98b3e7c0e..15a5d0c7f7 100644 --- a/lib/monitoring/metrics.ts +++ b/nextjs/monitoring/metrics.ts @@ -3,7 +3,7 @@ import * as promClient from 'prom-client'; const metrics = (() => { - // eslint-disable-next-line no-restricted-properties + if (process.env.PROMETHEUS_METRICS_ENABLED !== 'true') { return; } diff --git a/nextjs/utils/fetchApi.ts b/nextjs/utils/fetchApi.ts index 571ca25625..3919834ce1 100644 --- a/nextjs/utils/fetchApi.ts +++ b/nextjs/utils/fetchApi.ts @@ -7,9 +7,10 @@ import { httpLogger } from 'nextjs/utils/logger'; import type { ResourceName, ResourcePathParams, ResourcePayload } from 'client/api/resources'; -import metrics from 'lib/monitoring/metrics'; import { SECOND } from 'toolkit/utils/consts'; +import metrics from '../monitoring/metrics'; + type Params = { resource: R; pathParams?: ResourcePathParams; diff --git a/nextjs/utils/logRequestFromBot.ts b/nextjs/utils/logRequestFromBot.ts index 4e7e030c6b..a7fffbba8f 100644 --- a/nextjs/utils/logRequestFromBot.ts +++ b/nextjs/utils/logRequestFromBot.ts @@ -2,8 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'http'; -import metrics from 'lib/monitoring/metrics'; - +import metrics from '../monitoring/metrics'; import detectBotRequest from './detectBotRequest'; export default async function logRequestFromBot(req: IncomingMessage | undefined, res: ServerResponse | undefined, pathname: string) { diff --git a/pages/_app.tsx b/pages/_app.tsx index 4048bd56cd..c3e81d44a7 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -21,6 +21,7 @@ import Web3Provider from 'client/features/connect-wallet/components/Web3Provider import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; import { MarketplaceContextProvider } from 'client/features/marketplace/context'; +import GoogleAnalytics from 'client/shared/analytics/google/GoogleAnalytics'; import { initGrowthBook } from 'client/shared/feature-flags/init'; import useLoadFeatures from 'client/shared/feature-flags/useLoadFeatures'; import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'client/shared/monitoring/rollbar'; @@ -33,7 +34,6 @@ import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; import { Toaster } from 'toolkit/chakra/toaster'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorGlobalContainer from 'ui/shared/AppError/AppErrorGlobalContainer'; -import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import Layout from 'ui/shared/layout/Layout'; const RewardsContextProvider = dynamic(() => import('client/features/rewards/context').then(module => module.RewardsContextProvider), { ssr: false }); diff --git a/pages/api/monitoring/invalid-api-schema.ts b/pages/api/monitoring/invalid-api-schema.ts index d3d6f5996d..a36142d3e2 100644 --- a/pages/api/monitoring/invalid-api-schema.ts +++ b/pages/api/monitoring/invalid-api-schema.ts @@ -4,14 +4,13 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import type { ApiName } from 'client/api/types'; +import metrics from 'nextjs/monitoring/metrics'; import { httpLogger } from 'nextjs/utils/logger'; import { RESOURCES } from 'client/api/resources'; import getErrorMessage from 'client/shared/errors/get-error-message'; -import metrics from 'lib/monitoring/metrics'; - export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { const payload: { resource?: string; url?: string } = typeof req.body === 'string' ? JSON.parse(req.body) : req.body; diff --git a/pages/api/tokens/[hash]/instances/[id]/media-type.ts b/pages/api/tokens/[hash]/instances/[id]/media-type.ts index d77d48de00..798bb1ab08 100644 --- a/pages/api/tokens/[hash]/instances/[id]/media-type.ts +++ b/pages/api/tokens/[hash]/instances/[id]/media-type.ts @@ -3,12 +3,12 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import nodeFetch from 'node-fetch'; +import metrics from 'nextjs/monitoring/metrics'; import fetchApi from 'nextjs/utils/fetchApi'; import { httpLogger } from 'nextjs/utils/logger'; import getQueryParamString from 'client/shared/router/get-query-param-string'; -import metrics from 'lib/monitoring/metrics'; import { SECOND } from 'toolkit/utils/consts'; export default async function tokenInstanceMediaTypeHandler(req: NextApiRequest, res: NextApiResponse) { diff --git a/pages/chakra.tsx b/pages/chakra.tsx index 8b9b80f90c..cdb11a82b9 100644 --- a/pages/chakra.tsx +++ b/pages/chakra.tsx @@ -1,16 +1,15 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import DesignSystem from 'client/toolkit/pages/design-system/DesignSystem'; import type { NextPage } from 'next'; import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import Chakra from 'ui/pages/Chakra'; - const Page: NextPage = () => { return ( - + ); }; diff --git a/pages/login.tsx b/pages/login.tsx index 234de70845..8459cd61a8 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -5,7 +5,7 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import Login from 'ui/pages/Login'; +import Login from 'client/features/account/pages/login/Login'; const Page: NextPage = () => { return ( diff --git a/pages/sprite.tsx b/pages/sprite.tsx index a23fa0afe4..5479b86bbd 100644 --- a/pages/sprite.tsx +++ b/pages/sprite.tsx @@ -1,12 +1,11 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import Sprite from 'client/sprite/pages/Sprite'; import type { NextPage } from 'next'; import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; -import Sprite from 'ui/pages/Sprite'; - const Page: NextPage = () => { return ( diff --git a/pages/stats/[id].tsx b/pages/stats/[id].tsx index 60c7ca1c2a..249f899846 100644 --- a/pages/stats/[id].tsx +++ b/pages/stats/[id].tsx @@ -13,10 +13,10 @@ import fetchApi from 'nextjs/utils/fetchApi'; import { MultichainProvider } from 'client/features/multichain/context'; +import dayjs from 'client/shared/date-and-time/dayjs'; import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import dayjs from 'lib/date/dayjs'; const ChainStatsDetails = dynamic(() => import('client/features/chain-stats/pages/details/ChainStatsDetails'), { ssr: false }); diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts index 0892f99eb9..4ae49f279b 100644 --- a/playwright-ct.config.ts +++ b/playwright-ct.config.ts @@ -95,10 +95,10 @@ const config: PlaywrightTestConfig = defineConfig({ { find: '@metamask/providers', replacement: './playwright/mocks/modules/@metamask/providers.js' }, // Mock for growthbook to test feature flags - { find: 'client/shared/feature-flags/useFeatureValue', replacement: './playwright/mocks/lib/growthbook/useFeatureValue.js' }, + { find: 'client/shared/feature-flags/useFeatureValue', replacement: './playwright/mocks/client/shared/feature-flags/useFeatureValue.js' }, // Mock for reCaptcha hook - { find: 'ui/shared/reCaptcha/useReCaptcha', replacement: './playwright/mocks/ui/shared/recaptcha/useReCaptcha.js' }, + { find: 'client/shared/re-captcha/useReCaptcha', replacement: './playwright/mocks/client/shared/re-captcha/useReCaptcha.js' }, // The createWeb3Modal() function from web3modal/wagmi/react somehow pollutes the global styles which causes the tests to fail // We don't call this function in TestApp and since we use useWeb3Modal() and useWeb3ModalState() hooks in the code, we have to mock the module diff --git a/playwright/mocks/lib/growthbook/useFeatureValue.js b/playwright/mocks/client/shared/feature-flags/useFeatureValue.js similarity index 100% rename from playwright/mocks/lib/growthbook/useFeatureValue.js rename to playwright/mocks/client/shared/feature-flags/useFeatureValue.js diff --git a/playwright/mocks/ui/shared/recaptcha/useReCaptcha.js b/playwright/mocks/client/shared/re-captcha/useReCaptcha.js similarity index 100% rename from playwright/mocks/ui/shared/recaptcha/useReCaptcha.js rename to playwright/mocks/client/shared/re-captcha/useReCaptcha.js diff --git a/types/api/charts.ts b/types/api/charts.ts deleted file mode 100644 index ac95092c5a..0000000000 --- a/types/api/charts.ts +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -export interface ChartTransactionItem { - date: string; - transactions_count: number | null; -} - -export interface ChartMarketItem { - date: string; - closing_price: string | null; - market_cap?: string | null; - tvl?: string | null; -} - -export interface ChartTransactionResponse { - chart_data: Array; -} - -export interface ChartMarketResponse { - available_supply: string; - chart_data: Array; -} - -export interface ChartSecondaryCoinPriceResponse { - available_supply: string; - chart_data: Array; -} diff --git a/types/api/fheOperations.ts b/types/api/fheOperations.ts deleted file mode 100644 index 4478a889db..0000000000 --- a/types/api/fheOperations.ts +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import type { AddressParam } from 'client/slices/address/types/api'; - -export type FheOperationType = - | 'arithmetic' | - 'bitwise' | - 'comparison' | - 'unary' | - 'control' | - 'encryption' | - 'random'; - -export type FheType = - | 'Bool' | - 'Uint8' | - 'Uint16' | - 'Uint32' | - 'Uint64' | - 'Uint128' | - 'Uint160' | - 'Uint256' | - 'Bytes64' | - 'Bytes128' | - 'Bytes256'; - -export interface FheOperationInputs { - lhs?: string; - rhs?: string; - ct?: string; - control?: string; - if_true?: string; - if_false?: string; - plaintext?: number; -} - -export interface FheOperation { - log_index: number; - operation: string; - type: FheOperationType; - fhe_type: FheType; - is_scalar: boolean; - hcu_cost: number; - hcu_depth: number; - caller: AddressParam | null; - inputs: FheOperationInputs; - result: string; - block_number: number; -} - -export interface FheOperationsResponse { - items: Array; - total_hcu: number; - max_depth_hcu: number; - operation_count: number; -} diff --git a/types/api/reward.ts b/types/api/reward.ts deleted file mode 100644 index 7931c517aa..0000000000 --- a/types/api/reward.ts +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -export interface Reward { - reward: string; - type: 'Miner Reward' | 'Validator Reward' | 'Emission Reward' | 'Chore Reward' | 'Uncle Reward' | 'POA Mania Reward'; -} diff --git a/types/api/txAction.ts b/types/api/txAction.ts deleted file mode 100644 index 58ffb96f11..0000000000 --- a/types/api/txAction.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -export interface TxActionGeneral { - type: 'mint' | 'burn' | 'collect' | 'swap'; - data: { - amount0: string; - symbol0: string; - address0: string; - amount1: string; - symbol1: string; - address1: string; - }; -} - -export interface TxActionNft { - type: 'mint_nft'; - data: { - name: string; - symbol: string; - address: string; - to: string; - ids: Array; - }; -} - -export type TxAction = { - protocol: 'uniswap_v3'; -} & (TxActionGeneral | TxActionNft); diff --git a/types/views/nft.ts b/types/views/nft.ts deleted file mode 100644 index 52455c9312..0000000000 --- a/types/views/nft.ts +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -export interface NftMarketplaceItem { - name: string; - collection_url?: string; - instance_url?: string; - logo_url: string; -} diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png deleted file mode 100644 index e6d4d0a11d5002fd3a3565522710b336a12bf320..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43505 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 D{EMS* diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png deleted file mode 100644 index e8b45eb2cb5bcf83ec04371cdd0aa1cc2ca23d09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42994 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

^}9};@!!m_cxK4;mOHS$@$eLO4*|7HGM8d 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 diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png deleted file mode 100644 index b1c12ee274172a0d898bc626f7b82c0a7798e8e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63873 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 diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png deleted file mode 100644 index d34519a9ec04cbed05f8d72ec2dc0d2f2969f454..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47622 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 EA6r&&ssI20 diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_bridged-token-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_bridged-token-1.png deleted file mode 100644 index 14661d4cb1ab72bdf3738af8569cdae41ed9e6a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54308 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 z°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 diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png deleted file mode 100644 index 24d37325ecd6efd93b9dfbb031cb6a159d8dbdef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43780 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 Yj|SgaMLI74a98lul(ZDf6yALLA4UEt<8 diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_scam-token-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_scam-token-1.png deleted file mode 100644 index 89a67fb5d136d91eefe465a1ee24e1b6272f7ae1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52187 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

b*UL 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 diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png deleted file mode 100644 index 168ca12cc60ab9e5a76e6c8caff1116bf028d870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78413 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 Tq9EGvSBe?ZHs^NW8K$A5B&5T#71**CRlK5L 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 diff --git a/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-1.png b/ui/pages/__screenshots__/TokenInstance.pw.tsx_default_metadata-update-1.png deleted file mode 100644 index fc1827e5b0d1df1545b2d74bf55592bf212d8655..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189206 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