From 674dc73e423fba04ad168c2b19247e6cc5a330f0 Mon Sep 17 00:00:00 2001 From: tom Date: Tue, 19 May 2026 20:04:08 +0200 Subject: [PATCH 01/87] =?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 a093ef3899d..d3b731d71a8 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 eaaa2ccafa7..067f2bb0543 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/87] 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 cb019b6d498..3464d51cd6f 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 00000000000..5c6fffe5b6a --- /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 6f790e9ff8b..3e29ef4b1c9 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 645e975ebf0..b639ed27fa8 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 7e864be72fd..0b11fbdffb7 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 33e052f39f3..c702a102c1f 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 bf1106c1377..dafa947037b 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 061b02cfe71..b1c103ee959 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 8cbaa180c46..e7a9fd5b7b2 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/87] [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 ea2618c3e11..045b6870974 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 7746a2f1984..96126a1e9a1 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 b56efa324a2..2eeec3c12b9 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 f036d68b41f..7bbcc90a898 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 a4d1d7e25f1..ff21ce45951 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 3db1df31896..be1612c8fa4 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 e790c72a7ed..193367aa85d 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 aac604ebcb5..937a74ad57e 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 0979b850234..287f9a4c20c 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 5e39b6dd36d..f1ca7b01565 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 437b9ce4997..0c4ca7262d0 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 871bb9cc818..d0361a57be4 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 3d5fdf55f65..8afd0d1242c 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 7131cf8ca09..52436dabdca 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 897e328f1fe..1b9984bd875 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 2a4fd8b1a81..5b994a4d252 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 0c0945940ff..c76a50e1d16 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 c020ab16e4b..3855abd48c9 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 3c006a95824..f03351549b5 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 4e40b3e707e..c2e0e057a19 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 aea7affbe37..4496681faa5 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 d4757b49418..0dc25c0967c 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 6ec02d42903..552956dfe81 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 99a896ba068..dcce6a4881d 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 44193d55727..905c58d87c9 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 ada1b3e666c..1f2af370746 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 5d5ae41b631..b3657fc1790 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 276d3d43fa1..04401df9e39 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 9d854dbd27f..b521c976a2d 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 d79507a417f..7b62e34e538 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 a898cdf84b5..3bbcd7719fa 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 c91a5c1da13..c405110d0d3 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 9a177870b9c..9be71155a00 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 531a3be3522..f01d64847fc 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 93afc48fcbb..28d4986b075 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 6e37879d730..6d3baf1008c 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 a19e9d2fbc8..5ab2cd5dc3c 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 ce745f8f787..215fa17af78 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 bfb60e8ebc9..7ad2d332298 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 f9b071fdaba..14894ce81c4 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 224ac8e8f7b..a561c483c09 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 3ae67c416dc..daf14142f44 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 ebbe65c7658..5e5eda98487 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 0b19f47949e..9b3338a84d3 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 6a10626a0f2..5b3a5050d8d 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 a247210bbbe..9a3bdaa46b5 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 5798eea35d6..812da8a4dd2 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 a3f39db8851..0a669b3aaa4 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 dd0c114cab0..1dfdef9c076 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 77781738707..c2b376fff58 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 92b788f9ee6..e14628cae46 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 d1640d34ab1..48302fb8005 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 1b3dad474eb..2159d31c193 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 3a0f046a40a..872dc6afef8 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 39a639937b4..a64dcc552f4 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 28177dc4f20..c499ab66f55 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 8c6489aa84e..546706e72b7 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 62b2baa1794..eec9177f9b0 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 76011f79d5a..d475a771878 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 9b5fb1c8a2d..00000000000 --- 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 5552b5cc20a..00000000000 --- 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 f7f1616a189..8eea9cdd29a 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 7227a448ee9..0b71bb691de 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 81babb505a5..49d2fae390e 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/87] =?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 d3b731d71a8..2bc20b9fae5 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/87] [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 d97bf9e9e5c..c8c3b9e05e2 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 1d121a22108..ea226b92ea3 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 856bf99ceaa..2b9eddf592d 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 e22e17378b9..1338b58aa68 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 9296c21e736..9b2dd9351b3 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 6d54e4eca10..a8ea1faafcc 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 9a5fca534b7..58365e6eaa3 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 9ded595de41..8bf1ab8781a 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 b5281ba6832..9996dcb5bf0 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 6ccab67f757..46bf1177ade 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 c4fb0f059e2..b9552ac02df 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 1fd039db693..31acf32ea1e 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 523ab9ef58a..649c79f2b69 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 90da79ecd0f..fc002205cf5 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 0cb3e87cd71..55552340596 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 cb4b11364fc..ebe4ca84eb6 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 9077ce449d6..90d8fc64b15 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 5d8d9bba1cb..1b7c2123a11 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 9e9dbdda25e..7f4a459258d 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 e90ec3ae86e..ca3e9228973 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 62ce3c6e081..c1b64f36b0e 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 dfdd89efa6b..d2b5a8aa44c 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 a985c5430a5..b6cf50010d2 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 d7c8d1f2e36..e1933c0f611 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 60ec7930480..02d46b51376 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 943e472155d..7545ed16e4c 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 b3508df4b05..ddde94af319 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 476a40267f9..66dec4fb247 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 4779ad2d1f1..1d6d85c3523 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 df75fd9c733..24dcfbb5171 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 bd8bb9ca779..adeb6d6202a 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 dcb6380a895..70363d05438 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 fe146d58ca6..9ab20139a70 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 9da1252c1d9..6168544055f 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 604751f7b4d..29d15f54781 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 ecc1d616597..7b7a73e5e0a 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/87] =?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 2bc20b9fae5..7965576c4ed 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/87] 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 c8c3b9e05e2..219c89a66fd 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 ea226b92ea3..df8f9bf6ab9 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 2b9eddf592d..e0b70bf782c 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 9b2dd9351b3..cebc652882d 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 a8ea1faafcc..7f366a99ea1 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 58365e6eaa3..4fbb2c2b83d 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 8bf1ab8781a..4602a9bbf92 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 9996dcb5bf0..efc837a482f 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 46bf1177ade..5a23ca73da2 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 b9552ac02df..2fca52144e4 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 31acf32ea1e..385bc0a434a 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 fc002205cf5..d4969bc7ead 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 90d8fc64b15..0b60c9e8072 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 1b7c2123a11..7f17f2773ca 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 02d46b51376..29c718c2b2b 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 ddde94af319..7bb6688da45 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/87] [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 617fdc296c3..546c46bca4d 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 4a4d5fad2cd..e751e15bde1 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 2e8c2ccfda0..48adda811f3 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 c31967da22d..4249fdf71a8 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 5ed3f5d1b60..edf6322fbba 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 bdc41a49665..ea4ab135a15 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 3acdf167cde..7d046f05f53 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 19c5239ac37..fe4aba7923f 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 bdc7c5c364a..847f7c9a8e6 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 e13dc029e41..d36c5b9bd5d 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 d09f348b8eb..db066808743 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 4bf57a5c55a..055dba989cd 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 2fe58192d6c..0aad93a5f94 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 a3473c26d55..a8520efbe03 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 e60cad0b763..5b9a95834f5 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 c2b2d204c94..b3612a7d018 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 d0eda9a9571..e28afa7ebd2 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 922aa54f9ca..3b0989fca36 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 3866edbff1b..ec066bb7870 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 49d2fae390e..be2314a7671 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/87] =?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 7965576c4ed..840eb5c51b7 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/87] 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 fdf45608eab..bd7218f27c9 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/87] [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 c35a2f2e62a..0cc7ca3db49 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 85dbf5a5fc1..0fbfcb7b365 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 7360827fa28..8ef172e39d0 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 61cbd2d399f..a2d9e064b71 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 4309167381a..0f084045d8a 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 59aa1d479d1..4086c6de02d 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 f90c7bda094..9837b5592d8 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 713c3ded6e6..19dc2fa2dbf 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 e63c123e6a5..1e0f368b973 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 23b4b42f903..5ff16eb4417 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 385b295cf51..2fd29f4fa66 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 aa629a06eee..7c64e48e56d 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 0d4cee16294..d8ad3d293ee 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 f5f2b6f94dd..ac66ae2be08 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 f6962473ce6..a9e01d34526 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 ac7e282c344..aa2df8f0900 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 66dec4fb247..c8010c4119a 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 d6d83eec42b..a93f1b4b91f 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 1d15158a032..531484e0167 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 10ae09c8c10..d19617ba30e 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 09a7548ef17..174264e11aa 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 fe9eb99edf3..c458d99de6b 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 5674db81723..5189024d13d 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 262679214cd..85cd0f6f2e8 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 6672650dd21..aaec1119f78 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 889ea1665f1..46af9f90f41 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 24801eac9cf..d608de7a442 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 2ae650ebeda..1e06047b202 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 1d062f9ec75..a5820af5390 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 5d83b25091c..7080e21c8e0 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 53e99c2459d..c4a23da95c6 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 1d6d85c3523..ef0beefb065 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 dd80b514519..4c0bacd37a6 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 c96edb91c40..dc60cadd137 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 bca218627fd..e91b46c435f 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 e43beb28fa8..75333c6c790 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 c26f8c9308d..cbdbc673f00 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 4753217b2a3..49d87da7561 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 93f3be3e82a..1f897577c1f 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 a58addff01a..17601b18409 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 8bbc1c971c1..ee1a8bf2e63 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 63f1da2011d..267a22c310d 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 1184a73d820..f60b2da6376 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 0b55cfab11d..6bc1f8fd4c3 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 63309154ec4..0f78ff69df7 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 a261b1c1c78..9f598010d35 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 f42d07501ee..8b4c971f7d9 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 50046e8306a..d2e4f45b345 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 1aa51387953..965205c078a 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 00000000000..d248bd0483d --- /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 e14628cae46..f8020e684e7 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 48302fb8005..b236a574402 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 51fa4d971cf..9ee96559553 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 c499ab66f55..6bb41245cd1 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 8395ec94d58..5ff8998c5ea 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 b9e9edfc347..d6ca494137a 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 965517fb44b..9c295b3463c 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 83fe863e341..d1f8709738e 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 86e50afaeaf..6ba8f8ec13f 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 a19e391af91..6dbbcea7859 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 2e1414bb74b..f0b25ac04bc 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 29c718c2b2b..b44c30eb16e 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 269b82d2e8e..fb49d4405fa 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 d1d9d0b4a03..55ccabac371 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 8acb2287f4d..ed10133891c 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 7bb6688da45..fd08bd1a082 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 abe8a59400e..41df69f790e 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/87] =?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 840eb5c51b7..ea21259bdfe 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/87] [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 881f03cba40..29eac88cff0 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 f70f07cafd0..86e81957dde 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 874a523e5e0..1c7fd534d03 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 04dd3d057e2..84592d726c1 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 e7dd39b0e35..437d099a037 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 309df3cb22f..0eabd403f97 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 343a13814d4..da9a360c058 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 1ce74c99ee5..3833f7d5811 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 e6797b6218f..8133a2e7d91 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 9cb788d40b2..3ffb262d4c3 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 2fbf2780900..734f5e999a7 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 468557280c3..a5529268478 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 3480961bca0..907ca9f9eb3 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 4f18aa64db0..300a36d380e 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 f1f7a35e6e9..66804e9e397 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 f37ef9dfd6a..e0f9afbc77e 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 5cff4e2d41f..bbeb0d5ef33 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 4adbc0b22e6..94344f423dd 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 2f58c0b7a4f..4f5a92c826b 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 00000000000..b1f6f3da488 --- /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 2809e3945ad..a5316610468 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 a5f0ed44fe5..927faa8131b 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 6c47e85befd..7f3c51d67cc 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 3c107e715e9..403fe1d9f7d 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 8d8e5968079..8a27c9300e7 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 34b8f46ab45..5369963a98c 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 d96ba49182a..3500ac8ed97 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 817aed758b3..409b3e585f3 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 ad61f5bf921..c6d9451b655 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 e94d70849de..b49596dc8ab 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 7d903ca057a..d657319edf3 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/87] [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 29eac88cff0..bb471a93e34 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 b6a0b50eacf..4ba1f4831de 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 5f658828467..49913acb82d 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 2fb296be0bb..126dd7e7656 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 ca0c9531752..31181590bb0 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 1dfdef9c076..21f2a76c4c3 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 36dcb709011..49ba9cf7851 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 b550eb60cef..251cfde0382 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 0205b9bb507..5e1932208ea 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 09f2e0a66d0..f57313622d5 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 c5aa762cfe2..7f8b09050cb 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 3123b1bf7bc..d5aa9fa3c00 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 f218e4a1f90..b2dd5f9fd55 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 195ded5a5fb..ca30c4367a1 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 adf65d11288..d2f6dc095fb 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 297574953a0..6d54b6f3274 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 1b72fa94587..68710e83c12 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 848ed6dab9d..0d360415fae 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 0ce96fb1a83..fe25535f3d3 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 867bb05ae27..7cef444b0a2 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 7151aa5f977..51cbad8a19e 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 46b2ba27491..2f5f713cdac 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 eb8181d83da..3563c0d396f 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 c06046b7fea..f37e8226730 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 8dd194264c2..4897ca8a1cc 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 c4221b7c57d..cd9117420d6 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 bfa7f10a2aa..d92bc0e4081 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 0a0edd1b676..68087ff0234 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 2a5a6628f6a..1adaaff1293 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 c25378f8deb..29073bfccc2 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 d9b98c4efbb..7503ccac7b5 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 bc78fa31146..afcf3a3ebac 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 72a7803f959..fcd9641af55 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 c087d410963..aba8adbce96 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 9cc88898ef0..9b988b4bdd9 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 bb3777ef979..2674b9a63ef 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/87] =?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 ea21259bdfe..829996c91f1 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/87] 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 00000000000..f88a44ea308 --- /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 e5f807b0ce1..f539408ceea 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 194bc30dcb2..c2ff4ccbe77 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/87] =?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 829996c91f1..9aa0cbbcbbc 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/87] [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 bb471a93e34..f12b5c0b2cc 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 b62946c86ec..6265013fdfb 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 7b6933a207d..03ab620854f 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 5ac00f01bc9..21ef9f0c0b0 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 bc306571654..8fd9f328d19 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 eb56a8fdc18..f970cec1610 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 1b948d2b15c..f407ba5af5d 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 0163dbd0ebb..887766670b8 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 bd000b919bc..7a28cca55b5 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 2cdeeee7baf..489360ab024 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 00000000000..502555635f1 --- /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 00000000000..f6489ac45d6 --- /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 d880ef4fd88..99de9f3ae6b 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 a96bbb77cda..5241b4f706c 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 8f6e878e3c3..305a8337db6 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 8477ba7738b..b8ab0e000a2 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 fae718ee587..1c7a8e64c45 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 059697ef179..abb60f51a17 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 c815aee754b..a4ff2b6266e 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 514d0bf71da..ee77ba766af 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 3b59023fb83..fced7dae288 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 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 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 b3454e426da..cffdb38ae51 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 00000000000..8915a097be0 --- /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 c70dea3a35c..77fce6be8e4 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 d6f081ba401..984b23dd0db 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 4f7a434a9a1..499232950a2 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 5e3c8c68d5a..cf7a8b203fe 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 3a200627f00..b7e98c9b1aa 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 8f910003a17..263a5f7ef79 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 37655425b07..d5aff8b7199 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 f11eff90ad6..904c1e6d4f9 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 2e6e2649f55..2218f0f1cb8 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 800fc9cb709..cb19f28419a 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 d53d1f89d4e..a6f46ef814e 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 f964d2e497c..c8526cc0895 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 00000000000..51c7e0d00ef --- /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 393e52f8c6f..b38ee9d7ecf 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 b2239185ebb..d76ba2dae1f 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 2674b9a63ef..0075e4e29f1 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 136ca041ede..e8df30143f2 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 1e8323848b4..d271e92baa8 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 0720177434b..00000000000 --- 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 1f06d75eb05..00000000000 --- 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 eb1a34a4b0a..00000000000 --- 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/87] =?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 9aa0cbbcbbc..ee492ba9c5b 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/87] 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 c5ab384436c..2cff64bde74 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 3ec0935d8e2..a2b3afa1594 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 2a59cd20d67..aabe8885174 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 00000000000..fb541d5b1f7 --- /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 985b9ed68d4..98329c80266 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 d508b9d949b..33aa2269d8c 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 0c1a846dd32..a2de9f184e9 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 4e071e74560..23db4e497de 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 2af7c3d01c6..d334800e119 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 d30c68eb1ac..d8f962cb700 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 00000000000..91f15efaf83 --- /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 b3548cb2071..cda13bdc0e4 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 0075e4e29f1..2cb8f7658b0 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 c9649f28a4a..eb82f71dce5 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 c63f0bf1d99..d63495a0354 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 b1c103ee959..cf6a431bca9 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 e7a9fd5b7b2..a75c3ead7d3 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 33806c28c3c..82998acefbc 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 00000000000..16aa9f6dfac --- /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 00000000000..460e4eb0399 --- /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 e30af75546a..9ed7c57b370 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/87] =?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 ee492ba9c5b..21d1d631906 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/87] =?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 069aaa47c42..55f95fe594b 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 3e08dfa0287..cea2b43bc14 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 a93c4706a44..24fe188f338 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 245eadbe900..4dc70b4f7b7 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 afb0070d1ec..71059570670 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 6475d192b5f..be9d7200575 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 03a509fbc26..17196956848 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 4b4d9a0276b..96cc850bfa7 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 41f8c28073c..390d5494359 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 95a42b50734..dccfcdb4d43 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 fc853b72783..77d1bb02e6d 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 7ed40be7ce8..0edbfcb02ed 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 9aef6f6da9d..e9284e90172 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 4f695e35317..501088a9be6 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 d61edb0f0a9..e73f8437fa9 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 5aa34f75ec0..0f7e705b5a9 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 ec8e171d065..7ccba02adb3 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 c55f1a8327c..33a8daa002b 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 27b654e6bc2..07d1bde8dd1 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 73de0d1170c..35230c67bef 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 d27e95d04ca..8fb6ed8f1cb 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 1d9888f8ffe..93a5c5ec02f 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 ef1884d8bb5..890c713466f 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 8496c31f2ff..0deb06d958f 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 5b9a95834f5..576c4896e37 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 f3b0f462312..00000000000 --- 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 b3612a7d018..ee4efa73f77 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 e28afa7ebd2..a7f859430f6 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 bb6e90914e2..d5e8c823626 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 ee426501c68..62021d50d81 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 558759fc89b..0f87e7c589b 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 4757ed6b4ed..fbd5cbde717 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 ad4c37321f2..ca52b3156dd 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 c5cba26db4d..eee11f9c920 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 42dc343563a..40d2f6e6e7a 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/87] update glossary --- docs/GLOSSARY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index 067f2bb0543..9c4a5d307a9 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/87] [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 24ea047fd04..b9476bf159b 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 284aa9a7185..e6b79602d8d 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 55f95fe594b..46e9b9bf5e6 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 86e81957dde..ef1fdd16f04 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 0eabd403f97..3f4f4d2af87 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 300a36d380e..c0850da19b5 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 bbeb0d5ef33..6f1f3b0ef7a 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 94344f423dd..afc1d9acb94 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 927faa8131b..bd4c4b00eec 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 3b593705e39..5a95fe2bdad 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 ad446ae4907..ac4142cb59f 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 6032dfaa920..afedd6c0086 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 4555bc03e0d..9d02907927d 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 d972d96924d..d249fc80d35 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 3f7bf17e55c..79e30e0e7fe 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 eba5add3d4d..9e6299cc519 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 d42df774ea3..949df759693 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 8fc5ece56e6..f7d7d112266 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 d608de7a442..a8b4c7b6cdc 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 eead8ed99b9..5bd780c1bc4 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 91da366b610..deedd0f97c7 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 b0273188847..a2894ad391b 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 264d3676a2f..4b1c4744c6a 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 a86b345cc61..3b88cff8554 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 b1a5dd3c49b..a04248b9edc 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 96b351d88aa..d3f851b3a2c 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 d51ca4d0393..dc8ae28a24d 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 25e4a6fa84e..7b723d11e66 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 8eea9cdd29a..9a4c5df7531 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 12710827cb8..7bc8bb87d0b 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 d46e75cf9e2..f60bcf23019 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 8c2d7b5e16c..246ea8f268f 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 ef373eb7251..cd6ffe8c986 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 b9b9b13a2fc..82c6ef9fede 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 d1b3227acac..9b65c98d166 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 b0b9a687500..94d7ae5a577 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 beddc0d3054..f3401d5021c 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 86f603ae53e..1d568d87e30 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 1e3582ea462..066a8e4fa10 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 8d77f51ff73..afd2a84c7a1 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 91a4997b73f..2b88e9a1d65 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 c30a0e45f02..e1ba28e7df4 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 2c11cbeb923..1c9353e7c50 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 f9a9b87dde7..e5f02e97070 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 f731496cec8..a39595f9870 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 f5dbfe0e5e8..527ca0fc15b 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 0c9a5f9e3c6..6e108e578f1 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 763ed868fe9..f2ed9a5b996 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 8a3c196c157..960f829d242 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 ebd10ab179b..6e14f52dc84 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 6423551ef40..39b73da2618 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 4936b522cc7..700324f658e 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 aae773a9fa7..97126febde2 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 ab0f37c45d6..dd8a79b7a49 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 fb5f5e032dc..a9dcb9b8ef0 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 c9ba092bc29..0edd8459258 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 2198afb48d4..ee1feadd9f0 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 d655264a779..2d56a072229 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 336125d4751..03971f3f505 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 0f3807efbdf..af28910e881 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 82661849721..2950e628cb8 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 c174b76f26e..f85b7c052c2 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 5b60829b54d..3a22e354b11 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 8eb5bb4221d..cd49f44e9cf 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 184a206afd7..80774092c2e 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 a5f904e2ae8..8dd35fbe385 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 09b0f45d99c..0552e3a56d5 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 d782b1df9ef..1583b9830b8 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 a3916e20912..9416b49c780 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 fbfb6904fb1..5cd31015486 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 a3a3be3a01c..ebc3db776ee 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 29ff0c7abae..461452811b1 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 a18449e9e0b..c58e982b25e 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 8756e90593d..4d0f0f07a0e 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 38315a709f8..a767b4d904d 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 2b3afcc9fa0..0ea53347532 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 f1136893da2..4df8cd9f756 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 f4727cebae1..0cce9959859 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 a781c8cc896..93a71611986 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 9b85ba6d9e2..984fb902089 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 a7732c040eb..c5cc6402c89 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 ff46766af74..27ff0297c2f 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 6d3baf1008c..61cbaabd5fd 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 21f2a76c4c3..67ef7d76721 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 32907f18ea6..1205ccf9ab7 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 29073bfccc2..2b729cc12cf 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 f0d0a6fc35f..4dbd264d640 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 2891e00d643..68e1cb11b98 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 2b886400d4d..1088485bc2b 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 ca3e9228973..55c45f70dca 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 b867bea6548..f972e9cbc77 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 0d140ada0e2..e38779d63d8 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 43690e182c4..8a40aef9a18 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 c0c67924bb8..3aabfcb0e63 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 262666713fd..6cb6cc0912f 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 e166e8686e2..0f7a5107817 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 8f1db1f172d..16a03e3aa04 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 dd537528efd..d7f4b3aabcf 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 36198176697..c6605f5e99e 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 f6c08c7e257..5c633e420c0 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 246de226a86..12e03cb2c1b 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 2f28bf27716..693f84617a8 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 54190841cea..b0f9474ef7a 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 a9965be1b7a..5460c83d2a0 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 0cd7e572e01..35dbffff153 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 4d0583b3d5d..6d020daa8ce 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 83749ce3cc0..7188e68b400 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 7990ba10749..73153147caa 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 4fe6224aeae..41a864d3e1a 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 48bf1ddff68..3e1ccb8369d 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 2c4009cc7b2..b3aadc8116a 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 3e46dc275f6..bef9eaac8b3 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 f20f064bdf8..098c9c742bc 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 bbf1038a7e6..9144bcd50fa 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 88f5ab49164..70c1bf5da0a 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 2d605d5cb96..9bc80f1ff3d 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 d2b5a8aa44c..2b4906076ad 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 48b93fbbf7f..1f69faf6c4e 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 60de41612ba..1780943446c 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 c897602fc74..abfd15469c8 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 10fd4b93e4a..8c108d69100 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 fdd2a45a8d2..53a6cec44d0 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 43aba67cd81..b5853172b55 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 e932bfe14a9..f3a125bbcb9 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 d6c153c822f..cd09f3665f8 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 1802e397cbf..30ebd02c599 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 3fd41ba2946..12f197f71d6 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 51c1b305b44..a0f4a986ef4 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 e0ee4839240..42d532b5bc3 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 5b00be0cc4c..80be294f94b 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 15ff973cf4e..718ed6366c7 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 2d4b5a12bef..0c6d14f876c 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 ca5d76b2480..8ed561afb9f 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 756c1fbe31c..f8a35309b33 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 280370d8269..bc7566559e3 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 eb944c74e64..79c664ac832 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 b09deb20ad9..cc471273042 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 17485caab36..c888cf10bcd 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 2f6cabcbac0..2d7e7d6433b 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 1fa877d07ad..49ef0508657 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 73a6663de77..4c298e0e1b4 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 55ed385ab2e..4060588bfbf 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 672273703fd..2827eb112b1 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 a51c7da9667..f37a936d5a1 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 3ac9bc765fa..bdf33eabdaf 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 8bcb79e5032..d6a501d0f6e 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 44e089fc0be..525382f9c4a 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 5b43b9ce070..8329f04915d 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 1117951c0d2..d7ec3614487 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 e1e19207c27..562d5f76494 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 206b9ddd5b4..452bb639273 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 b64fbd9da52..e444d1580fb 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 e141f64dab4..55ea525d3dc 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 3e53475bcef..cf0ff552045 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 dd5274e3ca5..f1ad93315e3 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 640dda57bdc..47cc6fc43f0 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 affe4d05d24..36770f51ed3 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 f74e5c7f279..eb1ac1cdad2 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 7e687f96b49..2e4a6c1df7c 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 8af83ae85c0..7bd29e07268 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 6d3f262af40..d38ca979cea 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 bcc79dd79af..f31ff04e3a5 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 75eef2b6556..d7fcf84265d 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 7047ea3442b..d32be606d0d 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 a988c5d9e5f..c5cd3895b49 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 afcf3a3ebac..61816504833 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 bb9fb147e76..f0e9448f149 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 133e97f8894..9c41579922b 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 fcd9641af55..6de9dbf7fc0 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 1c4e4713f71..f5160df3a28 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 aba8adbce96..88d0de264ba 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 c6d9451b655..0e45d6f9e18 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 4c9dec7a00b..8759af9797b 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 31f24754bda..683ed46c81d 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 645da33ba3a..a513b5e69d5 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 af81cabb57f..569e06f310e 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 dfe96b61a97..961bbc6ff15 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 77694c5e757..f983c886181 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 236739990ab..adfb88c8e9e 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 0dd75eb9a4e..0bc4d090148 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 f0b25ac04bc..f3a31546db1 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 9ee6188b1e3..61498c4d362 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 1eb6ba532b2..9c8a75e4a37 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 e6b75f8fa68..27c15968e11 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 926b2ce4d0e..4aed25dc221 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 62021d50d81..08fc2c07f07 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 d657319edf3..5e25c058c0a 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 b381437770e..183e8151464 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 b6e5fd5cc78..a6e8d41b53b 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 0497bb119da..279a8a70b81 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 e8aca597b36..1e1809fa9a8 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 c19a4a1daa5..35cf1610642 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 b22ff0b137c..43d72050d19 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 29cd4f64614..e9713f87e65 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 c2088c9c66d..09dbbfc8c0c 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 06bce1fbcb8..54351f78cf9 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 bd2a72e18b9..f2eee195de4 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 aa81d008f0d..511ae70c09a 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 b7f16f2a15e..0cd36fc39f5 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 ca11fce9dd5..60c7ca1c2aa 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 e85d5363b14..3bf683117fd 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 7652dceff89..28ab326e029 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 e95e456f2d3..eff4f42817a 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 dee981a27db..87d50ae50eb 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 cf9a3d85885..6a0d592c6dc 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 7928578a647..8bed86838f8 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 073b944a2ac..3e87ef83b20 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 56d614f6728..6110a0426db 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 41df69f790e..407af7da86e 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 155d49ed761..00000000000 --- 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 05e40e27d12..0bed5c70913 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 a8331b3980d..b0763150d1a 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 6f888b3d115..face198b787 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 9814f712f9b..1e5d978555f 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 e3d27331666..acce30c91ab 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 7c934a44378..48df0470a93 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 ea8d3ccc1cc..6a3457dcd15 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/87] =?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 21d1d631906..143dbfca093 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/87] [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 e1694e8d450..2db4a70fe28 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 6e94cc03191..01ce5564e3e 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 8ef172e39d0..fdaf1cac0b7 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 2ac007a3cc4..655e85cc938 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 9b9efb48a7b..29fa1e0649f 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 12165e1ac53..d5b03db1ee5 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 c1a08e1f588..0a7630035f0 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 742aa64bc55..4d6cb4db6e3 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 140e9934e8d..e20ba8e2e10 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 e730c6f4b9e..12e75cdccab 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 148127d930d..d2ea38aa422 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 21e19a4a2bf..d45c3375f41 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 03157255eab..c1e981cae54 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 9f64e132b60..e09a5de94f8 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 97126febde2..db28a32c5a3 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 0edd8459258..e1ca6e5a2fe 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 d0332e2f30e..96eaef2604c 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 36e051626f5..2ba4862b6eb 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 4004756b5c9..d31b1836194 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 7f4a459258d..cb9b5c41cad 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 55c45f70dca..a4981591994 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 fe21d845089..671cc04278a 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 f972e9cbc77..b260497c747 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 e38779d63d8..a6a5c89cca3 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 00000000000..5129e25c8a9 --- /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 56081c0f418..a196a4c0044 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 a127aa6645d..f160df5f31c 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 303e4018d8e..b9b7500ea18 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 269a0eb768e..5e15c9678be 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 eae01570f5b..a3f66684d78 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 4bfd6e63452..5efe4384e87 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 2b4906076ad..5d792efb1a6 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 d9796fb58ac..fa36682f08e 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 3a4368ec7ae..11ea87cbf70 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 576c4896e37..fbc86d6cf19 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 9c295b3463c..4567c325b5f 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 cdedbed75aa..d3703aa9133 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 d76ba2dae1f..2305150e2b2 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 4f120e35ac8..1fe0d60bb27 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 2cb8f7658b0..e7881e433ff 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 f3a31546db1..8ba09121e3b 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 6ee0e6f403d..cb2aa69ab5e 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 07737f633fc..b93deea3e44 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 1670d932903..d497ec8d854 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 b44c30eb16e..4048bd56cd2 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 f653b9c773a..a2ba649c8cd 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 bbbbd35a43e..78f87f579c2 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 6ce469b67f3..fbfe27a4b9f 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 03a9bcba645..00000000000 --- 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 069b60fa3d2..af3c0f2cdcd 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 89815348641..f9cede76a5a 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 b05e52b7657..e31bd1e652b 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 197291aea36..97269671a0c 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 833ed39ee51..005e14f7e5f 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 dc919bd6c45..ecff6f7e896 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 58f32de3823..8ea5cbc39a1 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 70363d05438..ba1c819d457 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 2c546f54c01..1044fce1858 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/87] [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 bf4e567b514..733c5303770 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 1c0b77a04e6..ff77b855444 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 e11b758892a..2ea42505697 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 98d75b530a9..25ee06c8a42 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 f2b87cbbcfe..f3e5493c481 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 d81b4549e15..5df2983a24c 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 92ef350b433..9f1cedd97d7 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 76b7f6c3ade..aaf0b7e9b34 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 6456adf4acb..b157fb7a3c4 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 718c9b4b497..941fcb0a0bc 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 28de215677b..fe5511f167f 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 5814a0aac51..edc559f1da5 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 cebd3a7f84a..782935d78d6 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 39bab0582b7..d784f1a032f 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 9ea7408eb1b..da1ec97e526 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 5afc353aad9..67c81e914fd 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 b29f3c551ae..80656dd8ebf 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 fe0816c55d4..e191fde9b67 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 8cd7db9b713..76a33205b89 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 6f74b620308..1da64b338ca 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 fac985b1e02..3f004870b2a 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 1d3e5922f96..329532433b1 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 5544cbbf6b6..0f080c853ee 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 a1a09f3b840..29fa2c8918d 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 8f126ee2c3b..5f04d8abb23 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 3a55a0c449f..1451db9ee31 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 ef715521c66..88dcf83f670 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 8ff5c58b76c..281f78f1000 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 0bed5c70913..5799aeccd2d 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 0c5a475d844..4abdf976988 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 d382d024d39..85331c49a45 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 010a0b50bb9..36fde96ba1e 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 09dbbfc8c0c..59bf647c63e 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 261394e5b3c..49284743fff 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 5dd072dc3e2..602c5f29e19 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 87d50ae50eb..5559b1affd7 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 e4592f7410a..7339f01d06c 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/87] [ 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 9971165cb55..f40037a743c 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 143dbfca093..081f1c485b4 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 f12b5c0b2cc..f296ce15006 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 4ba1f4831de..3e14b0d1b97 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 f615d9b1bb6..ef6fe471b4d 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 4e9742e81f4..33226213cec 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 6b4b53a90a6..704ade8d2df 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 b0cb777b949..22b3863dbf9 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 a612b957362..526f5ab56fa 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 7ec229839fa..d167962ab85 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 1d26aca1fdb..24635ed74cc 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 f79d724450b..246a49e2b68 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 09dee12c98a..32d35512464 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 597e8f36bbf..87da9a9567e 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 ca52b3156dd..7d0abda549a 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 97103a9c27c..6c87e9c79b6 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 390d5494359..0ce2fe07823 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 dccfcdb4d43..7e138d27363 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 a5529268478..f86578fe5ea 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 6f1f3b0ef7a..7fe606712ac 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 bd4c4b00eec..b06299c5575 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 da1670b6ede..08dc4a3ddb6 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 9d02907927d..3c8fea22067 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 3464d51cd6f..bf5282f115e 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 2653ee616b1..24d2b10da6f 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 8fd9f328d19..ed54240c642 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 f50e8be3a83..18ab2d27190 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 4659e55e59b..c1bb5ca22f9 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 87bb44fd2cb..898327d8209 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 305a8337db6..ea2e6dc2a32 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 403fe1d9f7d..684534fbcba 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 87cc0b0b1ba..4f100616a7c 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 646d2d822c8..9126bdad537 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 8a27c9300e7..cd326be0858 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 1db78988a37..cadb2e46fa8 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 e2d77e75417..0ef2d863353 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 40756c3d66c..81e9e7355eb 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 2ea42505697..236efc8f63f 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 f3e5493c481..7546ef3152f 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 88dcf83f670..9e06028298f 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 79e30e0e7fe..ffad1a4e693 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 a37cde3b707..ea5f5d83e68 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 7026f9e1028..347f38a4e16 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 6928305ed2e..6daeb33a058 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 062ece1083c..2132e92c5ed 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 76f4343f5e8..2e1cb9dee3e 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 71538bacf87..f6489b2f337 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 be5e02a786c..fbe591ecb75 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 f274050d262..c08d329b243 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 370313e1f9a..2aa20bde78a 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 20111b62d95..fad7a458d15 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 62c08ec0717..cdc7248c457 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 7470f853787..2560120c0a6 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 507d9d55395..495952e1604 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 eb9ddaef531..8b7f3333c8c 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 1d70927534e..5c34865d492 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 478f0086916..26aa609c5d9 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 3948dfd91e9..f956340a4a0 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 691824dafe1..791f0891745 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 c8010c4119a..055249b2f5a 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 5189024d13d..1c53cf48522 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 85cd0f6f2e8..873eeb78fa7 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 aaec1119f78..d59c4feb004 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 46af9f90f41..7839246ec91 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 a8b4c7b6cdc..47fec3621a8 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 1e06047b202..0831d2deace 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 a5820af5390..9d6e2a1b5bd 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 ef0beefb065..3e1dc425112 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 4c0bacd37a6..daed32136d7 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 dc60cadd137..0679875dac1 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 0ce02f38e0c..aea37def72b 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 dbeb6d2444b..7d60dfe9a22 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 38c9362a32a..b610adc2665 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 30a0a08a391..37448392c72 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 a67a3da48e6..6292953ada4 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 e1ca6e5a2fe..3ff87502f8f 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 2950e628cb8..fc8b8535e25 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 f85b7c052c2..7f72ffa75da 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 ec6a6d28ed8..af49b27cfd4 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 3855abd48c9..3d6c2c1f939 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 a9a97c9da07..718cd5f7434 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 61cbaabd5fd..d95364f4717 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 215fa17af78..e9a79622ea5 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 daf14142f44..f71fd807de9 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 5b3a5050d8d..d8ebc9aa3fd 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 9a3bdaa46b5..f5072a19491 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 0b60c9e8072..949d2f2c61c 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 e5af024c512..4e637653729 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 049997889b5..203c0b52f58 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 20fb4d71603..a74ec10b36f 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 2c7d551afc5..d5b122973e6 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 e66bd6beb15..9d729ab9ab3 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 a5790b47f98..f00163022df 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 2813c883e4d..98ef1609b5b 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 edc898d9e46..49ff859eb1d 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 431b0ed0ddc..a045a50b8a0 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 6d3b613cb54..c11514a4cf3 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 431008b422a..d5844d2ea83 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 4c90ed55aab..d926a0af850 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 67ef7d76721..aff9d6c0df1 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 8edca32e5b1..111158c6239 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 9a025755b89..76319ac15bf 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 b3a46eacf79..130069f9d8c 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 910e832bf27..484019a2132 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 4660f93ee78..ca67542a732 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 c2b376fff58..c6083ecf457 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 2d3968dffb8..69948ef13dc 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 ef3176f67ca..b488c7e5d26 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 524368c4313..a08a6dc427f 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 36ef6b914cd..f8f53b82aae 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 12e03cb2c1b..8554a660574 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 693f84617a8..884c066d534 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 b0f9474ef7a..225c72a7a57 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 69395a194b9..400d282cb20 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 b522674d56a..7981a42513f 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 0e6e3fecd3a..4007c33c336 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 ea8a79cd78a..2cd672a3eda 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 e88b20ce8ae..c7c6efd74fe 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 1780943446c..2870ac04971 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 0bda4a43df3..fb9d6dd490d 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 86ea64b37df..0a14d72ceea 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 cd016771571..dbd81aef416 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 995667480d7..8cbf6b59c83 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 0b11fbdffb7..21937b6dbf7 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 579f5aa2e68..a567cd42287 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 6cec82daccb..2c2cb798133 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 e4d1ee5b9ae..315f19252bb 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 18907761200..afe15bf404d 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 9c649d809b0..a51c6f231bb 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 f090e216a6f..1453c53d70c 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 a0f4a986ef4..2da5a28c4d4 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 872dc6afef8..68f548f4f22 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 a64dcc552f4..a635787bb63 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 79c664ac832..3b4904298b4 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 c888cf10bcd..5bbf861135a 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 7b8df96f46e..274ab57e127 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 2d7e7d6433b..c7b18fbfdc8 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 4c298e0e1b4..a560bb80edf 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 2827eb112b1..bbf3beaf002 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 bdf33eabdaf..a805ae6a363 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 acce30c91ab..1b0dd009e16 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 562d5f76494..7dbf2030a06 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 1cd776b4c14..ac65d0115e8 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 ddfe9e4744e..7da5fe3c24c 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 e444d1580fb..5044db1e0e2 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 42acac20b49..6241b580f68 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 f1ad93315e3..5e2fff6ff46 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 890c713466f..04142c251ce 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 47cc6fc43f0..aa49293d375 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 d3946a8c439..36ff9ca82c1 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 a7604e00db0..3dd859a6e22 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 403d92a3eea..0243e6779ac 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 88939d699c7..0e5b9116656 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 a9a4143dc79..d605641a53f 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 b05bc4565ce..b566cb2a570 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 7503ccac7b5..9ddadc2dcd6 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 61816504833..8a22063985c 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 d47e79ebe42..60f3202448f 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 f4df0427f07..4b7d224e015 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 683ed46c81d..3ea3663478d 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 0e6ce1eaff5..3b53de54647 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 c4e2b2b18d7..7de129607c6 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 e3995c45f71..b9270063d59 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 2b90254a6b8..949eafdb099 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 291847ffd0b..206ce7c56b3 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 4aaa7db0d3b..d2b1c0c0091 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 601bfba0be4..7478773c47f 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 8f93f05c3fb..db8ac41e831 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 6dd8046f5e9..7fd04ab297a 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 d2c416384a9..08447d093af 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 f114915e0d4..d823062bd42 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 94c9f24919e..37dc2ed5863 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 9befa16b226..2abe1e6f68f 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 28cbe3aa32f..3e0a77338e1 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 211b637ce2b..e2640b3dd22 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 b8ac9060f02..0ca18ee579d 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 7efed3f6d10..2635534db48 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 be2314a7671..aff14a48437 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 e49881bb1cb..92626f4ca96 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 3285f7d2ec3..092156a6f10 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 418d9310b60..6082cd34766 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 e3e6a52a0a1..e3fa4a471c6 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 b540be533f8..ecfde224056 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 4333162c6b4..11da527e329 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 a05248631bc..bd71da4e9ce 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 3f3cfca4f24..c9c16990ee9 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 df5affbd8f0..445ad050f8b 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 81ad4e36415..e272e0de4b2 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 d1d0c24c1dc..47fa67242cc 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 2a2b1b13d24..7d6f3940136 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 4d801f0ffad..74b4849fae3 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 8481742d047..3f81fafdad1 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 bb0587c3e4f..a8aefa7138f 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 15ea9493ae0..aa0045155cc 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 e9fca20113a..accab3609ad 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 cebc034645f..811c619ffd6 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 d32145b8244..aa98d29c9d3 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 a6daef5b5ab..48a1df6f562 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 32f56308218..e5af5564fc2 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 9f0c64655e3..00000000000 --- 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 31c67be3818..00000000000 --- 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 00000000000..64e57f0db5c --- /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 6336b640191..d43f0aabcdf 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 68f6cce803f..b653d685e6d 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 fbc86d6cf19..2b8fc01524c 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 e921f0608f5..bda3edf6637 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 6b7f65fad9e..7e36c51d6fe 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 c2ff4ccbe77..cbee1458703 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 e7881e433ff..afe073b3b73 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 98d048b3287..e42ca46a952 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 4a31e54ec63..9945a161ab6 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 a75c3ead7d3..cd7bf51b840 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 609ada0da1b..d4a1eeed408 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 ca655936173..786950a4189 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 c98b3e7c0e0..15a5d0c7f72 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 571ca256254..3919834ce1c 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 4e7e030c6b5..a7fffbba8f8 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 4048bd56cd2..c3e81d44a7a 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 d3d6f5996d3..a36142d3e28 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 d77d48de000..798bb1ab08a 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 8b9b80f90ca..cdb11a82b91 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 234de708457..8459cd61a8a 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 a23fa0afe4e..5479b86bbd0 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 60c7ca1c2aa..249f899846b 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 0892f99eb9f..4ae49f279b2 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 ac95092c5a1..00000000000 --- 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 4478a889dba..00000000000 --- 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 7931c517aa5..00000000000 --- 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 58ffb96f111..00000000000 --- 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 52455c93121..00000000000 --- 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 zcmc$_Wl-Er@Gcr7fglNPAxLl!?gVFXTio3Vu($^c?y|_@Zo$K{3ju;Vi!Tz~U4vfU z_dWlwx6V0rtL}X2n(dnD>EG;3_tVcrs3^%`z9o74?AbF+fUKn2vuCeu1m2KLbdLX?SMsFJkKBYPE?zJXfF<2b3n}%8H{)hoVGf@HmR;M@-*8UxfE8 z9`;;b>2AaI8V?BE?d0v6uhXiV8k)4AO{O`8tyAaoEO$-7!;9xI#+}HRNE#;FS9YBe zfH7{d2?eU-9JF_*@x(9YHT$>V_B3ann^~MIzFS#f@O$?y>VNEcYImcr{zt-jA^u;V zXlUU3f2Ci_loYT2m5$X>QJ?=S-QoSes)4@H4=l7-cQ#c+{;Ie%$dePvvu>X)0Zyb%+A+UZ0uzkkh{W|Wdg@vOz)A* z{i3E`hqd#TT)hMWugTef+OId@jKYiR@eqgk9esAFzH092qD#?{<-U=>bTQ@s`wvHU#JyF=1w2>yfU~#7jQ8zD_P}Ls^*VplnqB$ zbv=k8sO;8^9&eiH@*r%Rqr@{YR7^nKrw%Uzm$A7)t2^QZ?trT?t< zxcHsi08&(XxFvVcNnwWb+kJNA+jIO9!njq{%u_9=3`idI=E$c0v>tg5Bwf1QzDQX@ z2);1+X4xfk1V7}@XmfaL;UXmmwVZ;o963xRlcp2k(5_HfFMx>(7hI z2t}<@eLa9L0KVbd>@|DW5xIZQj`VZL@6K|fk_U8@Mcu5%_v+vBdVF}~Sg99IYI8en zg-ZsXH`W{Ew5M@$xlNqUh~D1n>6~KY44Zc^i}uSCTt15X@>2JE9o^q2VGb7Rqv6nQ z-CHbMWlr>-rYtp+*0fLpkk|do%VtdCITP+(6&TL091E^6y}BNV$tThlmBL61FVZI8 zb7H@YyZfYe$Nm!J`{kXM&V;|qQrvCh*cRDV=^Di&a z`$QMT2hs`+j_MD1vWfLG8#Nl9ECOn!eM>)BvoDk@>BZ_#suW3b<}pF0Jg`QwE)ubX z({q?&3uDu2D)p9QpZr!V(Z=rG%-Nf%(Ap}gg^=k4hJ6G9P3cUP504iph~AZt z4bdUExAGlNhZG|J$l5;k?9UtV0Yu{MN1)<>8eZK|L(#9|Ok&D`yYPnqf<z+rm4F+Ct|6NgBZNc!&l1vKc_w zaucj!FbTjaO)e79TKdRoVTxnMMWhGNG5tnG zBQ+8Nd_gQPOQom>&LkMW>bIHHnkXwPuX3O=~{A?mW;F4lOjgx$(3l@ZV*AuqOje+w+PSU;d+KlLJQ%PQlZnh-8K}m5?0uK zU)8yWUHM}>@B8qB{;Vv=Usa-E1$h4c4S6KVUZLN5;)_ZXOGG`g2Cy0)E3aVj>ui_z1(F#jg0 zL6_$h@Tf&$Ri`pkF*8oM^LhGQsD~=jBh!ia140wB_C% zXmVuXoaHAN(XCP{CZtU_n@n+JJMoW5YEku7hyI-kIJzZBp>Ds{T$;o!In)M`yLPbp zby@9bIGo_x$+5gl#Tx)J`By1v_JQT&dCa`VnWUgg%&y)Kw`Bf1iIG_zyv1=04)$+Y zlPk2gtP?k=CTuuKKtY)&ruSpw3Z=fs_^YX zq&c()Oa9rdJB%8c$S0I-87rPZOG_7@8mSfen#*{5jh<~uA zXlYVuQrADSU`3MZYD2U!vgFZI3yF*A{qw_AfCxTaY-}uLMHL+3c{S9xjBPeh;pq{V zi3*qatFStOvVthMX5CAfpGcx{GHhxIvlsYUu3KCDWkFL7*wDX8BosF{|K@WsW=RI& zsD1{?ZnfRg{k+!H!3x-JRXy26Xpa{dAktDX*>V0HYgS99;6gIOB(2HH$N=)OQ|Ss* zpBSGpbv)qON0bPj^+6$~jCo1la`%>Ay~u}b#D2xO-zpO9`NCPX3!7 zpwRA*%q;qLIL?Em7%gR_rsC=6Q(2)dePDU;BR;gvQ$=6Rf(tw}^o}M`D&9;#>L*x- z*BvwjbF-#ZeeH0f39rAxWhfbaP2MaW|NCM|sgvKcVw3G^I~wwVo!BAdk0!a?^JfL( z13T?mAy(F3{(gjS#X%K58&8%}^V0<^*5S3N{E8k|v399{v+$B4E3K8)sA}7*Iwc8j z00*H7&uUL>S<$A_DqF|N6QYq=S<^mymVet7*M1+j9Jx2*erGDRHUMlYXDfYNIvYmn z)wp{o=bE+W20puJml|pDn7Bn!S*Q!9bN2|CCbwB$NgH}onJlR5Yv|ijBx=E72^SCI z0_cV#>VrF>@{#ft%=zZSuEM?B71cfWHPtiIYRQT(bAfmNna#)Avi4A;@cQB{ z%6+rFo-=rfTGd+9#`~~^2CL1MC$Vhat8Pwo(&ZeI(r);;am?(FVo!vGEZGfmY&PKa;o$fv(WCig;7I#91O&Ax z$8l)+9shgfJzaWFR~}xsRG+NO0VHoFtF`)M(P-EG!|_rXUo*u9nj&A#N~cVK0C4J; zygvBOGu8SyCOBw?_-Y+ba<<`--rLohYc>i4e48#*H-CKE)ysIG{_!cmea!g`6P`k8 zubf|e58V#sXK)UhxdM8a+(z9jPD!qkCy@t~2N-1^lI~JcTSVNid_!ofj!gJj%p5V) zC>l+1X2dHLN<_;I{?V1%X%L*#oKDdsLe%+~(m{172zQwc)66#wj>?$W%!u`EXVPph z%3&sJ@o%agEXqc^-(9$myqINEnr$LcP zNy)_0rVpvljHZ>FO47I~f7m_OYECr@C@pO{ouXs>CuqFsiBj8SgpbSa#*no5{wY

y1TIvlBf`NiBFJw9x$S%^kPIX_r-8$9~_Jj-z-6TKJpP)brVPesjr*>lMEH-&}v z_R*hGwWx)bT{-#WBX;7%Ij@L3xd_~Yf*Bm2r$j&$Np7UK*^;GYXhFTyCqE*~yr?he ztA}Q*Shs!CX_2$5*_M#gMpfin6=%kdx~dzM*Oc^tPEkj6E^r_3WZ{}}3Dj#Tlbfmc z%C_lBor?5I#3``a8{%S9BuYG=J}vBFmyBx@GP3)iHxI*>!*hX3nBHzCuAMV(Y(Rzk zagaa4HR=zNIe?BV+r$;>4;w9*PFu8vLlhT{F4L!{H*eA#fcTXR=MB_y@`KzpPP%yj zWZC1a!jH6D{-ge4`kEbcCcFem$p%RMSIjpgKsS!Pi(=+RaaI=2%$-!M#c3WQnmR8I zK+7DJV&SN8(xp7-cz~D(sHOa>ta{LWTSo(f z&R{uuZ!scdO1J|qWV$DQj(u&bs`5zqb=>XDAF6sd5r7AGpIb3Ss#y&Nsnd|pZ^!KW zAow1gB4B2{19CgD0~NPC**RC^LvzlsT@`O5F{Aq(m$wIg3Uh+EKHmh?cqAmTVyCl=G@WZ^oDQ7-2Me$_q&z|!mg0bF z;u!7~(u*@fAw=*9KcBF-aPK&NsD<0n zB_T8J??u@6l)tEy}Z4%7Hwe&dTFXSDeV62c8rJz)B+?+VTs~Zsx0{S>YnK z8dUP>9lpWHwnM{fYN3p zze_97M)N^yF#$Ws;CYZb)k6D&)Sse36W>HsOlMqSHZUq`I^W4WxeIl>@A`i2r+RUT z+4ijcn}f_Z?-;NdG%Mxygss6;s?vTsrF)O=^+ax4rRFU{--rm_dlS&wM%4u1&)YG% z6`!A*^CMnSIlhPz)9QFt>k_IUo_HRBLQRt|XfTX7$orG){-W6hCtM~(<6x6+I{Eft zX8$!}Gi9-oSB-J1Tqv^%)~RC0LkcR8jYpX7w@2F4rt6~dll^r`No(%mwGi=#ON4M} zrjTk?s!_{{G@s$DEHtfq>&m9T}6c2@Ewe13IxyOp-(&}!=mj~hnVp3Iy~T$LJ>N;I{wv`sU4Rxaj8eoVlX56G{LSkDFezY(3{sB z_kPuOhk=fp0gKNKY8;v#O~HSbS|E;JHY4(%FG~K2vvfzNeQ%VV5r1b!hb&bcuq>O} zd%gIY0>3Px0dgdHlPh_yrT<`bg=sh_-b^0GO)R_FinJuXPK$MEnx_wvb*XPd6!q&= zXt9_^Tb0x6 zMeO*IE#X#v38NP#V~XhTfDyjIK|Rt@fKe&Y5B$twfTSd&^O|m_aE=Hm2z~9&MbFwdO>RFIIY2kYgqoC5 zH>K|%7A>bpRV{<3WKyCzSb(3vtSsr!y(AbT3WLU%fQWe=chcgGUMHZ#e%Ro=UDKtZ z`O*y&T@maT`kij;;V3?nZNA%Y!_`be+9?$q{X`J{_qpT80e!=3WO;Uf)YMLS@~hE% z@sHV-zT<7OFb3OuYvSkWg`^8ft zyStLjZI}c%%3r^Uvo;W2&_?aZ7qAFZ{xwXy1(JySS1W!Zb~y>YO>8P3f(pKiQGbjwN>D-V7H7T+dCsN%i*kZ)y5w z^BUDW(wx{YW`6RFg#xP81*1+)F0N7;d?WPLa$`?#$IsW&RZd`OEN zU{p3ds8@FS*FY;MsCuE~Q(Vs1OIR#>sht|{IDf*vi|TJequ0WBnesExZPXu`ZQgQ^ z1~5TszRmCfm8wQi<`?SxXsAddLC{tQpCKX7d>`=@mZxmmRzDg+aI+xhb`#lRQ=;t6 zg9@6()g@doZH@WwH z$+OqKUh?XbHZGl&yq9q%r1JByf+%6(#j^>!;*GLJT{Vk5*HPrMc51e_&R4FD4S@K! z{Kt;D5Uzy7SG>fE1S1DuseQBs=Fqz<*=G2QZ049=Z}K24@=|fP7}MRax65%mLSBLF z^omtV`nKKq{8%R*xz#vQGFq|~0vxYtbaPzYW+RvNrlhhZv(JaGwCG5yWUvH3O`(C& zUkl-dkqcdUpbex~M`3o&Y}*PhOw;0xYF#>HDbu#a-`xdBD;~fVcI?Y)!E)`4;{v8R zYC;Xy(uQiwQ<@I-2_00satA@vkQ{HjT-og%B9N+;h)}arT$k|WNAakL>IjIQ%(g%p zeVX;0V}25&+6~(qq(bpDjhwrY%mQ4`}yPBRh0Jg`IN!ORb|wD|5ayIl|S49l6T9G8;URq zb|+S`=)c4T?h-bk{8DH80^)kQ7VbESKHNGx*f*W?a>31^SIL<`ozXU$DWJyKL2d{h zFfyy0vGUYgVN(|l?i=uC4WBRb7*LxK4yaG3O@ya7UYFrz?1SoHavt{)1m{4z0u~mj z`T^N(T{P8v#_Ydcl7|LH#r7!!GD{L%w*aaWf=^RP}9^kDWrB0Xa*xen8+v}rElXv zy8NvWP;uG%WqCG4p^oz^y-Y2nDUy*5%oC|kwtM>@K+*PhX}2&+pmxXHVj+&Rne{Cj zzh6VOIv%fuzqEpW^4WEcZ3QXnDkKl!I?1&!^5I@`W%an5Fz&-o92lSfT%s|8M*^9P zL|L?2nmme>_bW6W>8AAq8W4V_U7qIpP!{z7Gg z{pCG>eFjFRW*k3-`3rA(=#Z=Mm-uuDM5S>UY5gTn-0wcrho>Md5wFOi%Gj$P!evl! z7$6+nt>PS8MKWtwfsxwq9TJ6mdR`eFNhk-uJHch-(qS<4F5NB%XgyPw2zkFyp_IBO z^0AGF!6-E?X$d!1wfI!)?O;z+Dj1qjS)j1_(E7IM{K{zOm_9jqndwR-Y)?>ve51aF z7}EMCn~-^~lk%%anoDF_#uCAD0ioRH;f;}6?M5+6)kng?~~ z&Qe*zY0b$SZ%hUX3cl<4;u)IA+qRo6@D7_0Zd2p;Kb3+m9+@|~VW`K^frpl?gIWNeY;n`>)61^FS;mNm@8`_96D!3C0+yxFfB=Sgj znK*w&)~m-9Ng+4bEK9a3)f=aGMV8tYQ(Ff()q3=s+**ZrGuwXC5(?ipG(AS}5-d)O zvw%x8w3H^pG<^_H{4v67x~USA}4tT&aX zX1}+Y058c)=gA1JoW=5~ z3ok_U8PHoAtR%&0vFPP#j-wr)E6zr5gx26)J+6E(aHC2eWF4xP!cpv1Ol=#bwTjFl zu8>(!sm8hx)_lV1CjmLWac*^Gjq7Q*v38c08*1DF6MZjV=@|UCo>dr%yfki%oP_Kg@>r z!yzpJ&(G$(oFB`YdATM!=-_N0i6uSop%H;U1nDph5)`zb%5J+5pLhS3A*XJAu=_&x zXLzk|UhoKD(K9m>-|5QUL>|}g-7K+;!jhd15Zyy4nEzE5UwZE{12fSLS;+B}#`iYY ztJE%p^MC8;ovtHvF9)RLrtY0Yt)L~0O#}pK?;=IOzwL(r#6Pr3+K&4hg}1`K8Xl68 zB7B^hK_r7lWZuhF>|*tf67I{FiWbVEV&wg^)6l%iGZ=!i4`O4d-ureF`|Dw^2*!_l$jW>5P4YdmCz(hXS|78 zd8eF)!8{b?T4VL%*{@-Q_lo0Yj^k#S7_vF92EL?ymU1Rjf2eSWog-s-fCZCw3wc;8 zy|tCOHJeLP^B+RT*gZ7jT|Gp9ss#tOLxY~8fiUI9l>Wav!U^^N)RkDIP!<<`MusrZ z@%z;$5Wv&Dle99^TcxHiUlJ2=Em#*} ziYL-aqb#LQICjfHuPr^f8S+~FIA6mUyy$OFlszKozE4{c8g*1G|RT`i;xPUqI+m2278_UpFz@ z0x{mAZqq&$tbeVAK{#w@@N3wpj7C&1+|AK8$}OIfTa@f#Iq22?&*oU^l5TO??b3*3 zh6yc(2{>~HY~*LD{km$HrKR;M6!x%%k4o*5lJP*?pkh}QB&+cKxX#^T+#9@Yp?H)D zmUKy>l_@%@%bbqvTzx?5^gKnDpLDE5+uZ6k+f)%A=~Q02M|?p?@r$Lr^BVB04e3Jj zjS_P&|Dy7@k83oW&v6LuFAr{hXDyL(?fB;UAhj@&7)5NzvR*Uk=YIsd7VwGQ_X!z? zrQsQ5DS0PUX3u2EP$D?Hw>jG?H$E2!PH9 z1JyHaKP->)q8(UPrR!ojk}KKFjmGn5Qm2;o4f*6#GzW3UWYzg~Z9!WfNl{Ybdwn$8bCu_FEBlb8pZu~4?>}kseId=M>?y&Di7^O04?CdahW3))F zdhH2F&FLyi7gWO<#I4q`wshT)Mq>v6*}*;)hIJR>vp}tqZtK#iJw`^x^8yTt{jj=k zk9w@YQi6&^d^9wdh+JNpn@Hh}l-KATe_{9n=Ds`FaQ&1vW`hKZqzFm4Q@n?or7JMS zz2vH7IM#usC%rz_@Z_B`k!IysVF@OYt}UMR!YAMxkmAi zl;=0q7~TnK4+Y{Gl}}}IfI>2WW=P&9i>H20wxpuLrO^A?Gj{i%@1Ce1#`Z8_jZMKV z1VZs?w>`B#f^N9p|BC?f$35i#o*p9F=kq_qgol6p|A24uze!8^|5t4?H;|oz!g^KG zU*%88uiRg`xfD-3{Ga9+ke!`vkZmv{u&7kh+S*zl;O+Efc4~_Iv*)U0$M0XizWgu6 zo&<__A3HwvvzDCcg|8tYV{EeQ&Za8HfhzPk@!tXipZ$*>gTVIM$u)IO!0^HDA#HkY zeD9WWdSE8n|6=cmkA3@WtV?i-7G0&g$|EdS(h<|xv_dK;f zCGX$=XWcc!tB`-Yp83ChPWi7Cm~+ZPvEEyjb2IGgd_*^}q+z7x=lNJ0=59LKd8GF@ zSy%f^N!2De>e}(dY?7bp2v^-raQW^>ag0So$;V8DrmludQqd9x+mwYFu?HghGT3>a0cEbqyR-ClKd)sGRhdk8rG-SB zFj}*u{7$?1_tQ__{pBW%Nh;Vu*h{aTvTIMN>dRz=(!`rxX_tQ^(l|AjS+-j+rK(U5 zB;urF&9W`!cW}EHB1vc5`~4^(Ib9$S(m;qpv>pqJYI8XVT(mNHOSx?6lQNB62yAG7!F zV2A`M9KtFvlAo-)0(zs5OBTMA)cy|DmBFBNhjofDi73?n*nW-8HBr z+6uzkL0btOC3vE=X29vru^VOihACdbABAfj+LR`X0qb8YJ*6=2@dL^|CatX%Y%Rk$ z95dprD{88V8?ca-5Hp+7+Az9q^s3ut2c%P+8mmq`2_aLl$2bV;{TS5c2FIrn0>FKp z^^^IMA2uB;`C+Y2cW6k^(}J&85@z0f_i~!W;Mwg_wpIuj>~O3S)h`!bK+uw~`^iSV z@K=F5803weMO3BpWqudIZ@3aLiy8H{l=_eZf!~3(QYWpArXViPlS5)Gv$l+m0AJ+c zQYJ*D!;x{*zoC-499rn992_Oe;x;v)@UWAO6se~4L-j?yJbLaQ&EXov5qp?)iHBGT znp(S&y@0@xSFn~CEEO4bS7|)iXiPv3Ii5wyyM4$8$wijRUcYcr&Y_5O&O7j6+V5i@ zPQ?#0;Zq97>Q3+CMm4TRkB2a{s>hfynm&Bt1<{be2KRw1xdAJQlUnAKLyi3lf+xK@avb<5Zq>pbe$ zSSFO>`pC+%Xke>7R4-F^-METAEWtXzJ!f@!4l1K^|k4|I& zBgX=3aY@Cs;YLHyd4GH5>%?I{(e%!f?~c1D%qB+(%6&w&_e2(th~+0orhe>9i9mba zeYWmbIUOgmT(P`Kq-fXE`W}Ucq8@5h?h0KkWshqS2Z= z7e0nB!m$4Ft6G+Kzsn7k8uQ!_pR4{o;cCMdIbnOQK9Rvi-eaHp!p}jpuWf$R4Cq#B zNRwTxU|f}LUK`^*U-@-}H=x_x3i43qFmUIv?XOAel}7t3{@G9QTisP0^am2@{qoDr z3v{V?zuIa(o4;=p`A@UCmjTV(oQ4|vb%!-(j?W$J%57Y_eWYWiIkkM|m8ZEc*J~q+2tA8yxQ`kO~zaBJpd%qOZsdE8~RkVPie zlT$_Gm!2{KkdcbL+OFSVJ=Y}9X6^b1%uN3R+dEHPtji>~3t=*}>SFa<@FhQX#_y$k zV?~G2Hv{wA>Ph}%x(a%ZC#M>ppEJXn+$6d`;UWIeu4=!{sOk_3nbF;vv$@@YE^XyC z%^*&G`JK!l*6dJT8a0kn=DAEI-maDv-xqPe^zr;Tm{9LMMx(t<7|b<^jQFdHcrBI0 z>|4qIiR4~gy8q{7^zH8h%o5>c$vc}XiyDny9ETW8Om4Re4Sh??=yymehKg(~bi$#U zYSBA`7{U!)dH<^tntNQZYQcrDpp{XQ*|hON$<@(EzKN7NZ@KX)(9dhdy$=@p`U>_7 zekXt{2D9|9?b!|j|6JLk@ojY9-~TQ{9Nc|SgmLiPaS?Nxc3DQpkRNI5;^~)>#dO$( zCSHxFZJ-&ClZql4DjI96{EYdp^%W`|LFfyOvH3UO{0F8U_Y~*YeA!+TkZid4ElY8Q zl|)Y?yUa9;iYKJLOSw2=*v1$C|A1!^#Kw`?e#?{f~;op$kB^ivG+6Ga8YnbF4j zK`!ie><6*KT6i4?OL%EIy?=H|Kkmo0@+`|5e&37&spq1oPO7{cuAlu%P=+=4(g~R1 z2phN{*}_jLlv}J2I*VeZ0+QINaM~hPRmg(>?_T6D?TR;NclkeOe`@<1WS6{JTkZUN znL(>OLyk+IXl^}95VER5Ip=Y^sK_1WgKB^oewCv%@taL4hTp)pXdjOlze0l+?Z7;m zgF8&ARvACWR!|j0kBr+ z1-2v-Ny#Uw)Z@6EP!hXYOUxD9$_g9#;i0k8zu~0%t3*XE4_v#}XlM6N@q>jW3jq(= zoYCSJk0RRf?zu5*kI%tjaHXdUK9C$^F%V!&sP4PM1*LS&Nw=LfuyP=bTlkSDJ$LQQ z4J5CY*Ui&lYd)%R?PergCY0y#)%E*jVd0?qqoj!qbNc4BQ0yO{j`3qs=(-}qc3aGN zV|2CUz+|_EqMM?Gpk9@k?hl6nnzG%Y%X<(LCoQ+zmo;~cTNFnS-%sjP>+5sG;)QBn z1y>o^byv>UhXKXz!n%TAzd%24vXbiE%ot_VX&Zg{Ap+7HH;>Gi*FOQs*D=)f$Z3V~ zxvcvHhEIR!d}HQ)AbDH17@krK27m{4S3kXR@BPl1Ld=x7J@5Y$s+7K?M@QL#(ZRAv zR-f3P7Un?1dtliKImvgB*?Wyu?Nm`?Q)p_MMC~KzZW}Sw4fu@CVK%Qsz7)V=0~lqu zgC<|r50iW&e$)}SU{xt*&0$;TWQ$~BSu~gtGzD!zj`yQc2D#DG-w@0k%(WoShK!p2 zJoh7A38=MDLZpf}) z3P=oqI}3*0zj))Xzne%@cXAw*Na#B_nUhC9yvQq?EZm9p4zWRe`Y%Cxb~nZM4mH?Q>Hk)Ko%0U}iM^K0MLFMim^A3y|108Y+s`Z6zun&5|4YPY zV!9u2RR2Lx;+F>nJ3Bp}2HyMk?B>uY7qw;g|DJpt2a_}lhj?@=KBW}a*PyGZ;s@*a zX}1HtwL}z7Ph4HM*Q>Ov-62eb;R5BP%2_VB4|~tuE|bf~7rS0`S|a{Njy}wD=TiJ@ z&!5{ZpD*;P<{RracvSM;^K^z&$Oyf;kY9&(D32R%se5ww#}Qk=D9Ia6k!Z4wHzoE! z;nabW0JyeGu5oY-&HUQ1x5CNn>3c1=8P_;Ndspn`3Y#Q?s;m}cvFmgCPH$*c)44tP zvhKh~NuuU^-!^pKtVzB~qS&JWDcCCT@}KmxCahV2x<8{5f2W@V+oF6bCDp!$-Pds-#~7%4+OZ7BR5= zSm&$;X%di6%A%)ae!V!7Sd}8}y{0yt+FZZBo{RFPT1gI3r&$4v;Fz$SJQz>4zDR8w z27rUr7}jyVagbKXH9N5KUY2Mk-t7TNF5oT`Hj`;~@=bd~W`kMSoaT2XqtJ1d&BpTR zmM^_O6-PkcRq+s@X7!+A8ai5%Bl4!xrMM4JCJ-P2wLp?bf|}i#J~}Rx7Bo?G{EiT@ z=kWrXtR$gZytuiFMaSQOZtC5(u3x=VF3Y-an9AJU@6fR1p&{ zcyZ`cP?N>yIVI-N+bvne|HFKWbK=h~dZXgvZx2=g*nL{XuXbhs91QS4J;G>_m^{Pf z(ZL}jWs3s4ZPbOj_{%?imh-cT-FWIP2i-JQo9UDTfl20!vtMuBOv4{k7B})fDHiUK zF7@g0{p}>2uah8%;8@FxZe=4E=As8BvkrR+Nbm%0!}RNG7QVK4*#7ptnD`m%=Y)T- z;L%sGsxk~73`U}NhE&cN8gPqtT29XB^!@xRDzCgN4fE}o?J}pL?Nxvn zPp4c-T1;4kI&F>6jqKm9bKW=drQBHcCn<)>#Wvd2v~&6!3gL9!}C!D5od*zW-GEsK$wzGLbgW|QUc>%QOVXT|DE zUBbu&S1Fkh?B(WPWxG4B-LFiz+s}?}%Api9YS}|vj+-F|9;ju6Lp;!(L42hAI^G=R_Q#%nXa6-S%>)$RrO zW_^pZ!jNW|Mxi2KExpR%^%Y zKb~jY-n>T!1_qL|-4OQ^9(&%|9g7HqwO8_It{8^`LTj@ivVG1OFbK2|p|sb1U9_V(rld5*4tHLF$O_d0HlH)=J}> zH0hqu^Y4;45q*}$uNauPIHPGsKYciVX%;pA!cTX=k!M*1xZRdst&WRtXw_29^|cC| z+{c~vUO6L8(xV-^E9y{MS$vBOHXmSE$(z{sC)7)zbtbUW2kl|%-ka+pImAr@sq-c(1B^sFjZL~>q8BYcj82rR&P zb>M?jf}K?l)y2}w+zBW!brAs!ds$pcWU%K~S6zOus>=SV9-9h{h4fVvoXGhlWny*R z@JPM!tj6*_Dm(3MVpLmw0sn|6lPEo@$VY}%QegH++2iVp-4_1|T;?^@B_k+!_?g$d zB0r!$v6K2k%xM+FiMoGojxpjaYiX?ga~Y#NOFhtrNujH!Qu4d38nsdF30uz5{Vkmy z&w4Rt3thb9;ov9PZ;qCt8SNVdqnG|qkMaKM^iSk{F5|M$$q3;{)+F*qNE5$HCe5ll z-v{vnCIAD_G%ihzNe)0hedK$|KI+akvOa3+?DyVmpn#(9Uv(8y+f3v&D*D9}#KGTT zK|?2rlxy#_reiEWPnUBPzaTr~;h*T&p&Z87B`cc09`~uB8Ugw;=oSNh>@OJdJGO?J zd+#4C=5y`)almB!cdOzPi9XC5Dep#gn9b{+a_-y+H zOLy3bW$|oM`LWh%jBkQf#-J;9MhRYj^Jnn!Aub>GTEKX%S0Mp&A#LjJa^mrdT(qp* zJdqG5lY~U!*pwSlk`!`*DBmJD@NwK}YrYhWC@mf8?hvZh2`PXcE{cNL)Lx0o2|2Y0 zoy9SPK2~^p)^6SAl{He2ehvF8XHzpEsNJwrSKy>hHgyAQ$*Flb33?P*1fYIOwG26h zeb9K{>;IJRw-Jgn>L zwX8R!D^`UdwA&iLJIcu;&Un^~0B;;LsBscr=;(Ua{cb4vSvvWJ+7@DvCalc&75oY) zo&QS%c(V~$+}x7?Q+rFJa{lzM)zk2_pHm!<0Q?pcKyVfg@G{aIO-v5h!f(ru@TNpL ze~0MW`Y>zM5BfOH`Tes6mV8*WjELnTYLv5P%4fXo^ydBqrNg&26%eWRz1%-3ZT|4*bIwGgiDA)Ji-dQuVQDH%QaHhvE-LBdwXk_qM7UJudz+|LPZxe? z#+Smkj7)rI(EUWvH1}E#KV|%M(j%RsOIZc;uOZt234q%^9L$%9T2hs}z#|qCOXQT9 zg_qLso~SALLkL2%T06?#rErzVd4J1=^vO?a#*m}FdQxz2Fr4+F|dX%{r|L$FT zIhaLDV>@K$ZSje6!ccC9n1rbmm4G!(PddLLr@3!_mWTz}mgxe}f*D2N(xI*d5&jmy zo`%YmAzAJVH%!gWsnLvonxKhb?>%Yf`!#k7m}d*pLUi3t(?iSS3&oy6NCImwO_P_T z=d2KoYeaYsSMza>>uvGt{?zYXjl=L9n33~ZcU%08uO?BB?W1jbW7|m4P}T07*69ca>=!U&Bi8%{$MT?nT1#Oa0PiR}-y;MLQ8NDP0zqV64^@ zyO7_~OXH1Hxb<_D667K+bi+Vk$RJwK>UXSZLa;3r)Nyns>?Z=UXb1L%pm~$GNth$bRUK^ z`L6JGshN06X?l4H=OPLyu8J#&PF*vZoqhH?*_6XD@?I~_|5Y8U4V<<(M_e9}j zYw^pq*8UF`K!VKNt6>+789MN!bXKiZeU&yg_7}bLTCz7`Avn`Npl<(5tMUr&hP_Qb z+ubXa@QFo_s>V00$dLTGvZUg$H_pRUFS^tV(rj_a{TbzYLSlmQ(*Y|(gc2oPOB;S{ zm_x(y>gsgiXW+sSC|f#YVS4QFYYZ)*61wwOm0N;gDjL~Ijxz%HN3GQUk4^V@^H;&HPgqb$|>aNZ({emS! zaatt5%qlbmiG_3EcaSnr?!TQ9A$V9bI*p%4m82G@Ow|*5ArhWIx&(`X~ zk8Oj&@sEACuo1^^_hHSkHQLL#9Z?1!I(`S2C=&|1RJSd1vd}Va;cf`66#l(ZTu}T` zpg-*{fPtx6t@@rgsJ2X<&q-r{Eq}y%m@uucfb(cnIp0jy&@(yE>Cv+FQfS4+b(qni zi8TV7Y3pH!{6tewnb|U*duAu>6E{RexV1j!LGTlXK?6VVy!c@;u4cMT-9x`Dy+i|c z-tNr9A*t}#Z++)I!?StWsY)F&H9jz^r?6F!Z{U1}wNu z#Ej=WQl|r3Jk;s*I#g#IXY;FSqnGq%p-pNjX%G4$s^tam_zfuSt7i2rbDvVGePRwr zXY<8B6Xph$sTJkXwf}z9j9)sGJN`1mjQDvzQg*rYfQoJgc@=uRkr32Q%ah>n;ZFLP zuE66{oq9eg3y1rjL739C7Ig@^WEv*n>}KR91K0#Y7}wa;aHVJm^M&ivWs8pgALiaNEUqZs)+I!63&DZ~cXv;4f)wshL4v!x1$TFf;I0+i z-QC^Y-QiR^-Mf45+gtja^W1yu7xomZVAfi5jdzUk$sd8<1#la1Gcos|b>xaIBz*58 zrS2e>$jo{^z`p}tHA+V6#UHg6NEel57vzAW$wfkfXs%XLxyDrs*lylcm?t<|GWTmo z0{wm zKU&`wXofaqlUiv7Nj0|3&I=CKuMgssmt<@_?=0eOsIz-Q(6<;4hFpGFUC{ChDvh2D zMmjK#27mTFAN6g03?`O(r?o-@eR#j#t|II_l2f2U#2TdQdeD={jH&zLX-quaWBZXAX7?!b+Yn+y^P_fjjiH<&4&SAiE#|e zWzB81LHUZh&w~67f*H{Y?0V*RG9}fGC*BO0T4VQu8r9@lX(I0pepLT-;#qL_)J*CC z5p7_pOe0ZBPj}T8_0PiF9WHEg<6Xl`1pL^u<5X3eyqJ@3$U9%;9;$ca)~44g>!I^y zRQx=f787V4fJI!}xcO>*e$+Z5%`pH5mcm7iD3aqY?550qHMq}_*nW>eB!+YckiNT5 z@PW5`P`q~9x#avJEp6sh_9GM&Hm>o#KV8vVvIqe!e+PP~R_xFY0>R{l4z8@L_PRbH zU=-HqUXy}Jd};mLu~o3zZXT81!4BG0&9=n|AS5$8D*jgj|Fj zA9BPJvn8!Gl_M31Nae*8)2mMm);4Vat_9^)fs-yI`tbIh-3z7OG*;&lA0fr^gC%;5 z$JZB|A?G=B)7l0#!{-Tu^^eEjUqf~zP*9$%!>`*3>}F#1gFoBU?23&&a_*%;1OWS& z#!0t~tB>|!fB7*;pNa?CU!54ORvQqT_@6JY`v33ydHz++_P;KV`6tiD_JtDh_r44A zXnm1_07`xL)Z>MI~+QB>ZGUj zS4gABo^_CIsis9pXuaw8W5N~M$E*+=h*75|rM30vs;u1S&#jSrmyRE=%wZt66;P`` ztUXY4R?Bmv#acO}bC=RRe(m4fy__}d1g}u;?(Y+SONAxVQy?)U^YdqAS205; zdF93J+CRB_tg~V>lzV}Z{!~>|6!ZJgv4c)tWss`HR1x#3k!MO()sIIXVTkfr^YD0H zr!TB{T&<+L9l!8j2cU#-+?Bse$10LhjmKc)iC>X>kyZ-ID4yKL7HLGoZZH_DkJBkN zd9vj?l60h1|Hz~NYDUGgkn(#S98 zVuz+{On3cj(^V%=oZj6xA(kk z7mh4(1up#*%QkYti9?Z7J_Qz)VMS_+kU$3o`-^3B z!y*Bno5Z5)ABuH&H^^4Ba;mvS34Owr zVW?94yWCXs#&3RKJJuBHu;%4Ou-R~#3;I4J0>>bTM- zdu>Cm^wW`;fHtb+IX?bSn1%Kv{y3@HdiFE}GV{ZFmoVS++LPq=A{wSmNyjj{Ow-0O z*_uysxB7&5Q>q)Y^-}weCm|We#gD*NRBK9i*dU_#eUQ*9ck53pGFOwJ*3;1Jos}d$ zOHnIWTX!PaPl!SCi)B65$-}oC2Kbb2hdfh##4|x0UbB`pos#&GpjihyG7ex*+ozLyC zrQwOWOWV0m*M!6ACnnN=#nFnLf+>?vvtvD=1NgSzsa5Wxatj{RnmtW}tHR4rl1&kt zG%n)j$1F@bO}gE+op?4a=flL3)pL7AllgfGIVbwGU}jYiKMpvjWlQmx<~Pq;IouCs z^Y}XYFB0wF!KKg!g@+|{4c6Pt339cNL0ORhySIdU2d*;$1@Kl-!=aa0&VV=8`erLD z;GJLicn%l9u$LS)Ey46U4wwE$CM>Z``{PW-Nl9kf1G2LsULlvQ^XaOoj^MOuS!pt% z4$J-lt^VR;dJcs>fq;IkTiB;JX$cJ2u8C5en~8nsgZ1Yi|71J0wS~HiAeIOIJ1I{( znVe^}bNz{oEGZu8=*C>DrIB?CRzm6pM}Bb-Gmwp8-ABgo_*7qExVtgHVAhUDfNL2o z<{TXA7ezioD{)@f-cEtlpICPN7s6u@Ok?ZT8)@ysmtJRZbsk-GDAKBp&Ry3ZYm4a$ zXLA%{Pq#O|sB~eT{=uSVM2=!b4h*o|g8-C=rKWAi5Pp77+$DL&dYd)w-)|yxne-I5 zaL=BCtNt1SK?$F)XgU*EA29cJy=wk0{C~9U36biFAgB{#YT3Ej?k?*>i>hGRxH(zV zq~P2;4w%$&t54bL`eB*CooC|l_(+&1H8UtP=kBypFxJCXS_DJ*vhu?{9a25DW!WTY z*5Y;<8)Ly198GK(1XsVXS(1)lWC5RgpAwt!Oc~zDU}Lhob$|UiiQI#LuV(pno&-2m zeP)1>T{`7Ur*3-_m28{qQ0jgjok=)88nDU@v^iXgzCJB}u&E>$E6SsYA9Y>h|6DqG zu1ielK|~&Xj9BpEGLu_h*pXJ57t2M{wLaLHlVi>ke)(MDFqOJ|dFmIR!srz(-?ykp?QZMb%k>~VsX8kHthQu!eyV}MP*U!F z4~Tme6%HL>7U^Gq(BNRQpt{p&+U#iD9R!TnIT-`dsHe&^yBLZ(2a4o6-7L z3P=3a-lEwG(9D~R{{-S#u~`3taWkV1HAmpLNR4HX0ia?JOk>6}vJ}#0)|@LS!W-A! zx$LM{8i)|@L<@mFAc?T%7Y zM&s;g3t54I9<$Ea(O8OWOm=k4cK`a&LSoTFmBW|@S6^Xg+NDQ!aOfs%O6e)e+ql6@ zjH)}!&75o+hyDJcY|R{r7&#?BzcS^~3+l^BTwKNEOL=#RB3d;xol?qi_3`wnK+%4A zLT3Hc)n3DJHFr~lfE{XAG;FHtKapb9fcpu~& z+Ye?NpI;}w-gl?@s8uIEYn!O|gTv!~@zA#MMN;~f4|E98jWK^zTbUadW#nPXv4_N1 z+`)>k$75md=E%8ZSEfGiz1$Y9@bYtM^0qs)E98t_p0Ts55Lne}T}n7qbDMruSDbbG ze%e+~zok5-SKxM#7XW|0u-NarBanz={$lVz^1`VqCN&i3xm&T@Ion#jk04h3MZ0YGxl-h) zjDDZVUs4slE_=Sy(ocr6jOsArPJ}zoB;9++z>eUq}JS`$4W!clB$xaqzcss=924FhbMFc zut=fA4UweSVB}esDSrfKhyfv}=>0S?P- z(r9sEb`~;mwTvVkTIc6$co;D?dh%$JfH@AQ7|03SDf|YwUFjJ?(GYQisvi2fTW0{m zefrfhIKocK;=Fx?CwP~tq^Fm+8fFYjzy-&)^1jEi+TXJ*c3AS8L^y@BDh+ zPNW&BgyJMjsU6QY1iBsscC!I4g&_2!LRU3mw{_i zxcHK(3u7zkxF7Lc0gPYlsugQ^=r5%5(#Fm}uZiQ3_ zE_K*lqQ%V(1}=(B~gKumjWV>3(g-rmWiI;O@M0xq=$RzYre z3~G53-VEcb8BqHbh@w#z5GmxJYwo^!Rx8_XQiAVhN+@8wT^6m0@8Cno=KRb;wbt2| zuu>6bqd=m}La^g(ttYe)u>E@G&!VmyIf&}{WQQ&jp!aQ6490h9Y3WVtP|p~_X+;5J zHC<>1KC#5`u(CwzPCwF<=~pE+lcbWu@^(@MMD4^-)TnInX59_o`z_+Uv7a>fPI>W$ za8bNIq`L;!S$7}(1nDbrT8c*@fuVORStQbG@#GnZ3p9vLF;pAgHbVD>5S+JkZ<9jI zbD%w5o0l0c^Ik&k15se)m{yEIgCPtvoCJmMGQtv&iZ~TBCg&uYXv(BgHd~mS7%pqh z?1oQLtT*!P$6q!lV3Z-HaNJ8jDpi+<<$eq2jmZOjg8PR_l#zQwX<%c9^p*W)bPSx#YFjOn3X^&+gGz;MK3 z!dv;^9NXGWcNj*;Fu%O6e-wn~$h$3|6<+U;U#1qsZF_|l?6`9Hh=1OjNX2~She=JT z+KVSR8(lYdO#_@#4a%mnizP#M#4NTj!`*{|!_qK$?JT&wiqW(|F+}&cv$%{hnG-=^ z;6GoPTb+hglq4he1D}mT+vCzFDE0DGEF`{LG%x4lNGx*xWky>+R%}Z!ip>I#$f5rL?2Ys!uQ0cE)w_a&PPI zzsZO*pzh}a?!7mj3K^;5y3Mw^Sf_{={!Y*Dx-Art!UNf`@VO})UgQDI=11Glpm0Hv z`X*kgydfre4{e=t>vuFs`IZy8&EhjA9tTl+Q%H?!Z$qX{1oMh=m`h5U-4#=8!WD;V zM-VIn7L68fC4Vf;?EN_R=1XITnduv-%4FDssqM1*w=IfUs^Vr-0~4Lk^HA=Jw&pz6 z3|x$g(R*0EKA9$wPq_8r6T0kUbe4K|(>A*P)O>uFKO&v#zkNne{tzUb{W&_Go_YUk z$iUm6y}e|^894Z7S+_<3e0?8k61i`MlutIFCl)m2G*fGdET`f}RG1!^Ob}e;Ws>Za za=*_N9%8uIR)7?@w)3~3>#4-ycL}#MTg<B@`WIYp;=A6 zxn|y?JofgF{mD=jeyV&7kvq`^XmmXw>f=_P##bOEAy$?zSASOz-Zta*#Lfe6k0o!F zb{PF9Qgn|ddLSK(ar%mj>m|3Nb7|BL7A62b0Wrz`XuitFP-WiyW&e6D4P6U4SS?WO z{C?ih?g3<+z%#!DDmtG`LOEzw&rHO}?dQd_Sy0PO@E<{l(8bKd4WyI0sCkmI=ZRu9 zV#Q%#w=t&Hl3OT}yeHhm#(p;Kx?qs#n#^gcyI->Z5)?rNjvvuN$5OsLk=ltk^2(qb zNh=6SP9aUxu%vgbAUaNRH9%;cO-UU)hl8^#VQY9o$>w@- zEmnv!ZU1&9;q);)X{>**zjm81Pn7p__2l)n*{QEjlzOR*srw1O^|3=rNmB?-RffKq zChWvX%}T$%*25O0+sv*f^2qO9-iKWNYEoaL$NQrmq$=s<>%H^6+Q!@=nz88G?{>(l zL*L2Fk@v&fShx^|xdormwQUo#;K#JGq%hIew-7=SP{eg;CoXUX<7C3Kjt_>b)FTL* z*FOpA_xJ7%oOlY&mET3hP$V~6b~&tWh6r%s5*%A_iPXV?4l4)+UP7Jfxyh@u9mOqx zHJ}B%S(7Q>;I51{!FJQnz0Fh|fm5Lmjo2Q&;qS;vH>?4MeK6rd7HWg;=anT+mD72b zuNS#F=U?-)V{}))U167*I(D;vN;t>4DIA}uys#NEMcDI`s8f$2u2lS>lt}fJ-wwmp zc`fFNBPLb_Cy0Z+a1!2l>uSN0GH;^K?w!#L{}a8qH1Z;(Z?q5v898I)8VH%0e1c%f;gpvW8!ZlILth{A3bKt;Qim zH~OM!NFU$RydPUpdP8wNc$Q4Gf#}L_JESYZr554^CNGbtYSUhP@=y4QQWgG?jY|Db zsIbM#Y-E>B$X{-HC9&X;BC@bA#juY;oO z=971c-}_1-phtn8@pN`CvAIjFs?x5{XHn7__9xPKZR&A*l=O;=_ZAS-u^LDP2QSgm zmYQA)heQ-Tjid%rdHC3MJ@Cq^`|$D5KbpPdo&yY5__{9R->h)BdPmYfhliMJ4ZZ`4 zyYth80;)W#R@8wz@&+=#v8H^D(_%;#2zBD+O;xHP-TAeNrxE8NsLnphFY3(sNh~dRH=Z!s~G^ z{8|HcN1^TRz7^NQDl-{}TO77)U|E@t#3@tNMd3@3sk}}5)k!=j7eYACO9SM#Lt{sz z#A9adpli*jc6}8l&no*O?HVm&eoCKy;ASwH{pt*GoKA=lSG{U>)Po$)YR{Ein(Wdc z;y4hsU2WJ&bQ#A$G!v<*YnQUm%Oc3u(7(aW0Ii11#Tf6(ZOUJsZOhD%c(J+FTl9@c z?EaMn+rMpWPmWOg%|^!r;xKjs=?VU(gl!z&;TL1L?+@IPH*^Ve%n1+fmOP_n z#107p+prSD+&jQ#C6~UhUXP0#&j;&AiKr;E{j8Jj->Z#gjmIjcyY?mA;@PNC;31e2l z6uxtwt1+xoL$k+*Rl!nHs7QaKQ8-q6BMEmedr;e-h71G780Pci?h8gT={p0h|=ffB1jCD~#Oz~yppuCaa0&5j@Ht&nB-JCGhTx6{eU3pjmF=RKWBe=_ME zS-;#u=Uo!zze26XX9s3WQnGU^xWhycavSy z?jHGW5pGkXmXxFrr;t02ACQp|)Ia(~BP>WY`3t}rG|CS2jQkm5wUG42PYQh?QPlk> zr3~J1>J6X!Tkp)baHiGtnh)NBI9R*RP;e~SiA)7Lb9J<5XcG$&zK_j)qiOd=Wxs0w>0K;Z|N|1!VPRJ@lXqnk?1((d7JsHNKG` zIej`Tu^xCl^A9Sj9V&pD>%VCMQS%OB?piST9a37v29@+~;~g*HM~3duo+%K+=lgsF zc~CDRQNCou#if$HmL5ax*!~?f`P--pou~>TzBL*k9(l-Gm?C6j3n<@&+?Qph(n%HL ze!!cbv#G7RZvCDj%gSRvK6&~RZJg_4+k_x5!2HNPF_^nX=0O1Y9Rp8h4tNO!#<0>#72VMF8%~oK}64Lg~ zCOA8I84EeOvhxj`L{;@QlDM>D(WY;O?#NG$MQ8sK~rN~~kNZ7aRJ zksG`4mF&sNvyuSW#B%BrwK&CZEc2U?s%yRpNEHlV!14+t2!J7CV&c@lErtLeCs51I zu~uM8)^>ZTym2_W*)m~*g?nMl$(|BV8QY~WN!!;uW^VopPcY3%>_dS_M9$ZQ?`)IX zHB$-f91KB0QuP#`Cw91P2ROyE6tcM>ka&XnQ&&>}3?{$5sO#B!bkbPGdCv-Fs?kp# z#5`XncjWG`BhBjJtf}R}J0T>54`y?EO{SXMw+7%m&yMfrc?)So{cP~WB#i9#{jEKE z^Sjz19SW5SdA#Q}p*dk%nUt^6KPBI!mzm?;LCq;h*5W2RZhG?BUuZ=o^|woXU)dbq zUh-d(8c{kSgYl4mOdip_x7;jZ;8K?Fk5YGh3b5}{z_^Is-ad(|L2I)MNGMu%^PRK* zNh^7Fdk(6l)IUZjuyqYD-9Bu%3#%T(z;m5%y!NldQ;KlYo_b+Yyc`t^Vc95aNdz@O zl(!XEdnl9pu6?=&UV{RcM^8ALkM{sMt`$p2|r@c1lTM}fJKdg%yns?iT z)p%T-a%fd8L1PF^t8=ha1gBx)bS>K4D+N_FhwV0rh+#)v^#KSgFFFT{hc93T>5j6N zV;jO6lC9IR^g5$a@bP5l!25XIrVRwDPbN5x3 zaZcM~X#03q17(t52keK1ui&|#X=o0@Ub3l7;s%v9oW7lM3O{hUmvCw2csQi3Ztzvf zs0F)-RC+P*nk2=iW(Zm;ke0bw2>Ap>lQCPCmeSEr-SEOdYTpX_B|YiWDW}E)QcF>* zmCfic_`9P>Y-d^YQ`dS88^W4k-^HD*(%E3V5|jSMpuqA#8~#3f_J*xp;!8hUk)rQ0 zMu~SOX$fW??UxK$PIO|}lr0C3Sz56=t4+wMo!`AV!uD}HCqB&bRfahIP2&eSQk8=H z?j?nH6W$sEpDtO0srv_NsZKbe=+rtHckA;n;Z3q9 z&c8eAw?4ER+rMVDSIlI#`2w-Xvo0aBozNK{nu~I<#upzqCSrg;Q-zbJ?G)kCRCVv` zfwt=pOr}izYukODE26%x<;?RTA=?jL?MPMw`D*XKEwI>nq_BzLO*#AV`;LRZ5Ar~X za(^D4WInp(>p}RK@|!f~%3!hprdky5ay@2xRA3^oKLqNP0duXy#!zb$x=$jk3 zN41^AdMB=0%mP@QiU|(BL5(5H*|G zZf!q2F~k%Cb6`A1Og>@=>E7~Q8RVU4Up{<;=HC%9&*c_hRN<_$+($kArS1~LJKm9y z%jwzC+PGld$lB00?9TW4@_4-HxVaac^uw%xt@D`6SihNcFgZn+L1_s!)okgJaZ40j;iYi9qFgTF z{QbXRTERj62h++potF8RGl%L|;eSjkh+_P<7_XSh-2zrAp&>GcT<#&5Gi9#$VO=vM zyW=f}oYEvXKgR*ERcD`Ue>dpvaBx-Qcn3~E5Zgw1jK^-QV*>HN=5U%AIZFY}N4@o3 z;tImfV{ZZ0yC38#+%vqBIrMCmdJ+0gaNknYPKIXL(hPzGVwbY%pK^?%Hy`)VwU&Og zUnkAZi-=n^kWjnBtCOPo-l zuQ{GHbQj(8!Y&}*kZ*203J*DZ?R7ulgjxn08o0~m%TS?epo65ynGfu9Pd0~h{{DNd zZ_?O70ihQ2)dN=Nvm8Stb~VqG5|D($))EKDpNtvQvp(+)>hXiw%y{|>_1^MB9hPYg z@a{3D<*!X69tM!_|M5LQ!7TnB5(IhtH#H*vvNPlVu4{xG)-U5vR1)eo9nQg(=eO$u z@_2?368-bxe=|7gpLk#X55_S4tIrk-eBT&4_-OvCHc&T|>oV!lu zc6CSi^BPvmx}85`cOulEb)h>2H&B^r&~yH8ml(XPKn`fV8Cvxk=0T+rPx?Ify>Jmpy@1@K zA_1ij?z>j#!d9A-LT}IdYe!L|t<)Aq4!M-h)lEIkIzmF6Rx9={;?|9rtaFxK*3%St zzLwRyOLi#QKMJ)3eGSE$m8k)Zf(Yzj&0w&(_tpTl@{){jpya!Tr2ff1-O7uv8(S={ zGAAxz3hV>EeIgp*Vs*{8m7dyeqvG z7M|(zLu3bE5wJ`!Li>_x`YIZtuQ>F+8`(GdHGRz0a4_U;Z>b7$l6>rne*G4A{(EWx z{!p$Ni4quuRGEgQjWQ=<*n6D#;GV?K`WxFT!EmbJJvrl};b^J24muRr_g;oOxEiV$ zLcM}wjD(cq$K=UDZ1aDncXjhgoR_H>Pu}!PE=pVA{;7CY%xt)Qew$XE0yy< z*uZ^B3RB8iU{An(&{)mpHMfr^=aLgtMXXOF)1YxRl!#XO$MB?q#^3ED+yaztEzFOT z1Xq2>rk^RLS58G`pmWL=wap;YRt9=qwTPYSe=bTbM%yS$ImZw}!m0zyjBdIrc`TcE z6!R}F#6B2*v4`MEW2fUpi1>EOsNF|%vR|L(A|>E=?#h*J`%7X4ebOEglSd^WTV5&##!Zm5r{w&>)iKP)0JI>S(42<#8ac_PdbYtW z@$c>pyaQ+VzHy`GZ;Fpj65nKHUxd#-tkXLSIy%m%00}CAb;3|k;?XKw0!8r%uq{Lq z!*huV1UiMYYGzRd<802jq<*IG_LwVJ5B!D|9AL7(I^5|Tk=am;d3fqCai#svQRP>q zWRN(oGbGNNXmaK9H9Eh4fiz4NtSs5%JN}{StL}K|3dz8*RyZwk6pV!-JB-0ew1U#N26mm@s@(%eMPbPQuaA&4|M7*4|H3KrcF3xL~$*O_; zF2Tlezg-YaNMA2B1bCE}K5$-fP%%8o;W-00Cr1aaj=Y(rc_&PYIhwC~RjSM(LVJNJ zn@-5|VpHo^-@j=A?%CU^1!<<~joG^aiAJ6F&6~3gWJhaiH(UDB;mQC2ZN^QzyO(Cc zR+ljUBlLunAz(cB{lrs~tBC zcOjs$@!ls0^}Ad7u8Ks=#AN1=p7}-n@<3xNp5a(+Uh7aQtlJFW!eUE0 zj%Fsaqk$&LPID*3d(R}fH`cBC!#*3so~3vMJn?pw1Z-%WMwnlQUC6&nD<^U)eWbDA z(%EtzK_>zT!x&$0M~j808J$#Jc)FtT1hQjiB|xA)ar-Z^yqOdQTKFJg!LM6$AC1ke$I1h- zEi5|#M-^#@-u!eTI5h%y7cl9E0wJk2hu`GpwPJW8mMdBhXdN4Zvdrxaqf^b5zd04M zCR_Be{c#*<_L%nerapLPH1`lS{n#>!yxX|2T@>pVz9d2GV>AP zz;StwIE|dH(wy{+z@wa3ntPls7K_(Y87+A>SA&9d7M1(i%?iS+kzryEc*j_CL$dEw z_4rtP#?0rJ=A|r2_?ely72@4&rjsAeQTT5`)_1NwN~Z9cx*e`4IwmHYiuWt`2t3PX z=EgRY&DRk=Vkk}VuGP!@D6mHwf_tX#<_Fd z>1LWkkFV2%s7iUkxBP@NsHLUxd=$Wkx#pTG75})m4D2*IUp`pp+?!qPePQAJExgv* zBsRfojbyf9Giy5w=vZj}WZ1im3t?6QUgOE3M(7Wr6Hvh#_~6x~9BFtXDq&_f%Oq^H zrSm>x+lGgFKnN^2YB&U!iIk8NB?*jdRqXV74`uc2Z8gpGkFEJ9&lky;5$};_m$`AN zb8&(Q0F>7XIDMkjbG@(KCMM<-B^sg&ew$G7n47TH;ZUBM7f%u-B5~I`d`RtD9wh&p zL4fDm;5+=L=}>28xHKn-y+CISVlN1%V@u$Ntd8e0L${wx)BD*!1cSs$qXNwwzD8CG zvUI3cqOYyivOq3b3w`|l0sWA9d|;*bJ9Rc4C2A$dA$#}wne7L(GjZ=q*<#)Y^>1kJ zya~!>;w!_Ko{k6o*SJ5NJn-M)emDcGtiRy?Zo+pIUD;H3Z|n=?O{<*mDe>64wa8*T zQpg~l#ztrRFBOuVcDxGL23dJ9Sp(*IJYlzMH@>d-cuoom9em#ZBi^s!ukw*wAMkWr zvma=R#pa?4qUn!c&RD7c`atk9?4hr z<}Zob8z~CD4M9I0Z~~11nV*GAr&VrPIN9*g>_ko2=<2Mi4_qcVr+&#g$&mXfH2eln zd?W7uUClz5yCznYc&VDzW)B;BKERrL~615N20W5cnm5w(CDvwQ^1)AVP6R|1tq3SW9D*qog4{sk(b%NV zkVJghB~L}}VY|7K8HR+gkB=>&p?(G|nO)~a`DBX||3U3P3zTF0h{>1@65nkN=#t(& z4SjSA=v_5n7m@S*BuI|-9t2IgM;Srd8wh(Z0L}FTq=-5-Y8K6i3^D6wp!u~>#SL&W zDjnUIKWF!dl~KK1kQ5oHM5pDMQC`mEaA^y}_ArUq(;>k@qqS7h3-b$`looce+S|F%pjG>K(Cr9P1*(gi$R;SN@y~)0g7T^ zLY$@p>gFP%F)7RGp<-LsjGTM&m4ubq|0?L|L- zSGJ2UU~I7_uygJ4livN3k!3QTt?H;zb;yqU<6%D6*?AV0)Mj>ofch_1c5b>Ur(CPGNhxKwF~`1U z)1cBqTe^?T30bSRsdt`Fb^Jm$iJ{+11e?_KpMLFk?abr3$=5lbp-W~mW2N(5WONLr z$xLSv@E3vaCK%Ywjn%7zy!3nD~BR`KGIRMD3~XY zhJr<;;d(D3X|_VslDKBGU>sdC0>vl|0pp`Iu~M@(!@%%}3Qh0)xw%G&eISxf;2084 z4&*BOq@ZRUK3hretQM@O;&L!m5R%ZKbuFZAXzyqb^QLlH1bfe>oG+qmVp=%e-Y3#n zhj@G1T#JxU7OvZ7e)+98J%{^u9LN`G;Fo{1C+;9V|stepl z2{TTz&Fc{zjM$iBulu1V_~U3r@puwLmvExBnfXivU^8?WBmBNBQ)~E0x}=bUM>SHkkRP;dMaFTnd|d@CVSKOV1>@w1{2Tm*|B1Wv1T|#{i(ELCZuD` zs;dn}W=(vD=ZJ)$s;Eyh{v?Ube19W-Q9yv2+PrOUJ_4WCc;ALqi|xuk!r=~*;n!1LLcD!YncZOVeO&Q|04$8u7as9a{;~Xqd+GNNuh%{xXbKN zA8X^zY?D&57R{9nwiUNx3+#GyjEpjw?M9XLh~o`L6;dgF4b2de?h-G8@|FYskpnOO z?{naDUKdhEO`IcVJlzLp0FS*d@?Jom}BQ~-T{)XAJB=leqLa7-{t z4;wY#N@eyZW_DU;_G}8bt&k3VrpBn>zyK~D&!9{&7VH;f|HB7T1c+QSO;Brw9&C^& zlRSShi4$%YE_(V4bwokKl`wZ*dUdi#euahpW#EN$5dOwZ6SeQr)Uo;s=^IK=Q0Vt} zfOGu>kxO7c&!N23c)Jq z_voagQ}ZrQL9B59EZ*I2yFlFfx%moJU5Z>VxwOxd>-YE<%p9Z23E}wubq32m`kf*q z1HosKlMC(!uKE-1X6_#D5jjXLK>@26I{Qh4WDWmR-_2!V+RR6n%&2cUZaDbqvn_LR zj{Q%QnLYA31C<|5E*ADBQ&JK-g~^vS+euSy;A3wyZP>@M2>mhzi9y|knOQ= zLx!{1^Na$=3Pw@^?V#fIj@^YthehsA;Z5{w0V4~H-3{K*G4lo!<8Y+Uty)IvlH7@9FRF0 zE}IvH&k-4!0sQKzIG#^kmy2(E(q5KrXGJQMiLkT)gMW|18xvgY3qEixRPDeeq_9B3 zK}UFulE;|QQMw~xX4BgwVzcWzrPZ&yO~RbM91d2wW+fjr_EK)gPn3NPy)ySLHbm1A zo~96R?;S>G0dtQKGS>Wxa8WpBKmMH+3PqFTx5|!Lx1N4=jnS~EC-L$hGxA<9nGlue ztCIG;?VWJJFgXCA0;@s8o`AF4;?wS2hkFqdRC#48iRf_M6?kHeslu`#n^8j*t-@bJ z)F#~bKB;D61~XxwZzP3@fRZEMc9Nlmo{DaU3w??P;x8zcT3NUg`_EWsyoVap<0qdt z=oI(miA~4(YxBQ4WBmlwYOONXk0ZDHYqS>|N8BWrm(GvK8jQXKXKn1Csx3LbgE?*L zX+y_3FXnEbt?%~q=m<$fV-^`a$bZ>h^(1&$eWF3$uDgLGDD%X_;2ZOw&Nl#N zbStP}W|GVeK_0u@dCyZlTjX*wrds6M-jlfzNKZ$ z{1qpJ$KGk0$GO$>^T>{}-*!W(z&o(1JL-T~5WREPq+J)w1Mu1ta-w{5LB7Pr^y^~b zB7ouXC{6+9c}=%^bkC&D$-GdWwn>upN~X}`gd-fsQHHpbU?y4mB~t@bTK1B+=O(@z z5iK0!_v+{>o?%(w`PlQCe?ImAD(O&1nl7yfS#JOmptXL3(uXpc z#9f0E-vWX3yBJ}>Yxj2FW+Zsw&Kw5*1~<0N;RiN_F7(tL2luCu>Eo#I5I%FDEME_0 zV31}a4yNE-gvaD0v;G@Ws@o`T1OMBg953vUmCk}cml${?l_ zB{LiQfFXSVrZRZEY=_x+$-mX|pwbayA)&<8iK*G*91hdk<~btg@vE{T3glnfaA}-W zUrEy@m&}a#FVlg?x3|<&HCf>sd5-ugiLrQJRYDmA#uFOv7&LZ_IM{UAHh3BC<;U-& z2inGIRLThZVJGI!y-Cs*?Y$15>c&U5qjTntY?jCXt7o@JhAsR1AFW_ty}G>o3D=0Q z^G+HD>V^EP6d;|1Q6=^uxyMy%RHu~RIQf%=X00j)mi64J#8{Dr9o--zvyDf$pjuCo z(1QtW+F=OV$_{O~fkTg9jQx!62dZFU=T6`4`TiD6^M^yJlh3$3OQy3_K@t*biK*{d zGVYA#G*TpGt_DfA1O^7e3+9wJokes4hRDATj`Ci%rZOrxS5#Nl{qQ>b4pCa`WB5LW zTKtIMX+&+?B11u)@z>ToY2@Mkyh{g#yeaLm@nni$s|yWPlQfyr@6*?vngf{WJ(OUKKva+Y3w5#ZLIxLgr~gYsHX zIK|I|V9>H-$Ocop3P|)DD{+SHctv*az%0gDk67Qtf+EmQo@#<51adt0vJWjDy7H^_ zrhQb(xJEhtkj!VJR2W~r-EvuGzYi<;m<$^u!Sxp*ww-gf2_Hu^G*^~e(f>Lm!@~9- z6Z;n#%)fsfWK0L~8;Iun`|%HR6#hR3p#O$b3*ZEGDE~%34SklDW&Rwh*GpUEUk67B z{J%&x|NF*)M-^FFR0+t0+1Jppr&^92V{>zmn8OngvNl#7FvTJL48Y8d*2u_JICNB` z@Q-PFcFkjTVmm~T*J#Ca5kk<;LqZ`f85^=qO)GFO;Ju{fo%gcfact%)oz~P0DN;OD zv0M4{io<_cD4^pD6}GFr%j%1GcFT~QDP zjusU9m_k!!Zf=y{#(DO)P5{RTwv=HNx9gcwdfxfp=(}5Y@)c21|wL=De!l(-Xu!8lXjJOwgAictQ#qf+410ClW@7 zp-g+44$Z0i9h>=tW7S`*4o36M_X;9o0m%32p8pbBy`L6B^wR4jI1SK^bYv-U2jCHr>~{JNxP zQ?aj8cevwp3(vCILTf9Ss=D+NmE@4|tCdp1NhbC=WRRw^>sJ?U_XM~WzW_LKg^<$E zn#WIBsTd0CcXE(*aER1F;b6}#1oHG*ZQmh#X1cmd(B1vnE`ml>p%CBv%V>xt#wUm= zLvsizfqoije0*nYcU^7ls-8EIYtU3CmGw}YAHIE$T*GPSpxmDiEpnkehNQdB z@XgQ%Uan8bDP`GNUuNzA0JC8M;NR(~>t4)5l2uno>nT}KQojxuLMCWl#Cq45^du+_ z>xt6T^eHU<)!Xfn`%yJr;X zb@ro)TpUv16eQ8mEGtGC|J~{1@gFwYfH}5$)NVRRCNaG5OD1M@(zM2+%qUEJz?BcU zR1+DoqNg2J8W0&Sp+fpzRIf-%bg@$KKTuN1z5hT-_Zj(@hR**LO4=_`gDD~r1@3&a zWPUz960XGYR^~|>VoUrWwv5Hac4_18`cn#BAOfUe>2?%VX0qS}hL@i)X~!(7dk`Pu zQs9~4ca|7KG2RujN@L)i^-arK$qNYvLgJXV&cpch0=nGF5wt4`Jd3y zo{E2kj{5x1=qUOBlaBtc>drf;sczBY`k^ACpnxI@g7n^d?*SG0s7kNWq!U^YqV$gR z4gyM$-VqW4Ql$n2>AeLAEhMy1^1`j(+U5ec(MnKCjn$+vP#y8b&xzC&w2N9xN(fj7nlF7vc+c3Fgp3u?Q_-?q33&ZS7JGbz z>u)f2MEt&!z>#rcbx0T1J&=b@}LF)G+pL!k^<|^&P%_;p3eVX~eA4k-`Su8#2WWpL?_R zOxj!zg*+?Rs5i0lSiKrH{p-z?M}1TL;JtrTK*eX{DPRnXEX(^Xw3?<=)IYGn*32ts zGRrdUK@~81Ij))Mk@e&69yN>`U3s2_|6>qqsGAYTbmbSxDM{KFzWd4}X{alIB(6b^ z(eV)iIn^tPHDfcW;Jxoh(S7q$<%T+U??0aNyQ!nY1o?>{Gn+u$ynUWna7e(dr9ta; zGc^Z?@nj#l>OL%zT2iDV*1YNE;JfeJ7xM>UzWJr0u|!IgIzfQ-eRB<-MCRc@XpO9P zv;FIfIqG7O3gsh&`)Jyc17c@ttY0{Ln(n}}pK{*!pB2!J0G`~qw>MwC>dQ&2ICq?% zp5kin4W!OYu4IV(rtM#k!+xU4y8P)jmgqMPDYqT=k-|m(buYl|HcbUAmtTx`C>4n7 zRY@=A^NVE<2UM?-#Q9c}&>eo8b(|NoE~;iY@NA+bzgR(cr+HXoGaprD!~jiat`I(! z+-|Y=T^Q=FO2b>dIzg4jQ2Z`<123^RX~t)iq5h<2866=&sQqd0rpHSh4bN!M2N>%g zqNPsA5xE%iox-?ua`2!Ze@^3=;!&B*3(XTu0lImdDFovo_xW_{N%Vd_LF>uD+ky__ z<|jX%F3_S|4SY*57cUSK*uC?O$FjVBwj#|Eh?Kqy7$`QChjf|#b@L+PMfG~EW3N9$ z%F8Gpi@C7JPwome`-6E;`Mu$lwjyq4L5(Ni9f?4V#+EcW80FAu(;90D09fgNFSHLBjW zWBKs9Kls+-Cc&l4q*N4(37eh~xK|_6Yv2Z>rf(klIY^rOK(XO|#eYg`H;CSO?)1Bl zF@N)9Z0H)U>%tNKS6VyvhBJeo1Ie6>fP~R*`b$BB@u#;ln~epyIDjKep+ZhTZa3%d z9Jo{PjqW+h7k9IHer?D0Nsrju0%1t`M-`)D-)4DtmY00I)24NMY2(;!)^E?rya3QE z7Z~EA4xBUfZCo23Ok{Q;<`g9^xtkT7@Z!g3xShYHY+L5ih3})>LhGr@UvK_q{}x0 zEsltK44Sz`uwtc%cSp|HqJJ=vHIYEt-3I)dON1C0K22j*RvxRW4&=JXU{B^ZkJPRk6eLOTzAx0|wz>_}iEb}^8~55s<8w%Lxh zk74oA{tMJOwXnVK>L8+(&9LR|Q`rf(b*w#t=E*_p<{4o5F}Xi{*#pWSuoy&+RK0f& z-F|aW&Dh8LQb&t?ZB2$&b7-;>!I{;vN3ErpV8^^r&yobOudW%nPeGt$c($Wr)Z&$u z#d%ntoApIGh#0VIGg?WWRJ?i;+o`s%pR4k4z$TiC)=V5#_AfE-4b-{yo|08uBe$wQ zSXYyfPi=cQpX!*J}`D3i;L*E->zRSAP;D{MCUkc z{Bm{8`FpO30)@)?gg1dcf?0qY!>KDyBGi3e-O@G2uu^&Tj(rl;_#2i~x@!_*N{@PO z>q_bEt@~0w1uX=r#4+EQyFROua$ke^Bi%3L#+7Qm^qrJVmrZKT3~G^14__$8azL!G zFh>TeL2OvoXVrm@kf0CF&TCuaRPt-OCVg)5zm9(2es@dFu5$nSYQ<8J@j=%tG3~sl z#IpbdDL#6K8RpJ^v{0^N0u1~k`we8Xt~mtiWxpN%*hqW58`+OXAr5E~+O4_Y-rpQx zL_zfUWXIPQ5_<=ta$i#J?>D;saHZcAE?hFjz;kLJW0sC#AzGHWeR-x| z6OS8I;vuM>2?^k+7tV#Pxl@g*`Eaa7;nLi(xoA{9hRg{GHXCgkY0?&I!`kTWJaU%dtKMMJ-LGXLBxr=&F z%zRn}&Y!KUO(SkFm;S)_{8RXltfPv}N^yh9N9Jt1pP-^jSBw^y0GzQ&urG1pDmU*{1YAy5ch%Z z@vKpBR=bO6XYTyU&S6tNIhN__>Lxf3OGG#rpQ~tvr01`6@RUVIwitfd_VRyjd|IFB z?gh;@DR+U+&Qq9}%(^Yoa+#twElF=eX{jim@-tO~AVb@JHWmQrv`2Q1B*tR))pM#E zE2Z=>C1{Y8E5S7)|_8#0?8sUJ5ACzF}}`(W{J{{<}Wc?pY8 zRsVm1#s3@Pc&JyH3qoK~yfvkB2EsWV$@bR&**1A#{+2GHg-Yf3!yIxf9~S&0M=iWq z4p@u+Zq}bpc_7w=;5Jpm0`!z7P$<9L`&JD(r44fY0Pd}*FI!`(C8_@7^aqZ<0r(XB z81xM^I|~yv^9x<2_>}H-A`sCqF6Y`-bj;oqx3Z}5hMZh2G|eZk;+~U zR10UT8^aYqwn>qcuG4u&9Y$kc;vbk0s4>HAUpvoFS)yzzw?SMGHD$2HF?^y-jMxSw%cJ zte4L-bZ6fz@9EQunkHxy(QgL3jL|>|-m3o1T9IK1&YS5ESQ%x`CEA8I1c1hi4pQ5z zRrIoEYY-{l6`)vbN!M$Gkp>!BJ}>p)v<)Uns2^T0GpgufC@n?88|2t=CT{AKC?(D+ zJaM71nmY*hi`?KLfx8_3))cSeY3zuy6g^hl#28q-D?TqdAHzwe3VOJ!A#6-coSd-p z^6-+pM&wHsKw@2|q>v%^U7@Q9e8lTKb-;f8k+~{qW@c7JR`|pirO?o<&h^Ot&#fsG z=TQ11vOesgH2L8ug#yzGZ4IfWu$@x?5}nBQI@e(H5)UuE_a{7DQT^YIhlgn(&H+l27QROfET!?wm=kovc_31H zCWtX&(vLL5tk^W4%z9^JVAk6`#Op2OP8!G$@7%ny9RmTJK|A45IdWzGMg`7i!!3J3 zu2Y5@RPpQyY%?Wl{D!ecH&U722ub+Zc zD(HDw(Bv&-j$*cT=Z(6R<2A)hN$)p@5>^T<&a5!Kle-@AKd~^_ma6chtAPM7xgf9>Va*@$f)8d83< z{;8}>nS0<9L|oG@vqH{rNadT1W;=O0Yl=V&PF^cZJj*@_i+yq}@&R+{WRvSQ@vS$; zpO9CX`#-kDW~dfYvI+w)0{LZ!-I$LS3g>gVrG{ibsq8pQy^DbN4#Y+MP3V`u6#D!A zDD<14JAQ_ZDQnz0x(#~wEWXZ|mX6D{R?FDp4!|KD&J2$*;J(=bAv$$%_j3*z;FID(X{f7u!`xar4`*f2k-<7pqPlYH*ll``dw19z06 zZx44RhHXX^osbV@2aIkgIif%Dr|g(w&K}%{y2gIoc~BeokN_mFRq{kxA-gGJPHzP? z6uP2f{f&nqvxf6(@t?C}xny1%krcjiUug+(gqni{IRuM$#na~b&9U{?Vo9YZonWdh z;hVZ8q5;^Rl(SOnjn@3yhX<@l_L(~f|UXZIA+9o}jLagldhHe7H&1gmm83tIT< z3u zA_bnuam1<_G;+oivCruji`{F;`L}ur@QlCAX8e524YX`)srh)*Y3R7XXHMTEZZx&K zK6^MqYEg>aDp^%)Cn@oPi|>sPLj@Tw~-=za-u5%t5#5OV9ASRUmY z7cdC^W~BoOCw3fHJ+h+BAw+_&#<#rTcG{v)AvwM_3?qcqE&m|?kz?34I|O6GqrPVD zM)vDIu;D!QX!32a5F7FM<=68em#Ss0Chg+!$>#Ie~rIZ->Q=` z|Lo#wu^7FD7ae<_wlo~Z#(OLMRM7z;V^N<`v%5xsS6{hq*<+?2ues~W?EC8ByMG^6 zqURZmZn>}o?OY-QyAoUZuS-5gm9woyJzp74KSHt%R|)fF&3!u0+@8V&_YI_9>aApH zEZ(F&YE{TF(YKg*CP%c#tLdv&b*eB1ueGUWtpP7nYbuYvjaMvPIMICX1t>owDlu2q z8X1vpkc}iyGnxW4b9g`YzFfIZTnjC^k7yBQ`8@Xxof=wyyP^fGhsfZJ>^4~13L9B{ z^4uX#KVZ&MlK#F-J0%U-!MSQ=3|rio+}V2DN$~ml{7WN)2bQLiEl)RxwERZfh}THJ z;t=F9(yka>%GUxgDkhFYiY}?MOKxvL(W1b0Fo=9`!{vugf13Up4L`X~*dd*y{WgsU zZm+$uaRtdHgQzxm_ zNYl@3jG(^tTj7&kTisId(l2*S--XPlKZ8w9!xWzcn5HZWrjabCxgQ@qF$-`7r>wS@0v7}xpS&^s8eSE3Hsdu$3j zK1?vFW#}As^E@uykqRO@p0iqT{ZO>r(o3SBZSufmS*~SKrT=C+BLnPbTSvFWuOJ?@Ss z6fVAsdpl4F9(K|iAIFaXWtp)6_57Z;_lKZ9>Ro#9k(?jBQ?KXy7l)OFDT8`pRRr7B zt8l5H-RN6nc{Sspulvcx4Q4Ji0;n4=n(#Zh?ANe$VIS24hvst4+kDVN@rSI3qh58B z4hMsKz6tB>3U+FrN-j{|VEiu<%GtX){0B%OaKbu?F6)c+=}+>?#<-=LzE?^+Ff$=N z>F>VIgh4rOKiT*;F>-kQmq&D9+K-TQi>H6i@KsWkPY9&{YGo5%QAcZ6RF0x;yo@Vl z3Hy43)aZV-No7vaiM}hcV~-}EBau}c)iPJL*704ZeXu9*GQ&D`9mg#w984l>5;I^3 zstHA9(j~B&;F~~8@~GsYh{Sy5pX##%p=(O`lhxR`3T=O>P4NKGcvXaYPad%Ic%R9? zClg=o8S8bKeK&~7=VQs9uchT~c0a)70WpQa^O?T3kj1tS_(nyIeH?UPtT%&mhXN@{ zTRvLdXl?HWH74jEKGFg0lNC)(<@9Ru@>gVJ?0(yAYg8BkC?^o{Qj%Kfkzb!4B-v z(p2XsJ5l3tueN)-2Gxa-5V_;lvm!9v`Pt0pbgje0Q?ffCliMn@!D6LtM+GBu+7K?h zwb=kmWc4V`X}7j#T8U-`s~QD0uYRYBvFJN(?B)X#QlX&etlX;K$K2M2;E$fOomzG{ z@%JeurRB&1%*)xwMVMH>ox4$qao`P1yf1xqQI`k)EVWBU zeiuagp2#LEh+JxIiohFFN~6@B1!yqN8Exj9%8ElU-^u6#Mg_q z54|pR`g)Pbqq}peGjxMAWz>TjIil7edC1W#&w-zM4GfR2y*JAFa)Mf?sY#Yorx$Fl zDJjth`{)iYypC8#A|i6}t+dtXp{HZ${N-TR!Oc6qq&>KTu-KgXC?xkgbNEC65U?yR z=RlD1g_eaFKG&TU=wB4{nIW3SHB7)uoDv)#ZjyUW@&cit?$Z|wVaj-a%{~$)f)wBP z2XMaWFW~%qP|>m3N_mcw_CZ*EK!7`}g;0C!-aSUQQb0b4?<^oCS<{OMlbyhQe+o-~ zLXg#y(Gu3RX6Euoae_Af3Ny19x20yhsK5pU%Qv2tI$IwK&GcN8b7C#`B-9FAun7wb zFO2!PZfuu8GwocuFFhCEVh9|6B{5-_Sx|+c(Fj{$7m$S=WI~2FflVFYL!g+;0@>q| zg$07eK^25zb8Ye}8(DmKE+t0rI5^Y%$%E4Bs-U3o^y&9&Gw)-`0Jg}o<5nkv=udxg zI7O&rc<4=dybZGQ*4Ebd@8zTE=#qDw17nF=&c`C`2++T?_wwPTd4-=*GUt0ZfN&r9 z|6b|D=(RllB*M@x4?G4>=+?mFgUE+O38yzMS58D%=$x>Ju6GfL%PZ7XUMiO>ng{*| 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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