Commit 9c4c16ac authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #2772 from blockscout/release/v2-1-0

Fixes for release v2.1.0
parents 02393619 7d9f94e4
......@@ -7,12 +7,12 @@ const title = 'Ton Application Chain (TAC)';
const tonExplorerUrl = getEnvValue('NEXT_PUBLIC_TAC_TON_EXPLORER_URL');
const config: Feature<{ explorerUrl: string }> = (() => {
const config: Feature<{ tonExplorerUrl: string }> = (() => {
if (apis.tac && tonExplorerUrl) {
return Object.freeze({
title,
isEnabled: true,
explorerUrl: tonExplorerUrl,
tonExplorerUrl,
});
}
......
......@@ -3,7 +3,7 @@
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=tac_turin"
NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST=https://tac-operation-lifecycle.k8s-dev.blockscout.com
NEXT_PUBLIC_TAC_TON_EXPLORER_URL=https://testnet.tonscan.org
NEXT_PUBLIC_TAC_TON_EXPLORER_URL=https://testnet.tonviewer.com
# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
......
......@@ -16,67 +16,67 @@ All json-like values should be single-quoted. If it contains a hash (`#`) or a d
&nbsp;
## Table of contents
- [App configuration](ENVS.md#app-configuration)
- [Blockchain parameters](ENVS.md#blockchain-parameters)
- [API configuration](ENVS.md#api-configuration)
- [UI configuration](ENVS.md#ui-configuration)
- [Homepage](ENVS.md#homepage)
- [Navigation](ENVS.md#navigation)
- [Footer](ENVS.md#footer)
- [Favicon](ENVS.md#favicon)
- [Meta](ENVS.md#meta)
- [Views](ENVS.md#views)
- [Block](ENVS.md#block-views)
- [Address](ENVS.md#address-views)
- [Transaction](ENVS.md#transaction-views)
- [NFT](ENVS.md#nft-views)
- [Misc](ENVS.md#misc)
- [App features](ENVS.md#app-features)
- [My account](ENVS.md#my-account)
- [Gas tracker](ENVS.md#gas-tracker)
- [Advanced filter](ENVS.md#advanced-filter)
- [Address verification](ENVS.md#address-verification-in-my-account) in "My account"
- [Blockchain interaction](ENVS.md#blockchain-interaction-writing-to-contract-etc) (writing to contract, etc.)
- [Banner ads](ENVS.md#banner-ads)
- [Text ads](ENVS.md#text-ads)
- [Beacon chain](ENVS.md#beacon-chain)
- [User operations](ENVS.md#user-operations-erc-4337)
- [Rollup chain](ENVS.md#rollup-chain)
- [Export data to CSV file](ENVS.md#export-data-to-csv-file)
- [Google analytics](ENVS.md#google-analytics)
- [Mixpanel analytics](ENVS.md#mixpanel-analytics)
- [GrowthBook feature flagging and A/B testing](ENVS.md#growthbook-feature-flagging-and-ab-testing)
- [GraphQL API documentation](ENVS.md#graphql-api-documentation)
- [REST API documentation](ENVS.md#rest-api-documentation)
- [Marketplace](ENVS.md#marketplace)
- [Solidity to UML diagrams](ENVS.md#solidity-to-uml-diagrams)
- [Blockchain statistics](ENVS.md#blockchain-statistics)
- [Web3 wallet integration](ENVS.md#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Transaction interpretation](ENVS.md#transaction-interpretation)
- [Verified tokens info](ENVS.md#verified-tokens-info)
- [Name service integration](ENVS.md#name-service-integration)
- [Metadata service integration](ENVS.md#metadata-service-integration)
- [Public tag submission](ENVS.md#public-tag-submission)
- [Data availability](ENVS.md#data-availability)
- [Bridged tokens](ENVS.md#bridged-tokens)
- [Safe{Core} address tags](ENVS.md#safecore-address-tags)
- [Address profile API](ENVS.md#address-profile-api)
- [Address XStar XHS score](ENVS.md#address-xstar-xhs-score)
- [SUAVE chain](ENVS.md#suave-chain)
- [Celo chain](ENVS.md#celo-chain)
- [Ton Application Chain (TAC)](ENVS.md#ton-application-chain-tac)
- [MetaSuites extension](ENVS.md#metasuites-extension)
- [Validators list](ENVS.md#validators-list)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [Rollbar error monitoring](ENVS.md#rollbar-error-monitoring)
- [OpenTelemetry](ENVS.md#opentelemetry)
- [DeFi dropdown](ENVS.md#defi-dropdown)
- [Multichain balance button](ENVS.md#multichain-balance-button)
- [Get gas button](ENVS.md#get-gas-button)
- [Save on gas with GasHawk](ENVS.md#save-on-gas-with-gashawk)
- [Rewards service API](ENVS.md#rewards-service-api)
- [DEX pools](ENVS.md#dex-pools)
- [3rd party services configuration](ENVS.md#external-services-configuration)
- [App configuration](#app-configuration)
- [Blockchain parameters](#blockchain-parameters)
- [API configuration](#api-configuration)
- [UI configuration](#ui-configuration)
- [Homepage](#homepage)
- [Navigation](#navigation)
- [Footer](#footer)
- [Favicon](#favicon)
- [Meta](#meta)
- [Views](#views)
- [Block](#block-views)
- [Address](#address-views)
- [Transaction](#transaction-views)
- [NFT](#nft-views)
- [Misc](#misc)
- [App features](#app-features)
- [My account](#my-account)
- [Gas tracker](#gas-tracker)
- [Advanced filter](#advanced-filter)
- [Address verification](#address-verification-in-my-account) in "My account"
- [Blockchain interaction](#blockchain-interaction-writing-to-contract-etc) (writing to contract, etc.)
- [Banner ads](#banner-ads)
- [Text ads](#text-ads)
- [Beacon chain](#beacon-chain)
- [User operations](#user-operations-erc-4337)
- [Rollup chain](#rollup-chain)
- [Export data to CSV file](#export-data-to-csv-file)
- [Google analytics](#google-analytics)
- [Mixpanel analytics](#mixpanel-analytics)
- [GrowthBook feature flagging and A/B testing](#growthbook-feature-flagging-and-ab-testing)
- [GraphQL API documentation](#graphql-api-documentation)
- [REST API documentation](#rest-api-documentation)
- [Marketplace](#marketplace)
- [Solidity to UML diagrams](#solidity-to-uml-diagrams)
- [Blockchain statistics](#blockchain-statistics)
- [Web3 wallet integration](#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Transaction interpretation](#transaction-interpretation)
- [Verified tokens info](#verified-tokens-info)
- [Name service integration](#name-service-integration)
- [Metadata service integration](#metadata-service-integration)
- [Public tag submission](#public-tag-submission)
- [Data availability](#data-availability)
- [Bridged tokens](#bridged-tokens)
- [Safe{Core} address tags](#safecore-address-tags)
- [Address profile API](#address-profile-api)
- [Address XStar XHS score](#address-xstar-xhs-score)
- [SUAVE chain](#suave-chain)
- [Celo chain](#celo-chain)
- [Ton Application Chain (TAC)](#ton-application-chain-tac)
- [MetaSuites extension](#metasuites-extension)
- [Validators list](#validators-list)
- [Sentry error monitoring](#sentry-error-monitoring)
- [Rollbar error monitoring](#rollbar-error-monitoring)
- [OpenTelemetry](#opentelemetry)
- [DeFi dropdown](#defi-dropdown)
- [Multichain balance button](#multichain-balance-button)
- [Get gas button](#get-gas-button)
- [Save on gas with GasHawk](#save-on-gas-with-gashawk)
- [Rewards service API](#rewards-service-api)
- [DEX pools](#dex-pools)
- [3rd party services configuration](#external-services-configuration)
&nbsp;
......@@ -369,7 +369,7 @@ Settings for meta tags, OG tags and SEO
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` | Set to true if network has account feature | Required | - | `true` | v1.0.x+ |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `boolean` | See [below](ENVS.md#google-recaptcha) | Required | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `boolean` | See [below](#google-recaptcha) | Required | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | **DEPRECATED** Client id for [Auth0](https://auth0.com/) provider | - | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_AUTH_URL | `string` | **DEPRECATED** Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | - | - | `https://blockscout.com` | v1.0.x+ |
| NEXT_PUBLIC_LOGOUT_URL | `string` | **DEPRECATED** Account logout url. Required if account is supported for the app instance. | - | - | `https://blockscoutcom.us.auth0.com/v2/logout` | v1.0.x+ |
......@@ -399,7 +399,7 @@ This feature is **enabled by default**. To switch it off pass `NEXT_PUBLIC_ADVAN
### Address verification in "My account"
*Note* all ENV variables required for [My account](ENVS.md#my-account) feature should be passed alongside the following ones:
*Note* all ENV variables required for [My account](#my-account) feature should be passed alongside the following ones:
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
......@@ -413,12 +413,12 @@ This feature is **enabled by default**. To switch it off pass `NEXT_PUBLIC_ADVAN
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID | `string` | Project id for [WalletConnect](https://cloud.walletconnect.com/) integration | Required | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_NAME | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `Gnosis Chain` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_ID | `number` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `99` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `Ether` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `ETH` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | - | `18` | `6` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_NAME | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `Gnosis Chain` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_ID | `number` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `99` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `Ether` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `ETH` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | See in [Blockchain parameters](#blockchain-parameters) section | - | `18` | `6` | v1.0.x+ |
&nbsp;
......@@ -498,7 +498,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](ENVS.md#google-recaptcha) | true | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](#google-recaptcha) | true | - | `<your-secret>` | v1.0.x+ |
&nbsp;
......@@ -555,7 +555,7 @@ This feature is **always enabled**, but you can disable it by passing `none` val
| NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url. Can be used instead of NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | - | - | `https://admin-rs.services.blockscout.com` | v1.1.0+ |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | Required | - | `https://airtable.com/shrqUAcjgGJ4jU88C` | v1.0.x+ |
| NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM | `string` | Link to form where users can suggest ideas for the marketplace | - | - | `https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form` | v1.24.0+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](ENVS.md#blockchain-parameters) section | Required | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL | `string` | URL of configuration file (`.json` format only) which contains the list of categories to be displayed on the marketplace page in the specified order. If no URL is provided, then the list of categories will be compiled based on the `categories` fields from the marketplace (apps) configuration file | - | - | `https://example.com/marketplace_categories.json` | v1.23.0+ |
| NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL | `string` | URL of configuration file (`.json` format only) which contains app security reports for displaying security scores on the Marketplace page | - | - | `https://example.com/marketplace_security_reports.json` | v1.28.0+ |
| NEXT_PUBLIC_MARKETPLACE_FEATURED_APP | `string` | ID of the featured application to be displayed on the banner on the Marketplace page | - | - | `uniswap` | v1.29.0+ |
......@@ -668,7 +668,7 @@ This feature allows you to submit an application with a public address tag.
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_METADATA_SERVICE_API_HOST | `string` | Metadata Service API endpoint url | Required | - | `https://metadata.services.blockscout.com` | v1.30.0+ |
| NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url | Required | - | `https://admin-rs.services.blockscout.com` | v1.1.0+ |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](ENVS.md#google-recaptcha) | true | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](#google-recaptcha) | true | - | `<your-secret>` | v1.0.x+ |
&nbsp;
......@@ -900,7 +900,7 @@ The feature enables a "Save with GasHawk" button next to the "Gas used" value on
### Rewards service API
This feature enables Blockscout Merits program. It requires that the [My account](ENVS.md#my-account) and [Blockchain interaction](ENVS.md#blockchain-interaction-writing-to-contract-etc) features are also enabled.
This feature enables Blockscout Merits program. It requires that the [My account](#my-account) and [Blockchain interaction](#blockchain-interaction-writing-to-contract-etc) features are also enabled.
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
......
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.05.194a10 10 0 1 1 3.903 19.614A10 10 0 0 1 8.049.194ZM10 1.539a8.462 8.462 0 1 0 0 16.925 8.462 8.462 0 0 0 0-16.925Zm0 1.538a.77.77 0 0 1 .77.77v5.838l4.007 4a.771.771 0 0 1 .169.841.772.772 0 0 1-1.01.417.77.77 0 0 1-.251-.165l-4.23-4.232A.77.77 0 0 1 9.23 10V3.847a.77.77 0 0 1 .77-.77Z" fill="currentColor"/>
<path d="M19.697 9.64a9.704 9.704 0 0 0-1.434-4.73l-.194-.303a9.705 9.705 0 0 0-4.026-3.43l-.33-.143A9.703 9.703 0 0 0 8.461.419l-.354.064a9.705 9.705 0 0 0-4.71 2.405l-.259.25a9.705 9.705 0 0 0-2.58 4.618l-.075.35a9.705 9.705 0 0 0 .552 5.607l.143.33a9.704 9.704 0 0 0 3.43 4.025l.303.194a9.706 9.706 0 0 0 5.09 1.442l.48-.012a9.704 9.704 0 0 0 6.38-2.83l.332-.349A9.706 9.706 0 0 0 19.703 10l-.006-.359Zm-1.518-.047a8.189 8.189 0 0 0-2.11-5.09l-.28-.293a8.188 8.188 0 0 0-5.383-2.389L10 1.811a8.19 8.19 0 0 0-4.294 1.217l-.256.163A8.19 8.19 0 0 0 2.556 6.59l-.121.278a8.19 8.19 0 0 0-.466 4.73l.065.297A8.188 8.188 0 0 0 4.21 15.79l.218.21a8.19 8.19 0 0 0 3.974 2.03l.298.054a8.19 8.19 0 0 0 4.433-.52l.278-.12a8.19 8.19 0 0 0 3.397-2.895l.164-.255a8.19 8.19 0 0 0 1.216-4.295l-.01-.406ZM10.61 3.937a.61.61 0 0 0-.102-.339l-.077-.092a.61.61 0 0 0-.311-.168L10 3.327a.61.61 0 0 0-.338.102l-.093.077a.61.61 0 0 0-.179.43V10c0 .08.015.16.045.235l.056.105a.618.618 0 0 0 .075.092l4.17 4.17c.056.055.123.1.198.13l.115.035c.039.008.079.01.118.01h.002l.119-.01a.615.615 0 0 0 .115-.035l.107-.056a.601.601 0 0 0 .092-.075l.077-.093a.609.609 0 0 0 .057-.106l.035-.114.011-.12a.67.67 0 0 0-.011-.12l-.035-.115a.61.61 0 0 0-.134-.198L10.61 9.75V3.937Zm7.869 6.376a8.486 8.486 0 0 1-1.255 4.136l-.17.264a8.486 8.486 0 0 1-3.52 2.999l-.287.126a8.486 8.486 0 0 1-4.594.538l-.309-.055a8.484 8.484 0 0 1-4.117-2.104L4 15.999a8.485 8.485 0 0 1-2.254-4.037l-.068-.307a8.486 8.486 0 0 1 .484-4.902l.126-.288a8.486 8.486 0 0 1 2.999-3.52l.263-.17A8.486 8.486 0 0 1 10 1.516l.42.01A8.485 8.485 0 0 1 16 4l.289.305A8.484 8.484 0 0 1 18.485 10l-.006.314Zm1.509.182a10.002 10.002 0 0 1-2.575 6.216l-.342.36a10.001 10.001 0 0 1-6.575 2.916L10 20a10 10 0 0 1-5.244-1.486l-.311-.2A10.002 10.002 0 0 1 .91 14.166l-.148-.34a10.001 10.001 0 0 1-.57-5.777l.079-.362A10.002 10.002 0 0 1 2.928 2.93l.268-.257A10.001 10.001 0 0 1 8.049.192l.365-.065a10 10 0 0 1 5.413.634l.34.148a10.003 10.003 0 0 1 4.148 3.535l.2.312A10.002 10.002 0 0 1 20 9.999l-.012.496Zm-9.081-.869 3.904 3.899a.907.907 0 0 1 .198.294l.03.085c.026.085.04.174.04.263l-.005.09a.903.903 0 0 1-.035.174l-.03.085a.908.908 0 0 1-.138.229l-.06.066a.896.896 0 0 1-.216.157l-.08.037a.907.907 0 0 1-.61.03l-.083-.03a.904.904 0 0 1-.295-.194l-4.17-4.169a.914.914 0 0 1-.157-.214l-.038-.081a.91.91 0 0 1-.068-.349V3.937c0-.24.096-.471.265-.64l.067-.06A.906.906 0 0 1 10 3.03l.09.005a.914.914 0 0 1 .551.261l.06.066a.906.906 0 0 1 .206.575v5.69Z" fill="currentColor"/>
<path d="M8.164.859a9.3 9.3 0 0 1 8.3 2.457l.047.046c.136.134.268.272.395.415l.03.033c.207.234.403.478.587.733l.039.055.15.214c.087.131.17.266.25.401.15.252.29.51.415.773.012.024.022.049.033.073.084.18.161.362.233.546l.03.08a9.3 9.3 0 0 1 .607 3.297l-.012.46a9.301 9.301 0 0 1-2.395 5.781l-.317.334a9.3 9.3 0 0 1-6.114 2.712l-.461.012A9.3 9.3 0 0 1 5.105 17.9l-.29-.187a9.303 9.303 0 0 1-.819-.615l-.055-.049a9.33 9.33 0 0 1-.438-.397l-.098-.098a8.814 8.814 0 0 1-.351-.371l-.078-.088a9.32 9.32 0 0 1-.16-.188l-.062-.075a9.397 9.397 0 0 1-.294-.383A9.297 9.297 0 0 1 8.164.859Zm1.817 1.509a7.617 7.617 0 0 0-3.994 1.13l-.237.153A7.616 7.616 0 0 0 3.06 6.81l-.113.259a7.615 7.615 0 0 0-.433 4.398l.06.277a7.613 7.613 0 0 0 2.024 3.622l.203.196a7.615 7.615 0 0 0 3.695 1.888l.278.05a7.614 7.614 0 0 0 4.12-.484l.26-.112a7.615 7.615 0 0 0 3.159-2.692l.152-.237a7.616 7.616 0 0 0 1.13-3.993l-.008-.378a7.614 7.614 0 0 0-1.962-4.732l-.26-.274a7.614 7.614 0 0 0-5.005-2.22l-.378-.01Zm.083 1.138c.192.02.374.104.512.242l.056.062c.123.15.191.339.191.534v5.29l3.631 3.626c.079.078.142.17.185.274l.027.078c.024.08.037.162.037.245l-.005.084a.84.84 0 0 1-.032.162l-.027.078a.852.852 0 0 1-.129.213l-.057.062a.837.837 0 0 1-.199.146l-.075.034a.841.841 0 0 1-.566.029l-.079-.029a.842.842 0 0 1-.274-.18l-3.877-3.877a.853.853 0 0 1-.146-.2l-.036-.075a.846.846 0 0 1-.063-.323V4.344c0-.223.09-.438.247-.596l.061-.055a.844.844 0 0 1 .535-.192l.083.005Z" fill="currentColor"/>
</svg>
import getPageType from './getPageType';
import getPageType, { PAGE_TYPE_DICT } from './getPageType';
import logEvent from './logEvent';
import reset from './reset';
import useInit from './useInit';
......@@ -13,4 +13,5 @@ export {
getPageType,
userProfile,
reset,
PAGE_TYPE_DICT,
};
......@@ -22,11 +22,11 @@ export function getTacOperationStatus(type: tac.OperationType) {
}
export function getTacOperationStage(data: tac.OperationDetails, txHash: string) {
const currentStep = data.status_history.find((step) => step.transactions.some((tx) => tx.hash.toLowerCase() === txHash.toLowerCase()));
if (!currentStep) {
return null;
const currentStep = data.status_history.filter((step) => step.transactions.some((tx) => tx.hash.toLowerCase() === txHash.toLowerCase()));
if (currentStep.length === 0) {
return;
}
return STATUS_LABELS[currentStep.type];
return currentStep.map((step) => STATUS_LABELS[step.type]);
}
export const STATUS_SEQUENCE: Array<tac.OperationStage_StageType> = [
......
......@@ -107,6 +107,6 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
],
tac: [
[ 'NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST', 'http://localhost:3100' ],
[ 'NEXT_PUBLIC_TAC_TON_EXPLORER_URL', 'https://testnet.tonscan.org' ],
[ 'NEXT_PUBLIC_TAC_TON_EXPLORER_URL', 'https://testnet.tonviewer.com' ],
],
};
......@@ -4,7 +4,7 @@ import { ADDRESS_HASH } from './addressParams';
export const TAC_OPERATION: tac.OperationBriefDetails = {
operation_id: '0x4d3d36b7fcab0a2f93f24bf313ebfe9cc0b2c7157d2aef7e7f7d5835528428c6',
type: tac.OperationType.PENDING,
type: tac.OperationType.TAC_TON,
timestamp: '2025-05-05T12:32:22.000Z',
sender: {
address: '0x4d3d36b7fcab0a2f93f24bf313ebfe9cc0b2c7157d2aef7e7f7d5835528428c6',
......@@ -14,7 +14,7 @@ export const TAC_OPERATION: tac.OperationBriefDetails = {
export const TAC_OPERATION_DETAILS: tac.OperationDetails = {
operation_id: '0x6e7cdeea3f39e7664597a44ddb33ce47ba061cbee2992e2c7b0e3f9294ff8b30',
type: tac.OperationType.PENDING,
type: tac.OperationType.TAC_TON,
timestamp: '2025-05-05T12:32:22.000Z',
sender: {
address: ADDRESS_HASH,
......
......@@ -32,6 +32,10 @@ type Props = {
const DateInput = ({ value, onChange, placeholder, max }: { value: string; onChange: (value: string) => void; placeholder: string; max: string }) => {
const [ tempValue, setTempValue ] = React.useState(value ? dayjs(value).format('YYYY-MM-DD') : '');
React.useEffect(() => {
setTempValue(value ? dayjs(value).format('YYYY-MM-DD') : '');
}, [ value ]);
const handleChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => {
setTempValue(event.target.value);
onChange(event.target.value);
......@@ -96,7 +100,7 @@ const AgeFilter = ({ value = defaultValue, handleFilterChange, onClose }: Props)
<TableColumnFilter
title="Set last duration"
isFilled={ Boolean(currentValue.from || currentValue.to || currentValue.age) }
isTouched={ currentValue.age ? value.age !== currentValue.age : Boolean(currentValue.from && currentValue.to && !isEqual(currentValue, value)) }
isTouched={ currentValue.age ? value.age !== currentValue.age : !isEqual(currentValue, value) }
onFilter={ onFilter }
onReset={ onReset }
hasReset
......
......@@ -8,6 +8,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index';
import { Link } from 'toolkit/chakra/link';
import { BackToButton } from 'toolkit/components/buttons/BackToButton';
import { makePrettyLink } from 'toolkit/utils/url';
......@@ -46,6 +47,10 @@ const MarketplaceAppTopBar = ({ appId, data, isLoading, securityReport }: Props)
const showContractList = React.useCallback((id: string, type: ContractListTypes) => setContractListType(type), []);
const hideContractList = React.useCallback(() => setContractListType(undefined), []);
const handleBackToClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Back to', Source: mixpanel.PAGE_TYPE_DICT['/apps/[id]'] });
}, []);
return (
<>
<Flex alignItems="center" mb={{ base: 3, md: 2 }} rowGap={ 3 } columnGap={ 2 }>
......@@ -54,6 +59,7 @@ const MarketplaceAppTopBar = ({ appId, data, isLoading, securityReport }: Props)
href={ goBackUrl }
hint="Back to dApps list"
loading={ isLoading }
onClick={ handleBackToClick }
/>
<Link
external
......
......@@ -7,6 +7,7 @@ import { sortStatusHistory } from 'lib/operations/tac';
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp';
import AddressEntityTacTon from 'ui/shared/entities/address/AddressEntityTacTon';
import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus';
import TacOperationLifecycleAccordion from './TacOperationLifecycleAccordion';
......@@ -43,6 +44,16 @@ const TacOperationDetails = ({ isLoading, data }: Props) => {
</>
) }
<DetailedInfo.ItemLabel
hint="The status of the operation"
isLoading={ isLoading }
>
Status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TacOperationStatus status={ data.type } isLoading={ isLoading }/>
</DetailedInfo.ItemValue>
{ data.timestamp && (
<>
<DetailedInfo.ItemLabel
......@@ -66,7 +77,7 @@ const TacOperationDetails = ({ isLoading, data }: Props) => {
Lifecycle
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TacOperationLifecycleAccordion data={ statusHistory } isLoading={ isLoading }/>
<TacOperationLifecycleAccordion data={ statusHistory } isLoading={ isLoading } type={ data.type }/>
</DetailedInfo.ItemValue>
</>
) }
......
import React from 'react';
import type * as tac from '@blockscout/tac-operation-lifecycle-types';
import * as tac from '@blockscout/tac-operation-lifecycle-types';
import { AccordionItem, AccordionRoot } from 'toolkit/chakra/accordion';
......@@ -10,13 +10,16 @@ import TacOperationLifecycleAccordionItemTrigger from './TacOperationLifecycleAc
interface Props {
data: tac.OperationDetails['status_history'];
isLoading?: boolean;
type: tac.OperationType;
}
const TacOperationLifecycleAccordion = ({ data, isLoading }: Props) => {
const TacOperationLifecycleAccordion = ({ data, isLoading, type }: Props) => {
const isPending = type === tac.OperationType.PENDING && !isLoading;
return (
<AccordionRoot maxW="800px" display="flex" flexDirection="column" rowGap={ 6 } lazyMount>
{ data.map((item, index) => {
const isLast = index === data.length - 1;
const isLast = index === data.length - 1 && !isPending;
return (
<AccordionItem key={ index } value={ item.type } borderBottomWidth="0px">
<TacOperationLifecycleAccordionItemTrigger
......@@ -33,6 +36,17 @@ const TacOperationLifecycleAccordion = ({ data, isLoading }: Props) => {
</AccordionItem>
);
}) }
{ isPending && (
<AccordionItem value="pending" borderBottomWidth="0px">
<TacOperationLifecycleAccordionItemTrigger
status="pending"
isFirst={ false }
isLast={ true }
isLoading={ isLoading }
isSuccess={ false }
/>
</AccordionItem>
) }
</AccordionRoot>
);
};
......
import { HStack } from '@chakra-ui/react';
import { Box, HStack, Spinner } from '@chakra-ui/react';
import React from 'react';
import type * as tac from '@blockscout/tac-operation-lifecycle-types';
......@@ -9,7 +9,7 @@ import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
status: tac.OperationStage_StageType;
status: tac.OperationStage_StageType | 'pending';
isFirst: boolean;
isLast: boolean;
isLoading?: boolean;
......@@ -17,6 +17,32 @@ interface Props {
}
const TacOperationLifecycleAccordionItemTrigger = ({ status, isFirst, isLast, isSuccess, isLoading }: Props) => {
const content = (() => {
switch (status) {
case 'pending': {
return (
<HStack gap={ 2 }>
<Spinner size="md"/>
<Box color="text.secondary">
Pending
</Box>
</HStack>
);
}
default: {
return (
<HStack gap={ 2 } color={ isSuccess ? 'green.500' : 'red.600' }>
<IconSvg name={ isSuccess ? 'verification-steps/finalized' : 'verification-steps/error' } boxSize={ 5 } isLoading={ isLoading }/>
<Skeleton loading={ isLoading }>
{ STATUS_LABELS[status] }
</Skeleton>
</HStack>
);
}
}
})();
return (
<AccordionItemTrigger
position="relative"
......@@ -47,15 +73,14 @@ const TacOperationLifecycleAccordionItemTrigger = ({ status, isFirst, isLast, is
height: { base: '14px', lg: '6px' },
},
}}
disabled={ isLoading }
noIndicator={ isLoading }
disabled={ isLoading || status === 'pending' }
noIndicator={ isLoading || status === 'pending' }
cursor={ status === 'pending' ? 'default' : 'pointer' }
_disabled={{
opacity: status === 'pending' ? 1 : 'control.disabled',
}}
>
<HStack gap={ 2 } color={ isSuccess ? 'green.500' : 'red.600' }>
<IconSvg name={ isSuccess ? 'verification-steps/finalized' : 'verification-steps/error' } boxSize={ 5 } isLoading={ isLoading }/>
<Skeleton loading={ isLoading }>
{ STATUS_LABELS[status] }
</Skeleton>
</HStack>
{ content }
</AccordionItemTrigger>
);
};
......
......@@ -18,6 +18,7 @@ const TacOperationsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Value>
<OperationEntity
id={ item.operation_id }
type={ item.type }
isLoading={ isLoading }
/>
</ListItemMobileGrid.Value>
......
......@@ -19,12 +19,12 @@ const TacOperationsTable = ({ items, isLoading }: Props) => {
<TableRoot minW="950px">
<TableHeaderSticky top={ 68 }>
<TableRow>
<TableColumnHeader w="200px">Status</TableColumnHeader>
<TableColumnHeader w="100%">Operation</TableColumnHeader>
<TableColumnHeader w="200px">
Timestamp
<TimeFormatToggle/>
</TableColumnHeader>
<TableColumnHeader w="200px">Status</TableColumnHeader>
<TableColumnHeader w="250px">Sender</TableColumnHeader>
</TableRow>
</TableHeaderSticky>
......
......@@ -15,10 +15,15 @@ interface Props {
const TacOperationsTableItem = ({ item, isLoading }: Props) => {
return (
<TableRow>
<TableCell verticalAlign="middle">
<TacOperationStatus status={ item.type } isLoading={ isLoading }/>
</TableCell>
<TableCell verticalAlign="middle">
<OperationEntity
id={ item.operation_id }
type={ item.type }
isLoading={ isLoading }
truncation="constant_long"
/>
</TableCell>
<TableCell verticalAlign="middle">
......@@ -28,9 +33,6 @@ const TacOperationsTableItem = ({ item, isLoading }: Props) => {
color="text.secondary"
/>
</TableCell>
<TableCell verticalAlign="middle">
<TacOperationStatus status={ item.type } isLoading={ isLoading }/>
</TableCell>
<TableCell verticalAlign="middle" pr={ 12 }>
{ item.sender ? (
<AddressEntityTacTon
......
......@@ -47,7 +47,7 @@ const RewardsDashboard = () => {
return (
<>
<Flex gap={ 3 } justifyContent="space-between">
<Flex gap={ 3 } justifyContent="space-between" mb={ 6 }>
<PageTitle
title="Dashboard"
secondRow={ (
......@@ -58,6 +58,7 @@ const RewardsDashboard = () => {
to earn, spend, and learn more about the program.
</span>
) }
mb={ 0 }
/>
<AdBanner platform="mobile" w="fit-content" flexShrink={ 0 } borderRadius="md" overflow="hidden" display={{ base: 'none', lg: 'block ' }}/>
</Flex>
......
import React from 'react';
import * as tac from '@blockscout/tac-operation-lifecycle-types';
import * as tacOperationMock from 'mocks/operations/tac';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect } from 'playwright/lib';
......@@ -27,3 +29,25 @@ test('base view +@dark-mode +@mobile', async({ render, mockTextAd, mockApiRespon
await component.getByRole('button', { name: 'Executed in TON' }).click();
await expect(component).toHaveScreenshot();
});
test('pending operation', async({ render, mockTextAd, mockApiResponse, mockEnvs }) => {
await mockEnvs(ENVS_MAP.tac);
await mockTextAd();
await mockApiResponse('tac:operation', {
... tacOperationMock.tacOperation,
type: tac.OperationType.PENDING,
}, {
pathParams: { id: tacOperationMock.tacOperation.operation_id },
});
const component = await render(
<TacOperation/>,
{ hooksConfig: {
router: {
query: { id: tacOperationMock.tacOperation.operation_id },
isReady: true,
},
} },
);
await expect(component).toHaveScreenshot();
});
......@@ -44,7 +44,7 @@ const TacOperation = () => {
) : null;
const titleSecondRow = (
<OperationEntity id={ id } noLink variant="subheading"/>
<OperationEntity id={ id } noLink variant="subheading" type={ query.data?.type }/>
);
return (
......
......@@ -32,7 +32,7 @@ import IconSvg from 'ui/shared/IconSvg';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TacOperationTag from 'ui/shared/TacOperationTag';
import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus';
import SearchResultEntityTag from './SearchResultEntityTag';
......@@ -219,7 +219,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
case 'tac_operation': {
return (
<OperationEntity.Container>
<OperationEntity.Icon/>
<OperationEntity.Icon type={ data.tac_operation.type }/>
<OperationEntity.Link
isLoading={ isLoading }
id={ data.tac_operation.operation_id }
......@@ -233,7 +233,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
mr={ 2 }
/>
</OperationEntity.Link>
<TacOperationTag type={ data.tac_operation.type }/>
<TacOperationStatus status={ data.tac_operation.type }/>
</OperationEntity.Container>
);
}
......
......@@ -32,7 +32,7 @@ import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg';
import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TacOperationTag from 'ui/shared/TacOperationTag';
import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus';
import SearchResultEntityTag from './SearchResultEntityTag';
......@@ -331,7 +331,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
<>
<TableCell colSpan={ 2 } fontSize="sm">
<OperationEntity.Container>
<OperationEntity.Icon/>
<OperationEntity.Icon type={ data.tac_operation.type }/>
<OperationEntity.Link
isLoading={ isLoading }
id={ data.tac_operation.operation_id }
......@@ -345,7 +345,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
mr={ 2 }
/>
</OperationEntity.Link>
<TacOperationTag type={ data.tac_operation.type }/>
<TacOperationStatus status={ data.tac_operation.type }/>
</OperationEntity.Container>
</TableCell>
<TableCell fontSize="sm" verticalAlign="middle" isNumeric>
......
import { Flex, chakra } from '@chakra-ui/react';
import { debounce } from 'es-toolkit';
import { useRouter } from 'next/router';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index';
import { Heading } from 'toolkit/chakra/heading';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
......@@ -29,10 +31,12 @@ const TEXT_MAX_LINES = 1;
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => {
const tooltip = useDisclosure();
const isMobile = useIsMobile();
const router = useRouter();
const [ isTextTruncated, setIsTextTruncated ] = React.useState(false);
const headingRef = React.useRef<HTMLHeadingElement>(null);
const textRef = React.useRef<HTMLSpanElement>(null);
const pageType = mixpanel.getPageType(router.pathname);
const updatedTruncateState = React.useCallback(() => {
if (!headingRef.current || !textRef.current) {
......@@ -71,6 +75,11 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
}
}, [ tooltip ]);
const handleBackToClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Back to', Source: pageType });
backLink && 'onClick' in backLink && backLink.onClick();
}, [ backLink, pageType ]);
return (
<Flex className={ className } flexDir="column" rowGap={ 3 } mb={ 6 }>
<Flex
......@@ -85,7 +94,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
<BackToButton
hint={ backLink.label }
href={ 'url' in backLink ? backLink.url : undefined }
onClick={ 'onClick' in backLink ? backLink.onClick : undefined }
onClick={ handleBackToClick }
loadingSkeleton={ isLoading }
mr={ 3 }
/>
......
......@@ -68,7 +68,7 @@ const Icon = (props: IconProps) => {
const isProxy = Boolean(props.address.implementations?.length);
const isVerified = isProxy ? props.address.is_verified && props.address.implementations?.every(({ name }) => Boolean(name)) : props.address.is_verified;
const contractIconName: EntityBase.IconBaseProps['name'] = props.address.is_verified ? 'contracts/verified' : 'contracts/regular';
const label = (isVerified ? 'verified ' : '') + (isProxy ? 'proxy contract' : 'contract') + props.hintPostfix;
const label = (isVerified ? 'verified ' : '') + (isProxy ? 'proxy contract' : 'contract') + (props.hintPostfix ?? '');
return (
<EntityBase.Icon
......@@ -83,7 +83,7 @@ const Icon = (props: IconProps) => {
const label = (() => {
if (isDelegatedAddress) {
return (props.address.is_verified ? 'EOA + verified code' : 'EOA + code') + props.hintPostfix;
return (props.address.is_verified ? 'EOA + verified code' : 'EOA + code') + (props.hintPostfix ?? '');
}
return props.hint;
......
......@@ -23,7 +23,7 @@ const AddressEntityTacTon = (props: Props) => {
const href = (() => {
switch (props.chainType) {
case tac.BlockchainType.TON:
return tacFeature.explorerUrl + route({
return tacFeature.tonExplorerUrl + route({
pathname: '/address/[hash]',
query: {
...props.query,
......
import { chakra } from '@chakra-ui/react';
import { Spinner, chakra } from '@chakra-ui/react';
import React from 'react';
import * as tac from '@blockscout/tac-operation-lifecycle-types';
import { route } from 'nextjs-routes';
import * as EntityBase from 'ui/shared/entities/base/components';
......@@ -22,7 +24,14 @@ const Link = chakra((props: LinkProps) => {
);
});
const Icon = (props: EntityBase.IconBaseProps) => {
type IconProps = EntityBase.IconBaseProps & Pick<EntityProps, 'type'>;
const Icon = (props: IconProps) => {
switch (props.type) {
case tac.OperationType.PENDING: {
return <Spinner size="md" marginRight={ props.marginRight ?? '8px' }/>;
}
default: {
return (
<EntityBase.Icon
{ ...props }
......@@ -30,6 +39,8 @@ const Icon = (props: EntityBase.IconBaseProps) => {
borderRadius="none"
/>
);
}
}
};
type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<EntityProps, 'id'>;
......@@ -58,6 +69,7 @@ const Container = EntityBase.Container;
export interface EntityProps extends EntityBase.EntityBaseProps {
id: string;
type: tac.OperationType | undefined;
}
const OperationEntity = (props: EntityProps) => {
......
......@@ -14,7 +14,7 @@ const TxEntityTon = (props: TxEntity.EntityProps) => {
}
const formattedHash = props.hash.replace(/^0x/, '');
const defaultHref = `${ stripTrailingSlash(tacFeature.explorerUrl) }/tx/${ formattedHash }`;
const defaultHref = `${ stripTrailingSlash(tacFeature.tonExplorerUrl) }/transaction/${ formattedHash }`;
return <TxEntity.default { ...props } hash={ formattedHash } href={ props.href ?? defaultHref } icon={{ name: 'brands/ton' }} isExternal/>;
};
......
import React from 'react';
import { useSettingsContext } from 'lib/contexts/settings';
import * as mixpanel from 'lib/mixpanel/index';
import { IconButton } from 'toolkit/chakra/icon-button';
import type { IconButtonProps } from 'toolkit/chakra/icon-button';
import { Tooltip } from 'toolkit/chakra/tooltip';
......@@ -12,12 +13,17 @@ const TimeFormatToggle = (props: Props) => {
const settings = useSettingsContext();
const timeFormat = settings?.timeFormat || 'relative';
const handleClick = React.useCallback(() => {
settings?.toggleTimeFormat();
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Switch time format', Source: 'Table header' });
}, [ settings ]);
return (
<Tooltip content="Toggle time format">
<IconButton
aria-label="Toggle time format"
variant="icon_secondary"
onClick={ settings?.toggleTimeFormat }
onClick={ handleClick }
boxSize={ 5 }
selected={ timeFormat === 'absolute' }
borderRadius="sm"
......
......@@ -7,6 +7,7 @@ import { NETWORK_GROUPS } from 'types/networks';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useFetch from 'lib/hooks/useFetch';
import * as mixpanel from 'lib/mixpanel/index';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
export default function useNetworkMenu() {
......@@ -20,14 +21,21 @@ export default function useNetworkMenu() {
staleTime: Infinity,
});
const handleOpenChange = React.useCallback((details: { open: boolean }) => {
if (details.open) {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Network menu', Source: 'Header' });
}
onOpenChange(details);
}, [ onOpenChange ]);
return React.useMemo(() => ({
open,
onClose,
onOpen,
onToggle,
onOpenChange,
onOpenChange: handleOpenChange,
isPending,
data,
availableTabs: NETWORK_GROUPS.filter((tab) => data?.some(({ group }) => group === tab)),
}), [ open, onClose, onOpen, onToggle, onOpenChange, data, isPending ]);
}), [ open, onClose, onOpen, onToggle, handleOpenChange, data, isPending ]);
}
......@@ -7,16 +7,16 @@ import type { SearchResultTacOperation } from 'types/api/search';
import dayjs from 'lib/date/dayjs';
import * as OperationEntity from 'ui/shared/entities/operation/OperationEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import TacOperationTag from 'ui/shared/TacOperationTag';
import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus';
const SearchBarSuggestTacOperation = ({ data, isMobile }: ItemsProps<SearchResultTacOperation>) => {
const icon = <OperationEntity.Icon/>;
const icon = <OperationEntity.Icon type={ data.tac_operation.type }/>;
const hash = (
<chakra.mark overflow="hidden" whiteSpace="nowrap" fontWeight={ 700 } mr={ 2 }>
<HashStringShortenDynamic hash={ data.tac_operation.operation_id } noTooltip/>
</chakra.mark>
);
const status = <TacOperationTag type={ data.tac_operation.type }/>;
const status = <TacOperationStatus status={ data.tac_operation.type }/>;
const date = dayjs(data.tac_operation.timestamp).format('llll');
if (isMobile) {
......
......@@ -3,10 +3,11 @@ import React from 'react';
import type * as tac from '@blockscout/tac-operation-lifecycle-types';
import { getTacOperationStatus, getTacOperationStage } from 'lib/operations/tac';
import { getTacOperationStage } from 'lib/operations/tac';
import { Tag } from 'toolkit/chakra/tag';
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
import OperationEntity from 'ui/shared/entities/operation/OperationEntity';
import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus';
interface Props {
tacOperations: Array<tac.OperationDetails>;
......@@ -35,19 +36,20 @@ const TxDetailsTacOperation = ({ tacOperations, isLoading, txHash }: Props) => {
>
{ tacOperations.map((tacOperation) => {
const tags = [
getTacOperationStage(tacOperation, txHash),
getTacOperationStatus(tacOperation.type),
...(getTacOperationStage(tacOperation, txHash) || []),
];
return (
<HStack key={ tacOperation.operation_id } gap={ 3 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
<OperationEntity
id={ tacOperation.operation_id }
type={ tacOperation.type }
isLoading={ isLoading }
/>
{ tags.length > 0 && (
<HStack flexShrink={ 0 }>
{ tags.map((tag) => <Tag key={ tag } loading={ isLoading }>{ tag }</Tag>) }
<HStack flexShrink={ 0 } flexWrap="wrap">
<TacOperationStatus status={ tacOperation.type } isLoading={ isLoading }/>
{ tags.map((tag) => <Tag key={ tag } loading={ isLoading } flexShrink={ 0 }>{ tag }</Tag>) }
</HStack>
) }
</HStack>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment