Commit e7d9b96d authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into contract-verification-form

parents f1a7d210 ccfd529c
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.4 2.933a.156.156 0 0 0-.155.156v10.844a.467.467 0 0 1-.934 0V3.09A1.089 1.089 0 0 1 6.401 2h7.474a.467.467 0 0 1 .322.137l4.355 4.355a.467.467 0 0 1 .137.33v7.111a.467.467 0 0 1-.933 0V7.29h-3.89a.467.467 0 0 1-.466-.467V2.933h-7Zm7.933.66v2.763h2.763l-2.763-2.763Zm-3.11 14.385c0-.186.064-.305.157-.385.1-.086.275-.16.542-.16.32.001.631.105.888.297a.467.467 0 0 0 .558-.749 2.427 2.427 0 0 0-1.444-.481h-.002c-.433 0-.841.12-1.15.384-.315.27-.483.657-.483 1.094 0 .235.065.447.189.628.12.175.28.297.436.386.28.16.633.253.915.327l.051.014c.327.087.568.157.733.257.13.08.165.145.165.254 0 .24-.084.34-.184.404-.134.086-.36.14-.671.14-.32 0-.632-.104-.888-.296a.467.467 0 0 0-.558.749c.417.31.923.48 1.444.481h.001c.388 0 .823-.062 1.175-.287.386-.247.614-.654.614-1.19 0-.513-.277-.847-.613-1.052-.298-.181-.674-.281-.967-.36l-.011-.002c-.333-.089-.576-.154-.745-.25a.377.377 0 0 1-.128-.103c-.012-.018-.025-.044-.025-.1Zm4.02-1.364c.238-.1.51.013.61.25l1.125 2.7 1.124-2.7a.467.467 0 1 1 .862.36l-1.556 3.733a.467.467 0 0 1-.861 0l-1.556-3.733a.466.466 0 0 1 .252-.61Zm-8.067.897c-.653.001-1.243.585-1.243 1.4s.59 1.399 1.243 1.4c.287-.003.564-.112.776-.306a.467.467 0 1 1 .63.69 2.1 2.1 0 0 1-1.4.55h-.004C5.934 21.244 5 20.163 5 18.91c0-1.253.934-2.333 2.178-2.333h.004a2.1 2.1 0 0 1 1.4.55.467.467 0 1 1-.63.689 1.167 1.167 0 0 0-.776-.306Z" fill="currentColor" stroke="currentColor" stroke-width=".4" stroke-linecap="round" stroke-linejoin="round"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M6.4 2.933a.156.156 0 0 0-.155.156v10.844a.467.467 0 0 1-.934 0V3.09A1.089 1.089 0 0 1 6.401 2h7.474a.467.467 0 0 1 .322.137l4.355 4.355a.467.467 0 0 1 .137.33v7.111a.467.467 0 0 1-.933 0V7.29h-3.89a.467.467 0 0 1-.466-.467v-3.89h-7Zm7.933.66v2.763h2.763l-2.763-2.763Zm-3.11 14.385c0-.186.064-.305.157-.385.1-.086.275-.16.542-.16.32.001.631.105.888.297a.467.467 0 0 0 .558-.749 2.427 2.427 0 0 0-1.444-.481h-.002c-.433 0-.841.12-1.15.384-.315.27-.483.657-.483 1.094 0 .235.065.447.189.628.12.175.28.297.436.386.28.16.633.253.915.327l.051.014c.327.087.568.157.733.257.13.08.165.145.165.254 0 .24-.084.34-.184.404-.134.086-.36.14-.671.14-.32 0-.632-.104-.888-.296a.467.467 0 0 0-.558.749c.417.31.923.48 1.444.481h.001c.388 0 .823-.062 1.175-.287.386-.247.614-.654.614-1.19 0-.513-.277-.847-.613-1.052-.298-.181-.674-.281-.967-.36l-.011-.002c-.333-.089-.576-.154-.745-.25a.377.377 0 0 1-.128-.103c-.012-.018-.025-.044-.025-.1Zm4.02-1.364c.238-.1.51.013.61.25l1.125 2.7 1.124-2.7a.467.467 0 1 1 .862.36l-1.556 3.733a.467.467 0 0 1-.861 0l-1.556-3.733a.466.466 0 0 1 .252-.61Zm-8.067.897c-.653.001-1.243.585-1.243 1.4s.59 1.399 1.243 1.4c.287-.003.564-.112.776-.306a.467.467 0 1 1 .63.69 2.1 2.1 0 0 1-1.4.55h-.004C5.934 21.244 5 20.163 5 18.91s.934-2.333 2.178-2.333h.004a2.1 2.1 0 0 1 1.4.55.467.467 0 1 1-.63.689 1.167 1.167 0 0 0-.776-.306Z" fill="currentColor" stroke="currentColor" stroke-width=".4" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.4 2.933a.156.156 0 0 0-.156.156v10.844a.467.467 0 0 1-.933 0V3.09A1.089 1.089 0 0 1 6.4 2h7.476a.467.467 0 0 1 .32.137l4.356 4.355a.467.467 0 0 1 .137.33v7.111a.467.467 0 0 1-.934 0V7.29h-3.889a.467.467 0 0 1-.466-.467V2.933h-7Zm7.933.66v2.763h2.762l-2.762-2.763Z" fill="currentColor" stroke="currentColor" stroke-width=".4" stroke-linecap="round" stroke-linejoin="round"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M6.4 2.933a.156.156 0 0 0-.156.156v10.844a.467.467 0 0 1-.933 0V3.09A1.089 1.089 0 0 1 6.4 2h7.476a.467.467 0 0 1 .32.137l4.356 4.355a.467.467 0 0 1 .137.33v7.111a.467.467 0 0 1-.934 0V7.29h-3.889a.467.467 0 0 1-.466-.467v-3.89h-7Zm7.933.66v2.763h2.762l-2.762-2.763Z" fill="currentColor" stroke="currentColor" stroke-width=".4" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.752 20.137c-.016-.04-.03-.08-.043-.12-.068-.223.123-.42.356-.42h.074c.16 0 .29.123.369.263.045.08.103.147.17.2a.787.787 0 0 0 .256.136.99.99 0 0 0 .326.05.944.944 0 0 0 .421-.085.591.591 0 0 0 .26-.232.69.69 0 0 0 .086-.348.538.538 0 0 0-.036-.22.46.46 0 0 0-.119-.173c-.103-.096-.261-.173-.473-.232l-.63-.172a1.612 1.612 0 0 1-.55-.257 1.142 1.142 0 0 1-.36-.442 1.475 1.475 0 0 1-.125-.63c0-.293.065-.55.194-.768.13-.218.31-.388.538-.508.23-.12.495-.179.794-.179.31 0 .575.06.795.183.221.122.392.287.51.493.051.085.092.174.124.267.075.217-.117.414-.346.414-.194 0-.35-.152-.462-.31a.655.655 0 0 0-.25-.218.817.817 0 0 0-.379-.082c-.22 0-.395.06-.522.183a.559.559 0 0 0-.141.204.654.654 0 0 0-.048.258c0 .145.049.264.147.36a.922.922 0 0 0 .412.21l.634.173c.222.06.415.144.578.254.157.101.29.25.383.43.091.178.137.403.137.674 0 .297-.064.56-.191.789a1.308 1.308 0 0 1-.55.528c-.24.126-.531.19-.876.19-.26 0-.486-.036-.68-.109a1.375 1.375 0 0 1-.487-.303 1.355 1.355 0 0 1-.296-.45Zm-3.168-.04a1.803 1.803 0 0 1-.045-.17c-.044-.212.136-.39.354-.39h.05c.187 0 .332.157.412.325a.56.56 0 0 0 .197.224.448.448 0 0 0 .267.072c.194 0 .337-.066.43-.198.093-.132.14-.318.14-.56v-2.898a.404.404 0 0 1 .808 0v2.874c0 .53-.122.93-.365 1.209-.242.276-.576.415-1.005.415a1.402 1.402 0 0 1-.58-.113 1.17 1.17 0 0 1-.415-.32 1.428 1.428 0 0 1-.248-.47Zm9.279-1.906v.628c0 .308-.04.565-.12.771-.067.19-.182.352-.328.466a.795.795 0 0 1-.48.151.8.8 0 0 1-.48-.151 1.027 1.027 0 0 1-.326-.466 2.159 2.159 0 0 1-.12-.77v-.629c0-.31.04-.566.12-.77.067-.19.181-.352.326-.466a.787.787 0 0 1 .48-.155c.18 0 .34.052.48.155.146.113.26.275.329.465.08.205.119.461.119.771Zm.82.625v-.617c0-.454-.07-.844-.21-1.17a1.674 1.674 0 0 0-.602-.758c-.258-.176-.57-.265-.935-.265-.363 0-.676.089-.939.265-.26.173-.47.437-.601.755-.14.326-.21.717-.21 1.173v.617c0 .45.07.84.21 1.17.14.326.34.577.601.753.263.174.576.26.94.26s.676-.086.935-.26c.261-.176.462-.427.601-.753.14-.33.21-.72.21-1.17ZM16 17.69V20.528a.38.38 0 0 1-.762 0v-4.003a.426.426 0 0 1 .797-.208l1.66 2.97a.02.02 0 0 0 .04-.01v-2.796a.383.383 0 1 1 .765 0v4.006a.422.422 0 0 1-.79.206l-1.661-2.974a.056.056 0 0 0-.049-.028Z" fill="currentColor" stroke="currentColor" stroke-width=".3"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M7.752 20.137c-.016-.04-.03-.08-.043-.12-.068-.223.123-.42.356-.42h.074c.16 0 .29.123.369.263.045.08.103.147.17.2a.787.787 0 0 0 .256.136.99.99 0 0 0 .326.05.944.944 0 0 0 .421-.085.591.591 0 0 0 .26-.232.69.69 0 0 0 .086-.348.538.538 0 0 0-.036-.22.46.46 0 0 0-.119-.173c-.103-.096-.261-.173-.473-.232l-.63-.172a1.612 1.612 0 0 1-.55-.257 1.142 1.142 0 0 1-.36-.442 1.475 1.475 0 0 1-.125-.63c0-.293.065-.55.194-.768.13-.218.31-.388.538-.508.23-.12.495-.179.794-.179.31 0 .575.06.795.183.221.122.392.287.51.493.051.085.092.174.124.267.075.217-.117.414-.346.414-.194 0-.35-.152-.462-.31a.655.655 0 0 0-.25-.218.817.817 0 0 0-.379-.082c-.22 0-.395.06-.522.183a.559.559 0 0 0-.141.204.654.654 0 0 0-.048.258c0 .145.049.264.147.36a.922.922 0 0 0 .412.21l.634.173c.222.06.415.144.578.254.157.101.29.25.383.43.091.178.137.403.137.674a1.6 1.6 0 0 1-.191.789 1.308 1.308 0 0 1-.55.528c-.24.126-.531.19-.876.19-.26 0-.486-.036-.68-.109a1.375 1.375 0 0 1-.487-.303 1.355 1.355 0 0 1-.296-.45Zm-3.168-.04a1.803 1.803 0 0 1-.045-.17c-.044-.212.136-.39.354-.39h.05c.187 0 .332.157.412.325a.56.56 0 0 0 .197.224.448.448 0 0 0 .267.072c.194 0 .337-.066.43-.198.093-.132.14-.318.14-.56v-2.898a.404.404 0 0 1 .808 0v2.874c0 .53-.122.93-.365 1.209-.242.276-.576.415-1.005.415a1.402 1.402 0 0 1-.58-.113 1.17 1.17 0 0 1-.415-.32 1.428 1.428 0 0 1-.248-.47Zm9.279-1.906v.628c0 .308-.04.565-.12.771-.067.19-.182.352-.328.466a.795.795 0 0 1-.48.151.8.8 0 0 1-.48-.151 1.027 1.027 0 0 1-.326-.466 2.159 2.159 0 0 1-.12-.77v-.629c0-.31.04-.566.12-.77.067-.19.181-.352.326-.466a.787.787 0 0 1 .48-.155c.18 0 .34.052.48.155.146.113.26.275.329.465.08.205.119.461.119.771Zm.82.625v-.617c0-.454-.07-.844-.21-1.17a1.674 1.674 0 0 0-.602-.758c-.258-.176-.57-.265-.935-.265a1.65 1.65 0 0 0-.939.265c-.26.173-.47.437-.601.755-.14.326-.21.717-.21 1.173v.617c0 .45.07.84.21 1.17.14.326.34.577.601.753.263.174.576.26.94.26s.676-.086.935-.26c.261-.176.462-.427.601-.753.14-.33.21-.72.21-1.17ZM16 17.69v2.838a.38.38 0 0 1-.762 0v-4.003a.426.426 0 0 1 .797-.208l1.66 2.97a.02.02 0 0 0 .04-.01v-2.796a.383.383 0 1 1 .765 0v4.006a.422.422 0 0 1-.79.206l-1.661-2.974a.056.056 0 0 0-.049-.028Z" fill="currentColor" stroke="currentColor" stroke-width=".3"/>
</svg> </svg>
import type { IncomingMessage } from 'http';
import type { NextApiRequest } from 'next'; import type { NextApiRequest } from 'next';
import type { NextApiRequestCookies } from 'next/dist/server/api-utils';
import type { RequestInit, Response } from 'node-fetch'; import type { RequestInit, Response } from 'node-fetch';
import nodeFetch from 'node-fetch'; import nodeFetch from 'node-fetch';
...@@ -6,7 +8,7 @@ import { httpLogger } from 'lib/api/logger'; ...@@ -6,7 +8,7 @@ import { httpLogger } from 'lib/api/logger';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
export default function fetchFactory( export default function fetchFactory(
_req: NextApiRequest, _req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }),
) { ) {
// first arg can be only a string // first arg can be only a string
// FIXME migrate to RequestInfo later if needed // FIXME migrate to RequestInfo later if needed
......
...@@ -264,6 +264,9 @@ export const RESOURCES = { ...@@ -264,6 +264,9 @@ export const RESOURCES = {
], ],
filterFields: [ 'q' ], filterFields: [ 'q' ],
}, },
search_check_redirect: {
path: '/api/v2/search/check-redirect',
},
// DEPRECATED // DEPRECATED
old_api: { old_api: {
......
import type { GetServerSideProps, GetServerSidePropsResult } from 'next'; import type { GetServerSideProps } from 'next';
export type Props = { export type Props = {
cookies: string; cookies: string;
...@@ -6,7 +6,7 @@ export type Props = { ...@@ -6,7 +6,7 @@ export type Props = {
id?: string; id?: string;
} }
export const getServerSideProps: GetServerSideProps = async({ req, query }): Promise<GetServerSidePropsResult<Props>> => { export const getServerSideProps: GetServerSideProps<Props> = async({ req, query }) => {
return { return {
props: { props: {
cookies: req.headers.cookie || '', cookies: req.headers.cookie || '',
......
import type { NextPage } from 'next'; import type { GetServerSideProps, NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import type { SearchRedirectResult } from 'types/api/search';
import buildUrlNode from 'lib/api/buildUrlNode';
import fetchFactory from 'lib/api/nodeFetch';
import link from 'lib/link/link';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import getNetworkTitle from 'lib/networks/getNetworkTitle';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import SearchResults from 'ui/pages/SearchResults'; import SearchResults from 'ui/pages/SearchResults';
const SearchResultsPage: NextPage = () => { const SearchResultsPage: NextPage = () => {
...@@ -19,4 +26,42 @@ const SearchResultsPage: NextPage = () => { ...@@ -19,4 +26,42 @@ const SearchResultsPage: NextPage = () => {
export default SearchResultsPage; export default SearchResultsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, resolvedUrl, query }) => {
try {
const q = String(query.q);
const url = buildUrlNode('search_check_redirect', undefined, { q });
const redirectsResponse = await fetchFactory(req)(url);
const payload = await redirectsResponse.json() as SearchRedirectResult;
if (!payload || typeof payload !== 'object' || !payload.redirect) {
throw Error();
}
const redirectUrl = (() => {
switch (payload.type) {
case 'block': {
return link('block', { id: q });
}
case 'address': {
return link('address_index', { id: payload.parameter || q });
}
case 'transaction': {
return link('tx', { id: q });
}
}
})();
if (!redirectUrl) {
throw Error();
}
return {
redirect: {
destination: redirectUrl,
permanent: false,
},
};
} catch (error) {}
return getServerSidePropsBase({ req, res, resolvedUrl, query });
};
...@@ -47,7 +47,7 @@ const variantOutline = defineStyle((props) => { ...@@ -47,7 +47,7 @@ const variantOutline = defineStyle((props) => {
const isGrayTheme = c === 'gray' || c === 'gray-dark'; const isGrayTheme = c === 'gray' || c === 'gray-dark';
const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props); const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const borderColor = isGrayTheme ? mode('gray.300', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props); const borderColor = isGrayTheme ? mode('gray.200', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props); const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
const activeColor = (() => { const activeColor = (() => {
if (c === 'gray') { if (c === 'gray') {
......
import { menuAnatomy as parts } from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
cssVar,
defineStyle,
} from '@chakra-ui/styled-system';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
const $bg = cssVar('menu-bg');
const $shadow = cssVar('menu-shadow');
const baseStyleList = defineStyle({
[$bg.variable]: '#fff',
[$shadow.variable]: 'shadows.2xl',
_dark: {
[$bg.variable]: 'colors.gray.900',
[$shadow.variable]: 'shadows.dark-lg',
},
borderWidth: '0',
bg: $bg.reference,
boxShadow: $shadow.reference,
});
const baseStyleItem = defineStyle({
_focus: {
[$bg.variable]: 'colors.blue.50',
_dark: {
[$bg.variable]: 'colors.gray.800',
},
},
_hover: {
[$bg.variable]: 'colors.blue.50',
_dark: {
[$bg.variable]: 'colors.gray.800',
},
},
bg: $bg.reference,
});
const baseStyle = definePartsStyle({
list: baseStyleList,
item: baseStyleItem,
});
const Menu = defineMultiStyleConfig({
baseStyle,
});
export default Menu;
...@@ -29,6 +29,7 @@ const baseStyleContent = defineStyle((props) => { ...@@ -29,6 +29,7 @@ const baseStyleContent = defineStyle((props) => {
_dark: { _dark: {
[$popperBg.variable]: `colors.gray.900`, [$popperBg.variable]: `colors.gray.900`,
[$arrowShadowColor.variable]: `colors.whiteAlpha.300`, [$arrowShadowColor.variable]: `colors.whiteAlpha.300`,
boxShadow: 'dark-lg',
}, },
width: 'xs', width: 'xs',
border: 'none', border: 'none',
......
import { selectAnatomy as parts } from '@chakra-ui/anatomy'; import { selectAnatomy as parts } from '@chakra-ui/anatomy';
import { import {
createMultiStyleConfigHelpers, createMultiStyleConfigHelpers,
defineStyle,
} from '@chakra-ui/styled-system'; } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import Input from './Input'; import Input from './Input';
const { defineMultiStyleConfig } = const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys); createMultiStyleConfigHelpers(parts.keys);
const variantOutline = definePartsStyle((props) => {
return {
field: {
...Input.variants?.outline(props).field,
borderColor: mode('gray.200', 'gray.600')(props),
_hover: {
borderColor: mode('gray.300', 'gray.500')(props),
},
_focusVisible: {
borderColor: mode('gray.200', 'gray.600')(props),
boxShadow: 'none',
},
cursor: 'pointer',
},
};
});
const iconSpacing = defineStyle({ const iconSpacing = defineStyle({
paddingInlineEnd: '8', paddingInlineEnd: '8',
}); });
...@@ -38,6 +55,10 @@ const sizes = { ...@@ -38,6 +55,10 @@ const sizes = {
}; };
const Select = defineMultiStyleConfig({ const Select = defineMultiStyleConfig({
variants: {
...Input.variants,
outline: variantOutline,
},
sizes, sizes,
defaultProps: { defaultProps: {
size: 'sm', size: 'sm',
......
...@@ -8,6 +8,7 @@ import FormLabel from './FormLabel'; ...@@ -8,6 +8,7 @@ import FormLabel from './FormLabel';
import Heading from './Heading'; import Heading from './Heading';
import Input from './Input'; import Input from './Input';
import Link from './Link'; import Link from './Link';
import Menu from './Menu';
import Modal from './Modal'; import Modal from './Modal';
import Popover from './Popover'; import Popover from './Popover';
import Radio from './Radio'; import Radio from './Radio';
...@@ -33,6 +34,7 @@ const components = { ...@@ -33,6 +34,7 @@ const components = {
Form, Form,
FormLabel, FormLabel,
Link, Link,
Menu,
Modal, Modal,
Popover, Popover,
Radio, Radio,
......
...@@ -7,6 +7,7 @@ export interface Log { ...@@ -7,6 +7,7 @@ export interface Log {
data: string; data: string;
index: number; index: number;
decoded: DecodedInput | null; decoded: DecodedInput | null;
tx_hash: string | null;
} }
export interface LogsResponseTx { export interface LogsResponseTx {
......
...@@ -49,3 +49,9 @@ export interface SearchResult { ...@@ -49,3 +49,9 @@ export interface SearchResult {
export interface SearchResultFilters { export interface SearchResultFilters {
q: string; q: string;
} }
export interface SearchRedirectResult {
parameter: string | null;
redirect: boolean;
type: 'address' | 'block' | 'transaction' | null;
}
...@@ -3,15 +3,7 @@ import { ...@@ -3,15 +3,7 @@ import {
Flex, Flex,
HStack, HStack,
Icon, Icon,
Modal,
ModalContent,
ModalCloseButton,
Text, Text,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
...@@ -23,7 +15,6 @@ import transactionIcon from 'icons/transactions.svg'; ...@@ -23,7 +15,6 @@ import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -40,7 +31,6 @@ const LatestBlocksItem = ({ tx }: Props) => { ...@@ -40,7 +31,6 @@ const LatestBlocksItem = ({ tx }: Props) => {
const timeAgo = useTimeAgoIncrement(tx.timestamp || '0', true); const timeAgo = useTimeAgoIncrement(tx.timestamp || '0', true);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isOpen, onOpen, onClose } = useDisclosure();
return ( return (
<Box <Box
...@@ -52,29 +42,14 @@ const LatestBlocksItem = ({ tx }: Props) => { ...@@ -52,29 +42,14 @@ const LatestBlocksItem = ({ tx }: Props) => {
_last={{ borderBottom: '1px solid', borderColor: 'divider' }} _last={{ borderBottom: '1px solid', borderColor: 'divider' }}
> >
<Flex justifyContent="space-between" width="100%" alignItems="start" flexDirection={{ base: 'column', lg: 'row' }}> <Flex justifyContent="space-between" width="100%" alignItems="start" flexDirection={{ base: 'column', lg: 'row' }}>
{ !isMobile && ( { !isMobile && <Flex mr={ 3 }><TxAdditionalInfo tx={ tx }/></Flex> }
<Popover placement="right-start" openDelay={ 300 } isLazy>
{ ({ isOpen }) => (
<>
<PopoverTrigger>
<AdditionalInfoButton isOpen={ isOpen } mr={ 3 }/>
</PopoverTrigger>
<PopoverContent border="1px solid" borderColor="divider">
<PopoverBody>
<TxAdditionalInfo tx={ tx }/>
</PopoverBody>
</PopoverContent>
</>
) }
</Popover>
) }
<Box width={{ base: '100%', lg: 'calc(50% - 32px)' }}> <Box width={{ base: '100%', lg: 'calc(50% - 32px)' }}>
<Flex justifyContent="space-between"> <Flex justifyContent="space-between">
<HStack> <HStack>
<TxType types={ tx.tx_types }/> <TxType types={ tx.tx_types }/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/> <TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</HStack> </HStack>
{ isMobile && <AdditionalInfoButton onClick={ onOpen }/> } { isMobile && <TxAdditionalInfo tx={ tx } isMobile/> }
</Flex> </Flex>
<Flex <Flex
mt={ 2 } mt={ 2 }
...@@ -147,12 +122,6 @@ const LatestBlocksItem = ({ tx }: Props) => { ...@@ -147,12 +122,6 @@ const LatestBlocksItem = ({ tx }: Props) => {
</Flex> </Flex>
</Box> </Box>
</Flex> </Flex>
<Modal isOpen={ isOpen } onClose={ onClose } size="full">
<ModalContent paddingTop={ 4 }>
<ModalCloseButton/>
<TxAdditionalInfo tx={ tx }/>
</ModalContent>
</Modal>
</Box> </Box>
); );
}; };
......
...@@ -16,6 +16,9 @@ const EmptyElement = ({ className }: { className?: string }) => { ...@@ -16,6 +16,9 @@ const EmptyElement = ({ className }: { className?: string }) => {
color={ color } color={ color }
borderRadius="base" borderRadius="base"
as={ tokenPlaceholderIcon } as={ tokenPlaceholderIcon }
transitionProperty="background-color,color"
transitionDuration="normal"
transitionTimingFunction="ease"
/> />
); );
}; };
......
...@@ -7,7 +7,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -7,7 +7,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -16,6 +15,7 @@ import ListItemMobile from 'ui/shared/ListItemMobile'; ...@@ -16,6 +15,7 @@ import ListItemMobile from 'ui/shared/ListItemMobile';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
type Props = TokenTransfer & { type Props = TokenTransfer & {
baseAddress?: string; baseAddress?: string;
...@@ -52,10 +52,14 @@ const TokenTransferListItem = ({ ...@@ -52,10 +52,14 @@ const TokenTransferListItem = ({
<TokenSnippet hash={ token.address } w="auto" maxW="calc(100% - 140px)" name={ token.name || 'Unnamed token' }/> <TokenSnippet hash={ token.address } w="auto" maxW="calc(100% - 140px)" name={ token.name || 'Unnamed token' }/>
<Tag flexShrink={ 0 } ml={ 2 } mr={ 2 }>{ token.type }</Tag> <Tag flexShrink={ 0 } ml={ 2 } mr={ 2 }>{ token.type }</Tag>
<Tag colorScheme="orange">{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange">{ getTokenTransferTypeText(type) }</Tag>
{ showTxInfo && <AdditionalInfoButton position="absolute" top={ 0 } right={ 0 }/> } { showTxInfo && txHash && (
<Flex position="absolute" top={ 0 } right={ 0 }>
<TxAdditionalInfo hash={ txHash } isMobile/>
</Flex>
) }
</Flex> </Flex>
{ 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id }/> } { 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id }/> }
{ showTxInfo && ( { showTxInfo && txHash && (
<Flex justifyContent="space-between" alignItems="center" lineHeight="24px" width="100%"> <Flex justifyContent="space-between" alignItems="center" lineHeight="24px" width="100%">
<Flex> <Flex>
<Icon <Icon
......
...@@ -5,7 +5,6 @@ import React from 'react'; ...@@ -5,7 +5,6 @@ import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -13,6 +12,7 @@ import InOutTag from 'ui/shared/InOutTag'; ...@@ -13,6 +12,7 @@ import InOutTag from 'ui/shared/InOutTag';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
type Props = TokenTransfer & { type Props = TokenTransfer & {
baseAddress?: string; baseAddress?: string;
...@@ -44,9 +44,9 @@ const TokenTransferTableItem = ({ ...@@ -44,9 +44,9 @@ const TokenTransferTableItem = ({
return ( return (
<Tr alignItems="top"> <Tr alignItems="top">
{ showTxInfo && ( { showTxInfo && txHash && (
<Td> <Td>
<AdditionalInfoButton/> <TxAdditionalInfo hash={ txHash }/>
</Td> </Td>
) } ) }
<Td> <Td>
...@@ -59,7 +59,7 @@ const TokenTransferTableItem = ({ ...@@ -59,7 +59,7 @@ const TokenTransferTableItem = ({
<Td lineHeight="30px"> <Td lineHeight="30px">
{ 'token_id' in total ? <TokenTransferNft hash={ token.address } id={ total.token_id }/> : '-' } { 'token_id' in total ? <TokenTransferNft hash={ token.address } id={ total.token_id }/> : '-' }
</Td> </Td>
{ showTxInfo && ( { showTxInfo && txHash && (
<Td> <Td>
<Address display="inline-flex" maxW="100%" fontWeight={ 600 } lineHeight="30px"> <Address display="inline-flex" maxW="100%" fontWeight={ 600 } lineHeight="30px">
<AddressLink type="transaction" hash={ txHash }/> <AddressLink type="transaction" hash={ txHash }/>
......
...@@ -22,6 +22,9 @@ const AddressContractIcon = ({ className }: Props) => { ...@@ -22,6 +22,9 @@ const AddressContractIcon = ({ className }: Props) => {
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
fontWeight="700" fontWeight="700"
transitionProperty="background-color,color"
transitionDuration="normal"
transitionTimingFunction="ease"
> >
С С
</Box> </Box>
......
...@@ -24,7 +24,8 @@ test('with decoded input data +@mobile +@dark-mode', async({ mount }) => { ...@@ -24,7 +24,8 @@ test('with decoded input data +@mobile +@dark-mode', async({ mount }) => {
address={ addressMocks.withName } address={ addressMocks.withName }
topics={ TOPICS } topics={ TOPICS }
data={ DATA } data={ DATA }
type="tx" type="transaction"
tx_hash={ null }
/> />
</TestApp>, </TestApp>,
); );
...@@ -40,7 +41,8 @@ test('without decoded input data +@mobile', async({ mount }) => { ...@@ -40,7 +41,8 @@ test('without decoded input data +@mobile', async({ mount }) => {
address={ addressMocks.withoutName } address={ addressMocks.withoutName }
topics={ TOPICS } topics={ TOPICS }
data={ DATA } data={ DATA }
type="tx" type="transaction"
tx_hash={ null }
/> />
</TestApp>, </TestApp>,
); );
......
...@@ -14,7 +14,7 @@ import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; ...@@ -14,7 +14,7 @@ import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import LogTopic from 'ui/shared/logs/LogTopic'; import LogTopic from 'ui/shared/logs/LogTopic';
type Props = Log & { type Props = Log & {
type: 'address' | 'tx'; type: 'address' | 'transaction';
}; };
const RowHeader = ({ children }: { children: React.ReactNode }) => ( const RowHeader = ({ children }: { children: React.ReactNode }) => (
...@@ -23,11 +23,13 @@ const RowHeader = ({ children }: { children: React.ReactNode }) => ( ...@@ -23,11 +23,13 @@ const RowHeader = ({ children }: { children: React.ReactNode }) => (
</GridItem> </GridItem>
); );
const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => { const LogItem = ({ address, index, topics, data, decoded, type, tx_hash: txHash }: Props) => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200'); const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const dataBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); const dataBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const hasTxInfo = type === 'address' && txHash;
return ( return (
<Grid <Grid
gridTemplateColumns={{ base: 'minmax(0, 1fr)', lg: '200px minmax(0, 1fr)' }} gridTemplateColumns={{ base: 'minmax(0, 1fr)', lg: '200px minmax(0, 1fr)' }}
...@@ -41,7 +43,7 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => { ...@@ -41,7 +43,7 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => {
pt: 0, pt: 0,
}} }}
> >
{ !decoded && type === 'tx' && ( { !decoded && type === 'transaction' && (
<GridItem colSpan={{ base: 1, lg: 2 }}> <GridItem colSpan={{ base: 1, lg: 2 }}>
<Alert status="warning" display="inline-table" whiteSpace="normal"> <Alert status="warning" display="inline-table" whiteSpace="normal">
To see accurate decoded input data, the contract must be verified.{ space } To see accurate decoded input data, the contract must be verified.{ space }
...@@ -49,11 +51,15 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => { ...@@ -49,11 +51,15 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => {
</Alert> </Alert>
</GridItem> </GridItem>
) } ) }
<RowHeader>Address</RowHeader> { hasTxInfo ? <RowHeader>Transaction</RowHeader> : <RowHeader>Address</RowHeader> }
<GridItem display="flex" alignItems="center"> <GridItem display="flex" alignItems="center">
<Address mr={{ base: 9, lg: 0 }}> <Address mr={{ base: 9, lg: 0 }}>
<AddressIcon address={ address }/> { !hasTxInfo && <AddressIcon address={ address } mr={ 2 }/> }
<AddressLink type="address" hash={ address.hash } alias={ address.name } ml={ 2 }/> <AddressLink
hash={ hasTxInfo ? txHash : address.hash }
alias={ hasTxInfo ? undefined : address.name }
type={ type }
/>
</Address> </Address>
{ /* api doesn't have find topic feature yet */ } { /* api doesn't have find topic feature yet */ }
{ /* <Tooltip label="Find matches topic"> { /* <Tooltip label="Find matches topic">
...@@ -93,4 +99,4 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => { ...@@ -93,4 +99,4 @@ const TxLogItem = ({ address, index, topics, data, decoded, type }: Props) => {
); );
}; };
export default React.memo(TxLogItem); export default React.memo(LogItem);
...@@ -70,7 +70,6 @@ const LogTopic = ({ hex, index }: Props) => { ...@@ -70,7 +70,6 @@ const LogTopic = ({ hex, index }: Props) => {
borderRadius="base" borderRadius="base"
value={ selectedDataType } value={ selectedDataType }
onChange={ handleSelectChange } onChange={ handleSelectChange }
focusBorderColor="none"
mr={ 3 } mr={ 3 }
flexShrink={ 0 } flexShrink={ 0 }
w="auto" w="auto"
......
...@@ -79,8 +79,9 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid ...@@ -79,8 +79,9 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid
paddingBottom={{ base: isHomepage ? 0 : 4, lg: 0 }} paddingBottom={{ base: isHomepage ? 0 : 4, lg: 0 }}
boxShadow={ scrollDirection !== 'down' && isSticky ? 'md' : 'none' } boxShadow={ scrollDirection !== 'down' && isSticky ? 'md' : 'none' }
transform={{ base: isHomepage ? 'none' : transformMobile, lg: 'none' }} transform={{ base: isHomepage ? 'none' : transformMobile, lg: 'none' }}
transitionProperty="transform,box-shadow" transitionProperty="transform,box-shadow,background-color,color,border-color"
transitionDuration="slow" transitionDuration="normal"
transitionTimingFunction="ease"
> >
<InputGroup size={{ base: isHomepage ? 'md' : 'sm', lg: 'md' }}> <InputGroup size={{ base: isHomepage ? 'md' : 'sm', lg: 'md' }}>
<InputLeftElement w={{ base: isHomepage ? 6 : 4, lg: 6 }} ml={{ base: isHomepage ? 4 : 3, lg: 4 }} h="100%"> <InputLeftElement w={{ base: isHomepage ? 6 : 4, lg: 6 }} ml={{ base: isHomepage ? 4 : 3, lg: 4 }} h="100%">
......
...@@ -50,7 +50,7 @@ const TxLogs = () => { ...@@ -50,7 +50,7 @@ const TxLogs = () => {
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
) } ) }
{ data.items.map((item, index) => <LogItem key={ index } { ...item } type="tx"/>) } { data.items.map((item, index) => <LogItem key={ index } { ...item } type="transaction"/>) }
</Box> </Box>
); );
}; };
......
import { Box, Heading, Text, Flex, Link } from '@chakra-ui/react'; import {
import BigNumber from 'bignumber.js'; Modal,
ModalContent,
ModalCloseButton,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config'; import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import getValueWithUnit from 'lib/getValueWithUnit';
import link from 'lib/link/link';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization';
const TxAdditionalInfo = ({ tx }: { tx: Transaction }) => { import TxAdditionalInfoContainer from './TxAdditionalInfoContainer';
const sectionProps = { import TxAdditionalInfoContent from './TxAdditionalInfoContent';
borderBottom: '1px solid',
borderColor: 'divider',
paddingBottom: 4,
};
const sectionTitleProps = { type Props =
color: 'gray.500', ({
fontWeight: 600, hash: string;
marginBottom: 3, tx?: undefined;
fontSize: 'sm', } |
}; {
hash?: undefined;
tx: Transaction;
}) & {
isMobile?: boolean;
}
const TxAdditionalInfo = ({ hash, tx, isMobile }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const content = hash !== undefined ? <TxAdditionalInfoContainer hash={ hash }/> : <TxAdditionalInfoContent tx={ tx }/>;
if (isMobile) {
return (
<>
<AdditionalInfoButton onClick={ onOpen }/>
<Modal isOpen={ isOpen } onClose={ onClose } size="full">
<ModalContent paddingTop={ 4 }>
<ModalCloseButton/>
{ content }
</ModalContent>
</Modal>
</>
);
}
return ( return (
<> <Popover placement="right-start" openDelay={ 300 } isLazy>
<Heading as="h4" size="sm" mb={ 6 }>Additional info </Heading> { ({ isOpen }) => (
<Box { ...sectionProps } mb={ 4 }> <>
<Text { ...sectionTitleProps }>Transaction fee</Text> <PopoverTrigger>
<Flex> <AdditionalInfoButton isOpen={ isOpen }/>
<CurrencyValue </PopoverTrigger>
value={ tx.fee.value } <PopoverContent border="1px solid" borderColor="divider">
currency={ appConfig.network.currency.symbol } <PopoverBody>
exchangeRate={ tx.exchange_rate } { content }
accuracyUsd={ 2 } </PopoverBody>
/> </PopoverContent>
</Flex> </>
</Box>
{ tx.gas_used !== null && (
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text>
<Flex>
<Text>{ BigNumber(tx.gas_used).toFormat() }</Text>
<TextSeparator/>
<Text>{ BigNumber(tx.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ Number(BigNumber(tx.gas_used).dividedBy(BigNumber(tx.gas_limit)).toFixed(2)) }/>
</Flex>
</Box>
) }
{ (tx.base_fee_per_gas !== null || tx.max_fee_per_gas !== null || tx.max_priority_fee_per_gas !== null) && (
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas fees (Gwei)</Text>
{ tx.base_fee_per_gas !== null && (
<Box>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.base_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
{ tx.max_fee_per_gas !== null && (
<Box>
<Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
{ tx.max_priority_fee_per_gas !== null && (
<Box>
<Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_priority_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
</Box>
) } ) }
<Box { ...sectionProps } mb={ 4 }> </Popover>
<Text { ...sectionTitleProps }>Others</Text>
<Box>
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ tx.type }</Text>
{ tx.type === 2 && <Text fontWeight="400" as="span" ml={ 1 } color="gray.500">(EIP-1559)</Text> }
</Box>
<Box>
<Text as="span" fontWeight="500">Nonce: </Text>
<Text fontWeight="600" as="span">{ tx.nonce }</Text>
</Box>
<Box>
<Text as="span" fontWeight="500">Position: </Text>
<Text fontWeight="600" as="span">{ tx.position }</Text>
</Box>
</Box>
<Link fontSize="sm" href={ link('tx', { id: tx.hash }) }>More details</Link>
</>
); );
}; };
export default TxAdditionalInfo; export default React.memo(TxAdditionalInfo);
import { Box, Divider, Skeleton } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxAdditionalInfoContent from './TxAdditionalInfoContent';
interface Props {
hash: string;
}
const TxAdditionalInfoContainer = ({ hash }: Props) => {
const { data, isError, isLoading } = useApiQuery('tx', {
pathParams: { id: hash },
queryOptions: {
refetchOnMount: false,
},
});
if (isLoading) {
return (
<Box>
<Skeleton w="130px" h="24px" borderRadius="full" mb={ 6 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="75%" h="16px" borderRadius="full"/>
<Skeleton w="75%" h="16px" borderRadius="full" mt={ 1 }/>
<Skeleton w="75%" h="16px" borderRadius="full" mt={ 1 }/>
</Box>
<Divider my={ 4 }/>
<Skeleton w="80px" h="16px" borderRadius="full"/>
</Box>
);
}
if (isError) {
return <DataFetchAlert/>;
}
return <TxAdditionalInfoContent tx={ data }/>;
};
export default React.memo(TxAdditionalInfoContainer);
import { Box, Heading, Text, Flex, Link } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { Transaction } from 'types/api/transaction';
import appConfig from 'configs/app/config';
import getValueWithUnit from 'lib/getValueWithUnit';
import link from 'lib/link/link';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization';
const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => {
const sectionProps = {
borderBottom: '1px solid',
borderColor: 'divider',
paddingBottom: 4,
};
const sectionTitleProps = {
color: 'gray.500',
fontWeight: 600,
marginBottom: 3,
fontSize: 'sm',
};
return (
<>
<Heading as="h4" size="sm" mb={ 6 }>Additional info </Heading>
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Transaction fee</Text>
<Flex>
<CurrencyValue
value={ tx.fee.value }
currency={ appConfig.network.currency.symbol }
exchangeRate={ tx.exchange_rate }
accuracyUsd={ 2 }
/>
</Flex>
</Box>
{ tx.gas_used !== null && (
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text>
<Flex>
<Text>{ BigNumber(tx.gas_used).toFormat() }</Text>
<TextSeparator/>
<Text>{ BigNumber(tx.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ Number(BigNumber(tx.gas_used).dividedBy(BigNumber(tx.gas_limit)).toFixed(2)) }/>
</Flex>
</Box>
) }
{ (tx.base_fee_per_gas !== null || tx.max_fee_per_gas !== null || tx.max_priority_fee_per_gas !== null) && (
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas fees (Gwei)</Text>
{ tx.base_fee_per_gas !== null && (
<Box>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.base_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
{ tx.max_fee_per_gas !== null && (
<Box mt={ 1 }>
<Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
{ tx.max_priority_fee_per_gas !== null && (
<Box mt={ 1 }>
<Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_priority_fee_per_gas, 'gwei').toFormat() }</Text>
</Box>
) }
</Box>
) }
<Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Others</Text>
<Box>
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ tx.type }</Text>
{ tx.type === 2 && <Text fontWeight="400" as="span" ml={ 1 } color="gray.500">(EIP-1559)</Text> }
</Box>
<Box mt={ 1 }>
<Text as="span" fontWeight="500">Nonce: </Text>
<Text fontWeight="600" as="span">{ tx.nonce }</Text>
</Box>
<Box mt={ 1 }>
<Text as="span" fontWeight="500">Position: </Text>
<Text fontWeight="600" as="span">{ tx.position }</Text>
</Box>
</Box>
<Link fontSize="sm" href={ link('tx', { id: tx.hash }) }>More details</Link>
</>
);
};
export default React.memo(TxAdditionalInfoContent);
...@@ -4,11 +4,8 @@ import { ...@@ -4,11 +4,8 @@ import {
Flex, Flex,
Icon, Icon,
Link, Link,
Modal,
ModalContent,
ModalCloseButton,
Text, Text,
useDisclosure } from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
...@@ -19,7 +16,6 @@ import transactionIcon from 'icons/transactions.svg'; ...@@ -19,7 +16,6 @@ import transactionIcon from 'icons/transactions.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link'; import link from 'lib/link/link';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -40,8 +36,6 @@ const TAG_WIDTH = 48; ...@@ -40,8 +36,6 @@ const TAG_WIDTH = 48;
const ARROW_WIDTH = 24; const ARROW_WIDTH = 24;
const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }: Props) => { const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const dataTo = tx.to ? tx.to : tx.created_contract; const dataTo = tx.to ? tx.to : tx.created_contract;
const isOut = Boolean(currentAddress && currentAddress === tx.from.hash); const isOut = Boolean(currentAddress && currentAddress === tx.from.hash);
...@@ -50,103 +44,95 @@ const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }: ...@@ -50,103 +44,95 @@ const TxsListItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }:
const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement); const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement);
return ( return (
<> <ListItemMobile display="block" width="100%" isAnimated key={ tx.hash }>
<ListItemMobile display="block" width="100%" isAnimated key={ tx.hash }> <Flex justifyContent="space-between" mt={ 4 }>
<Flex justifyContent="space-between" mt={ 4 }> <HStack>
<HStack> <TxType types={ tx.tx_types }/>
<TxType types={ tx.tx_types }/> <TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/> </HStack>
</HStack> <TxAdditionalInfo tx={ tx } isMobile/>
<AdditionalInfoButton onClick={ onOpen }/> </Flex>
</Flex> <Flex justifyContent="space-between" lineHeight="24px" mt={ 3 } alignItems="center">
<Flex justifyContent="space-between" lineHeight="24px" mt={ 3 } alignItems="center"> <Flex>
<Flex> <Icon
<Icon as={ transactionIcon }
as={ transactionIcon } boxSize="30px"
boxSize="30px" mr={ 2 }
mr={ 2 } color="link"
color="link" />
/> <Address width="100%">
<Address width="100%">
<AddressLink
hash={ tx.hash }
type="transaction"
fontWeight="700"
truncation="constant"
/>
</Address>
</Flex>
{ tx.timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
</Flex>
{ tx.method && (
<Flex mt={ 3 }>
<Text as="span" whiteSpace="pre">Method </Text>
<Text
as="span"
variant="secondary"
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
>
{ tx.method }
</Text>
</Flex>
) }
{ showBlockInfo && tx.block !== null && (
<Box mt={ 2 }>
<Text as="span">Block </Text>
<Link href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</Link>
</Box>
) }
<Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address width={ `calc((100%-${ currentAddress ? TAG_WIDTH : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon address={ tx.from }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isOut }
/>
</Address>
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mr={ 2 }/> : (
<Icon
as={ rightArrowIcon }
boxSize={ 6 }
mx={ 2 }
color="gray.500"
/>
) }
<Address width="calc((100%-40px)/2)">
<AddressIcon address={ dataTo }/>
<AddressLink <AddressLink
type="address" hash={ tx.hash }
hash={ dataTo.hash } type="transaction"
alias={ dataTo.name } fontWeight="700"
fontWeight="500" truncation="constant"
ml={ 2 }
isDisabled={ isIn }
/> />
</Address> </Address>
</Flex> </Flex>
{ tx.timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
</Flex>
{ tx.method && (
<Flex mt={ 3 }>
<Text as="span" whiteSpace="pre">Method </Text>
<Text
as="span"
variant="secondary"
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
>
{ tx.method }
</Text>
</Flex>
) }
{ showBlockInfo && tx.block !== null && (
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Value { appConfig.network.currency.symbol } </Text> <Text as="span">Block </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).toFormat() }</Text> <Link href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</Link>
</Box> </Box>
<Box mt={ 2 } mb={ 3 }> ) }
<Text as="span">Fee { appConfig.network.currency.symbol } </Text> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).toFormat() }</Text> <Address width={ `calc((100%-${ currentAddress ? TAG_WIDTH : ARROW_WIDTH + 8 }px)/2)` }>
</Box> <AddressIcon address={ tx.from }/>
</ListItemMobile> <AddressLink
<Modal isOpen={ isOpen } onClose={ onClose } size="full"> type="address"
<ModalContent paddingTop={ 4 }> hash={ tx.from.hash }
<ModalCloseButton/> alias={ tx.from.name }
<TxAdditionalInfo tx={ tx }/> fontWeight="500"
</ModalContent> ml={ 2 }
</Modal> isDisabled={ isOut }
</> />
</Address>
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mr={ 2 }/> : (
<Icon
as={ rightArrowIcon }
boxSize={ 6 }
mx={ 2 }
color="gray.500"
/>
) }
<Address width="calc((100%-40px)/2)">
<AddressIcon address={ dataTo }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isIn }
/>
</Address>
</Flex>
<Box mt={ 2 }>
<Text as="span">Value { appConfig.network.currency.symbol } </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).toFormat() }</Text>
</Box>
<Box mt={ 2 } mb={ 3 }>
<Text as="span">Fee { appConfig.network.currency.symbol } </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).toFormat() }</Text>
</Box>
</ListItemMobile>
); );
}; };
......
...@@ -7,10 +7,6 @@ import { ...@@ -7,10 +7,6 @@ import {
Icon, Icon,
VStack, VStack,
Text, Text,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
Show, Show,
Hide, Hide,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
...@@ -22,7 +18,6 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -22,7 +18,6 @@ import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import link from 'lib/link/link'; import link from 'lib/link/link';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -73,20 +68,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement } ...@@ -73,20 +68,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }
key={ tx.hash } key={ tx.hash }
> >
<Td pl={ 4 }> <Td pl={ 4 }>
<Popover placement="right-start" openDelay={ 300 } isLazy> <TxAdditionalInfo tx={ tx }/>
{ ({ isOpen }) => (
<>
<PopoverTrigger>
<AdditionalInfoButton isOpen={ isOpen }/>
</PopoverTrigger>
<PopoverContent border="1px solid" borderColor="divider">
<PopoverBody>
<TxAdditionalInfo tx={ tx }/>
</PopoverBody>
</PopoverContent>
</>
) }
</Popover>
</Td> </Td>
<Td pr={ 4 }> <Td pr={ 4 }>
<VStack alignItems="start" lineHeight="24px"> <VStack alignItems="start" lineHeight="24px">
......
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