Commit 904eb3c9 authored by tom's avatar tom

Merge branch 'block-details-tab' into blocks-page

parents 5f0f9a06 fcfc5256
...@@ -10,4 +10,4 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout ...@@ -10,4 +10,4 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_SUPPORTED_NETWORKS=[{"name":"Gnosis Chain","type":"xdai","subType":"mainnet","group":"mainnets","isAccountSupported":true, "chainId": 100},{"name":"Optimism on Gnosis Chain","shortName":"OoG","type":"xdai","subType":"optimism","group":"mainnets","icon":"https://www.fillmurray.com/60/60", "chainId": 300},{"name":"Arbitrum on xDai","type":"xdai","subType":"aox","group":"mainnets", "chainId": 200},{"name":"Ethereum","shortName":"ETH","type":"eth","subType":"mainnet","group":"mainnets", "chainId": 1},{"name":"Ethereum Classic","shortName":"ETC","type":"etc","subType":"mainnet","group":"mainnets", "chainId": 61},{"name":"POA","shortName":"POA","type":"poa","subType":"core","group":"mainnets","isAccountSupported":true, "chainId": 99},{"name":"RSK","shortName":"RBTC","type":"rsk","subType":"mainnet","group":"mainnets", "chainId": 30},{"name":"Gnosis Chain Testnet","type":"xdai","subType":"testnet","group":"testnets"},{"name":"POA Sokol","shortName":"POA","type":"poa","subType":"sokol","group":"testnets", "chainId": 77},{"name":"ARTIS Σ1","type":"artis","subType":"sigma1","group":"other", "chainId": 246529},{"name":"LUKSO L14","shortName":"POA","type":"lukso","subType":"l14","group":"other", "chainId": 22}] NEXT_PUBLIC_SUPPORTED_NETWORKS=[{"name":"Gnosis Chain","type":"xdai","subType":"mainnet","group":"mainnets","isAccountSupported":true,"chainId":100,"currency":"xDAI"},{"name":"Optimism on Gnosis Chain","shortName":"OoG","type":"xdai","subType":"optimism","group":"mainnets","icon":"https://www.fillmurray.com/60/60","chainId":300,"currency":"xDAI"},{"name":"Arbitrum on xDai","type":"xdai","subType":"aox","group":"mainnets","chainId":200,"currency":"xDAI"},{"name":"Ethereum","shortName":"ETH","type":"eth","subType":"mainnet","group":"mainnets","chainId":1,"currency":"ETH"},{"name":"Ethereum Classic","shortName":"ETC","type":"etc","subType":"mainnet","group":"mainnets","chainId":61,"currency":"ETC"},{"name":"POA","shortName":"POA","type":"poa","subType":"core","group":"mainnets","chainId":99,"currency":"POA","nativeTokenAddress": "0x029a799563238d0e75e20be2f4bda0ea68d00172"},{"name":"RSK","shortName":"RBTC","type":"rsk","subType":"mainnet","group":"mainnets","chainId":30,"currency":"RBTC"},{"name":"Gnosis Chain Testnet","type":"xdai","subType":"testnet","group":"testnets","isAccountSupported":true,"currency":"xDAI"},{"name":"POA Sokol","shortName":"POA","type":"poa","subType":"sokol","group":"testnets","chainId":77,"currency":"SPOA"},{"name":"ARTIS Σ1","type":"artis","subType":"sigma1","group":"other","chainId":246529,"currency":"ATS"},{"name":"LUKSO L14","shortName":"POA","type":"lukso","subType":"l14","group":"other","chainId":22,"currency":"LYX"},{"name":"Astar","type":"astar","group":"other","chainId":22,"currency":"ASTR"}]
...@@ -32,19 +32,23 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -32,19 +32,23 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_FOOTER_TWITTER_LINK | `string` | Link to Twitter in the footer | `https://www.twitter.com/blockscoutcom` | | NEXT_PUBLIC_FOOTER_TWITTER_LINK | `string` | Link to Twitter in the footer | `https://www.twitter.com/blockscoutcom` |
| NEXT_PUBLIC_FOOTER_TELEGRAM_LINK | `string` | Link to Telegram in the footer | `https://t.me/poa_network` | | NEXT_PUBLIC_FOOTER_TELEGRAM_LINK | `string` | Link to Telegram in the footer | `https://t.me/poa_network` |
| NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` | | NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` |
| NEXT_PUBLIC_SUPPORTED_NETWORKS | `Array<Network>` where `Network` can have following [properties](#network-configuration-properties) | Configuration of supported networks | `[{"name":"Gnosis Chain","type":"xdai","subType":"mainnet","group":"mainnets","isAccountSupported":true, "chainId": 100},{"name":"Optimism on Gnosis Chain","shortName":"OoG","type":"xdai","subType":"optimism","group":"mainnets","icon":"https://www.fillmurray.com/60/60", "chainId": 300},{"name":"Arbitrum on xDai","type":"xdai","subType":"aox","group":"mainnets", "chainId": 200},{"name":"Ethereum","shortName":"ETH","type":"eth","subType":"mainnet","group":"mainnets", "chainId": 1},{"name":"Ethereum Classic","shortName":"ETC","type":"etc","subType":"mainnet","group":"mainnets", "chainId": 61},{"name":"POA","shortName":"POA","type":"poa","subType":"core","group":"mainnets","isAccountSupported":true, "chainId": 99},{"name":"RSK","shortName":"RBTC","type":"rsk","subType":"mainnet","group":"mainnets", "chainId": 30},{"name":"Gnosis Chain Testnet","type":"xdai","subType":"testnet","group":"testnets"},{"name":"POA Sokol","shortName":"POA","type":"poa","subType":"sokol","group":"testnets", "chainId": 77},{"name":"ARTIS Σ1","type":"artis","subType":"sigma1","group":"other", "chainId": 246529},{"name":"LUKSO L14","shortName":"POA","type":"lukso","subType":"l14","group":"other", "chainId": 22}]` | | NEXT_PUBLIC_SUPPORTED_NETWORKS | `Array<Network>` where `Network` can have following [properties](#network-configuration-properties) | Configuration of supported networks | `[{"name":"POA","type":"poa","subType":"core","group":"mainnets","isAccountSupported":true,"chainId":99,"currency":"POA"}]` |
### Network configuration properties ### Network configuration properties
| Property | Type | Description | Example value | Property | Type | Description | Example value
| --- | --- | --- | --- | | --- | --- | --- | --- |
| name | `string` | Displayed name of the network | `"Gnosis Chain"` | | name | `string` | Displayed name of the network | `"Gnosis Chain"` |
| chainId | `number` | Id of the network. Could be seen there – [https://chainlist.org/](https://chainlist.org/) | `1` | | shortName | `string` | Used for SEO attributes (page title and description) | `"OoG"` |
| chainId | `number` | Id of the network. Could be found here – [https://chainlist.org/](https://chainlist.org/) | `1` |
| currency | `string` | Network currency symbol. Could be found here – [https://chainlist.org/](https://chainlist.org/) | `"xDAI"` |
| nativeTokenAddress | `string` | Address of network's native token | `"0x029a799563238d0e75e20be2f4bda0ea68d00172"` |
| type | `string` | Network type (used as first part of the base path) | `"xdai"` | | type | `string` | Network type (used as first part of the base path) | `"xdai"` |
| subType | `string` | Network subtype (used as second part of the base path) | `"mainnet"` | | subType | `string` | Network subtype (used as second part of the base path) | `"mainnet"` |
| group | `mainnets \| testnets \| other` | Indicates in which tab network appears in the menu | `"mainnets"` | | group | `mainnets \| testnets \| other` | Indicates in which tab network appears in the menu | `"mainnets"` |
| isAccountSupported | `boolean` *(optional)* | Set to true if network has account feature | `true` | | isAccountSupported | `boolean` *(optional)* | Set to true if network has account feature | `true` |
| icon | `string` *(optional)* | Network icon; if not provided, will fallback to icon predefined in the project; if the project doesn't have icon for such network then the common placeholder will be shown; *Note* that icon size should be 30px by 30px | `"https://www.fillmurray.com/60/60"` | | icon | `string` *(optional)* | Network icon; if not provided, will fallback to icon predefined in the project; if the project doesn't have icon for such network then the common placeholder will be shown; *Note* that icon size should be 30px by 30px | `"https://www.fillmurray.com/60/60"` |
| logo | `string` *(optional)* | Network logo; if not provided, will fallback to logo predefined in the project; if the project doesn't have logo for such network then the common placeholder will be shown; *Note* that logo height should be 20px and width less than 120px | `"https://www.fillmurray.com/240/40"` | | logo | `string` *(optional)* | Network logo; if not provided, will fallback to logo predefined in the project; if the project doesn't have logo for such network then the common placeholder will be shown; *Note* that logo height should be 20px and width less than 120px | `"https://www.fillmurray.com/240/40"` |
| assetsNamePath | `string` *(optional)* | Network name for constructing url of token logos according to template `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/${assetsNamePath}/assets/${tokenAddress}/logo.png`. It should match network name in TrustWallet assets repo, see the full list [here](https://github.com/trustwallet/assets/tree/master/blockchains). The project already has some pre-defined mapping for popular network, which is match assetsNamePath against provided network type and sub-type. So typically you don't need to provide this variable, if you network is in the list or its type in config is conformed to a name in TrustWallet repo. If it is not the case, pass value here | `"ethereum"` |
*Note* the base path for the network is built up from its `type` and `subType` like so `https://blockscout.com/<type>/<subType>` *Note* the base path for the network is built up from its `type` and `subType` like so `https://blockscout.com/<type>/<subType>`
...@@ -44,8 +44,8 @@ export const tx = { ...@@ -44,8 +44,8 @@ export const tx = {
position: 342, position: 342,
input_hex: '0x42842e0e0000000000000000000000007767dac225a233ea1055d79fb227b1696d538b75000000000000000000000000fc3017c31fe752fc48e904050ea5d6edfc38a1b00000000000000000000000000000000000000000000000000000000000000e3b', input_hex: '0x42842e0e0000000000000000000000007767dac225a233ea1055d79fb227b1696d538b75000000000000000000000000fc3017c31fe752fc48e904050ea5d6edfc38a1b00000000000000000000000000000000000000000000000000000000000000e3b',
transferred_tokens: [ transferred_tokens: [
{ from: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', to: '0xF7A558692dFB5F456e291791da7FAE8Dd046574e', token: 'USDT', amount: 192.7, usd: 194.05 }, { from: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', to: '0xF7A558692dFB5F456e291791da7FAE8Dd046574e', token: { symbol: 'VIK', hash: '0xADFE00d92e5A16e773891F59780e6e54f40B532e', name: 'Viktor Coin' }, amount: 192.7, usd: 194.05 },
{ from: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', to: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', token: 'TOKE', amount: 76.1851851851846, usd: 194.05 }, { from: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', to: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', token: { symbol: 'PAO', hash: '0xC98a06220239818B086CD96756d4E3bC41EC848E', name: 'POA Candy' }, amount: 76.1851851851846, usd: 194.05 },
], ],
txType: 'transaction' as TxType, txType: 'transaction' as TxType,
}; };
......
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.774 13.088a.936.936 0 0 0 0 1.325l2.814 2.812a.935.935 0 0 0 1.324 0l2.813-2.812A.935.935 0 1 0 8.4 13.088l-1.213 1.21v-9.95a.954.954 0 0 0-.938-.937.954.954 0 0 0-.937.938v9.95L4.1 13.088a.937.937 0 0 0-1.327 0ZM12.813 15.626a.937.937 0 1 0 1.875 0V5.702l1.213 1.21a.935.935 0 1 0 1.324-1.324l-2.812-2.813a.936.936 0 0 0-1.325 0l-2.812 2.813A.935.935 0 1 0 11.6 6.912l1.213-1.21v9.924Z" fill="currentColor"/> <path d="M2.774 13.088a.936.936 0 0 0 0 1.325l2.814 2.812a.935.935 0 0 0 1.324 0l2.813-2.812A.935.935 0 1 0 8.4 13.088l-1.213 1.21v-9.95a.954.954 0 0 0-.938-.937.954.954 0 0 0-.937.938v9.95L4.1 13.088a.937.937 0 0 0-1.327 0Zm10.039 2.538a.937.937 0 1 0 1.875 0V5.702l1.213 1.21a.935.935 0 1 0 1.324-1.324l-2.812-2.813a.936.936 0 0 0-1.325 0l-2.812 2.813A.935.935 0 1 0 11.6 6.912l1.213-1.21v9.924Z" fill="currentColor"/>
</svg> </svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="url(#toke_svg__a)" d="M0 0h20v20H0z"/>
<defs>
<pattern id="toke_svg__a" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#toke_svg__b" transform="scale(.03125)"/>
</pattern>
<image id="toke_svg__b" width="32" height="32" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="url(#usdt_svg__a)" d="M0 0h20v20H0z"/>
<defs>
<pattern id="usdt_svg__a" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#usdt_svg__b" transform="scale(.03125)"/>
</pattern>
<image id="usdt_svg__b" width="32" height="32" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>
...@@ -12,7 +12,7 @@ export enum NAMES { ...@@ -12,7 +12,7 @@ export enum NAMES {
export function get(name?: string | undefined | null) { export function get(name?: string | undefined | null) {
if (!isBrowser()) { if (!isBrowser()) {
return () => {}; return undefined;
} }
return Cookies.get(name); return Cookies.get(name);
} }
......
...@@ -60,7 +60,8 @@ export default NETWORKS; ...@@ -60,7 +60,8 @@ export default NETWORKS;
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// isAccountSupported: true, // isAccountSupported: true,
// chainId: 100 // chainId: 100,
// currency: 'xDAI',
// }, // },
// { // {
// name: 'Optimism on Gnosis Chain', // name: 'Optimism on Gnosis Chain',
...@@ -68,15 +69,17 @@ export default NETWORKS; ...@@ -68,15 +69,17 @@ export default NETWORKS;
// type: 'xdai', // type: 'xdai',
// subType: 'optimism', // subType: 'optimism',
// group: 'mainnets', // group: 'mainnets',
// icon: 'https://www.fillmurray.com/60/60' // icon: 'https://www.fillmurray.com/60/60',
// chainId: 300 // chainId: 300,
// currency: 'xDAI',
// }, // },
// { // {
// name: 'Arbitrum on xDai', // name: 'Arbitrum on xDai',
// type: 'xdai', // type: 'xdai',
// subType: 'aox', // subType: 'aox',
// group: 'mainnets', // group: 'mainnets',
// chainId: 200 // chainId: 200,
// currency: 'xDAI',
// }, // },
// { // {
// name: 'Ethereum', // name: 'Ethereum',
...@@ -84,7 +87,8 @@ export default NETWORKS; ...@@ -84,7 +87,8 @@ export default NETWORKS;
// type: 'eth', // type: 'eth',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 1 // chainId: 1,
// currency: 'ETH',
// }, // },
// { // {
// name: 'Ethereum Classic', // name: 'Ethereum Classic',
...@@ -92,7 +96,8 @@ export default NETWORKS; ...@@ -92,7 +96,8 @@ export default NETWORKS;
// type: 'etc', // type: 'etc',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 61 // chainId: 61,
// currency: 'ETC',
// }, // },
// { // {
// name: 'POA', // name: 'POA',
...@@ -100,7 +105,10 @@ export default NETWORKS; ...@@ -100,7 +105,10 @@ export default NETWORKS;
// type: 'poa', // type: 'poa',
// subType: 'core', // subType: 'core',
// group: 'mainnets', // group: 'mainnets',
// chainId: 99 // chainId: 99,
// currency: 'POA',
// isAccountSupported: true,
// nativeTokenAddress: '0x029a799563238d0e75e20be2f4bda0ea68d00172',
// }, // },
// { // {
// name: 'RSK', // name: 'RSK',
...@@ -108,7 +116,8 @@ export default NETWORKS; ...@@ -108,7 +116,8 @@ export default NETWORKS;
// type: 'rsk', // type: 'rsk',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 30 // chainId: 30,
// currency: 'RBTC',
// }, // },
// { // {
// name: 'Gnosis Chain Testnet', // name: 'Gnosis Chain Testnet',
...@@ -116,6 +125,7 @@ export default NETWORKS; ...@@ -116,6 +125,7 @@ export default NETWORKS;
// subType: 'testnet', // subType: 'testnet',
// group: 'testnets', // group: 'testnets',
// isAccountSupported: true, // isAccountSupported: true,
// currency: 'xDAI',
// }, // },
// { // {
// name: 'POA Sokol', // name: 'POA Sokol',
...@@ -123,14 +133,16 @@ export default NETWORKS; ...@@ -123,14 +133,16 @@ export default NETWORKS;
// type: 'poa', // type: 'poa',
// subType: 'sokol', // subType: 'sokol',
// group: 'testnets', // group: 'testnets',
// chainId: 77 // chainId: 77,
// currency: 'SPOA',
// }, // },
// { // {
// name: 'ARTIS Σ1', // name: 'ARTIS Σ1',
// type: 'artis', // type: 'artis',
// subType: 'sigma1', // subType: 'sigma1',
// group: 'other', // group: 'other',
// chainId: 246529 // chainId: 246529,
// currency: 'ATS',
// }, // },
// { // {
// name: 'LUKSO L14', // name: 'LUKSO L14',
...@@ -138,6 +150,14 @@ export default NETWORKS; ...@@ -138,6 +150,14 @@ export default NETWORKS;
// type: 'lukso', // type: 'lukso',
// subType: 'l14', // subType: 'l14',
// group: 'other', // group: 'other',
// chainId: 22 // chainId: 22,
// currency: 'LYX',
// },
// {
// name: 'Astar',
// type: 'astar',
// group: 'other',
// chainId: 22,
// currency: 'ASTR',
// }, // },
// ]; // ];
import type { GetStaticPaths } from 'next'; import type { GetStaticPaths } from 'next';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
export const getStaticPaths: GetStaticPaths = async() => { export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false }; return { paths: [], fallback: true };
}; };
import type { GetStaticPaths } from 'next';
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: [], fallback: true };
};
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const ApiKeysPage: NextPage<Props> = ({ pageParams }: Props) => { const ApiKeysPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const CustomAbiPage: NextPage<Props> = ({ pageParams }: Props) => { const CustomAbiPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const PublicTagsPage: NextPage<Props> = ({ pageParams }: Props) => { const PublicTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => { const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const TransactionTagsPage: NextPage<Props> = ({ pageParams }: Props) => { const TransactionTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const WatchListPage: NextPage<Props> = ({ pageParams }: Props) => { const WatchListPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head> <Head>
......
...@@ -2,13 +2,13 @@ import Head from 'next/head'; ...@@ -2,13 +2,13 @@ import Head from 'next/head';
import React from 'react'; import React from 'react';
import Apps from 'ui/pages/Apps'; import Apps from 'ui/pages/Apps';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
const AppsPage = () => { const AppsPage = () => {
return ( return (
<Page> <Page>
<PageHeader text="Apps"/> <PageTitle text="Apps"/>
<Head><title>Apps</title></Head> <Head><title>Apps</title></Head>
<Apps/> <Apps/>
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import App from 'ui/pages/App';
const AppPage: NextPage = () => {
return (
<>
<Head><title>App Card Page</title></Head>
<App/>
</>
);
};
export default AppPage;
export { getStaticPaths } from 'lib/next/apps/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Link, Code } from '@chakra-ui/react'; import type { NextPage } from 'next';
import type { NextPage, GetStaticPaths } from 'next'; import Head from 'next/head';
import { useRouter } from 'next/router';
import type { ChangeEvent } from 'react';
import React from 'react'; import React from 'react';
import * as cookies from 'lib/cookies'; import Home from 'ui/pages/Home';
import useNetwork from 'lib/hooks/useNetwork';
import useToast from 'lib/hooks/useToast';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
const Home: NextPage = () => {
const router = useRouter();
const selectedNetwork = useNetwork();
const toast = useToast();
const [ isFormVisible, setFormVisibility ] = React.useState(false);
const [ token, setToken ] = React.useState('');
React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && selectedNetwork?.isAccountSupported));
}, [ selectedNetwork?.isAccountSupported ]);
const handleTokenChange = React.useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
setToken(event.target.value);
}, []);
const handleSetTokenClick = React.useCallback(() => {
cookies.set(cookies.NAMES.API_TOKEN, token);
setToken('');
toast({
position: 'top-right',
title: 'Success 🥳',
description: 'Successfully set cookie',
status: 'success',
variant: 'subtle',
isClosable: true,
onCloseComplete: () => {
setFormVisibility(false);
},
});
}, [ toast, token ]);
const prodUrl = new URL(`/${ router.query.network_type }/${ router.query.network_sub_type }`, 'https://blockscout.com').toString();
const HomePage: NextPage = () => {
return ( return (
<Page> <>
<VStack gap={ 4 } alignItems="flex-start" maxW="800px"> <Head><title>Home Page</title></Head>
<PageHeader text={ <Home/>
`Home Page for ${ selectedNetwork?.name } network` </>
}/>
{ /* will be deleted when we move to new CI */ }
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md">
!!! Temporary solution for authentication !!!
</AlertTitle>
<AlertDescription mt={ 3 }>
To Sign in go to <Link href={ prodUrl } target="_blank">{ prodUrl }</Link> first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment
</AlertDescription>
</Alert>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button>
</>
) }
</VStack>
</Page>
); );
}; };
export default Home; export default HomePage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => { export { getStaticPaths } from 'lib/next/account/getStaticPaths';
return { export { getStaticProps } from 'lib/next/getStaticProps';
props: {},
};
};
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => { const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -15,7 +15,7 @@ type Props = { ...@@ -15,7 +15,7 @@ type Props = {
} }
const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => { const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
const title = getNetworkTitle(pageParams); const title = getNetworkTitle(pageParams || {});
return ( return (
<> <>
<Head><title>{ title }</title></Head> <Head><title>{ title }</title></Head>
......
...@@ -4,8 +4,9 @@ export type NetworkGroup = 'mainnets' | 'testnets' | 'other'; ...@@ -4,8 +4,9 @@ export type NetworkGroup = 'mainnets' | 'testnets' | 'other';
export interface Network { export interface Network {
name: string; name: string;
// https://chainlist.org/ chainId: number; // https://chainlist.org/
chainId?: number; currency: string;
nativeTokenAddress: string;
shortName?: string; shortName?: string;
// basePath = /<type>/<subType>, e.g. /xdai/mainnet // basePath = /<type>/<subType>, e.g. /xdai/mainnet
type: string; type: string;
...@@ -14,4 +15,5 @@ export interface Network { ...@@ -14,4 +15,5 @@ export interface Network {
icon?: FunctionComponent<SVGAttributes<SVGElement>> | string; icon?: FunctionComponent<SVGAttributes<SVGElement>> | string;
logo?: FunctionComponent<SVGAttributes<SVGElement>> | string; logo?: FunctionComponent<SVGAttributes<SVGElement>> | string;
isAccountSupported?: boolean; isAccountSupported?: boolean;
assetsNamePath?: string;
} }
...@@ -7,6 +7,7 @@ import { block } from 'data/block'; ...@@ -7,6 +7,7 @@ import { block } from 'data/block';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useNetwork from 'lib/hooks/useNetwork';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -22,6 +23,7 @@ const BlockDetails = () => { ...@@ -22,6 +23,7 @@ const BlockDetails = () => {
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
const link = useLink(); const link = useLink();
const router = useRouter(); const router = useRouter();
const network = useNetwork();
const handleCutClick = React.useCallback(() => { const handleCutClick = React.useCallback(() => {
setIsExpanded((flag) => !flag); setIsExpanded((flag) => !flag);
...@@ -76,7 +78,10 @@ const BlockDetails = () => { ...@@ -76,7 +78,10 @@ const BlockDetails = () => {
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Block reward" title="Block reward"
hint="For each block, the miner is rewarded with a finite amount of Ether on top of the fees paid for all transactions in the block." hint={
`For each block, the miner is rewarded with a finite amount of ${ network?.currency || 'native token' }
on top of the fees paid for all transactions in the block.`
}
columnGap={ 1 } columnGap={ 1 }
> >
<Text>{ block.reward.static + block.reward.tx_fee - block.burnt_fees }</Text> <Text>{ block.reward.static + block.reward.tx_fee - block.burnt_fees }</Text>
...@@ -115,15 +120,15 @@ const BlockDetails = () => { ...@@ -115,15 +120,15 @@ const BlockDetails = () => {
title="Base fee per gas" title="Base fee per gas"
hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion." hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion."
> >
<Text>{ (block.base_fee_per_gas / 10 ** 9).toLocaleString('en', { minimumFractionDigits: 18 }) } Ether </Text> <Text>{ (block.base_fee_per_gas / 10 ** 9).toLocaleString('en', { minimumFractionDigits: 18 }) } { network?.currency } </Text>
<Text variant="secondary" whiteSpace="pre">{ space }({ block.base_fee_per_gas.toLocaleString('en', { minimumFractionDigits: 9 }) } Gwei)</Text> <Text variant="secondary" whiteSpace="pre">{ space }({ block.base_fee_per_gas.toLocaleString('en', { minimumFractionDigits: 9 }) } Gwei)</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Burnt fees" title="Burnt fees"
hint="Amount of ETH burned from transactions included in the block. Equals Block Base Fee per Gas * Gas Used." hint={ `Amount of ${ network?.currency || 'native token' } burned from transactions included in the block. Equals Block Base Fee per Gas * Gas Used.` }
> >
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
<Text ml={ 1 }>{ block.burnt_fees.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text> <Text ml={ 1 }>{ block.burnt_fees.toLocaleString('en', { minimumFractionDigits: 18 }) } { network?.currency }</Text>
<Tooltip label="Burnt fees / Txn fees * 100%"> <Tooltip label="Burnt fees / Txn fees * 100%">
<Box> <Box>
<Utilization ml={ 4 } value={ block.burnt_fees / block.reward.tx_fee }/> <Utilization ml={ 4 } value={ block.burnt_fees / block.reward.tx_fee }/>
......
...@@ -6,6 +6,7 @@ import type ArrayElement from 'types/utils/ArrayElement'; ...@@ -6,6 +6,7 @@ import type ArrayElement from 'types/utils/ArrayElement';
import type { blocks } from 'data/blocks'; import type { blocks } from 'data/blocks';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useNetwork from 'lib/hooks/useNetwork';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
import AccountListItemMobile from 'ui/shared/AccountListItemMobile'; import AccountListItemMobile from 'ui/shared/AccountListItemMobile';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -20,6 +21,7 @@ interface Props { ...@@ -20,6 +21,7 @@ interface Props {
const BlocksListItem = ({ data, isPending }: Props) => { const BlocksListItem = ({ data, isPending }: Props) => {
const spinnerEmptyColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200'); const spinnerEmptyColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const link = useLink(); const link = useLink();
const network = useNetwork();
return ( return (
<AccountListItemMobile rowGap={ 3 }> <AccountListItemMobile rowGap={ 3 }>
...@@ -56,7 +58,7 @@ const BlocksListItem = ({ data, isPending }: Props) => { ...@@ -56,7 +58,7 @@ const BlocksListItem = ({ data, isPending }: Props) => {
</Flex> </Flex>
</Box> </Box>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Reward ETH</Text> <Text fontWeight={ 500 }>Reward { network?.currency }</Text>
<Text variant="secondary">{ (data.reward.static + data.reward.tx_fee - data.burnt_fees).toLocaleString('en', { maximumFractionDigits: 5 }) }</Text> <Text variant="secondary">{ (data.reward.static + data.reward.tx_fee - data.burnt_fees).toLocaleString('en', { maximumFractionDigits: 5 }) }</Text>
</Flex> </Flex>
<Flex> <Flex>
......
...@@ -2,9 +2,12 @@ import { Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react'; ...@@ -2,9 +2,12 @@ import { Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { blocks } from 'data/blocks'; import { blocks } from 'data/blocks';
import useNetwork from 'lib/hooks/useNetwork';
import BlocksTableItem from 'ui/blocks/BlocksTableItem'; import BlocksTableItem from 'ui/blocks/BlocksTableItem';
const BlocksTable = () => { const BlocksTable = () => {
const network = useNetwork();
return ( return (
<TableContainer width="100%" mt={ 8 }> <TableContainer width="100%" mt={ 8 }>
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }> <Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }>
...@@ -15,8 +18,8 @@ const BlocksTable = () => { ...@@ -15,8 +18,8 @@ const BlocksTable = () => {
<Th width="144px">Miner</Th> <Th width="144px">Miner</Th>
<Th width="64px" isNumeric>Txn</Th> <Th width="64px" isNumeric>Txn</Th>
<Th width="40%">Gas used</Th> <Th width="40%">Gas used</Th>
<Th width="30%">Reward ETH</Th> <Th width="30%">Reward { network?.currency }</Th>
<Th width="30%">Burnt fees ETH</Th> <Th width="30%">Burnt fees { network?.currency }</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -12,8 +12,8 @@ import ApiKeyListItem from 'ui/apiKey/ApiKeyTable/ApiKeyListItem'; ...@@ -12,8 +12,8 @@ import ApiKeyListItem from 'ui/apiKey/ApiKeyTable/ApiKeyListItem';
import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable'; import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal'; import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile'; import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
...@@ -128,7 +128,7 @@ const ApiKeysPage: React.FC = () => { ...@@ -128,7 +128,7 @@ const ApiKeysPage: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<PageHeader text="API keys"/> <PageTitle text="API keys"/>
{ content } { content }
</Box> </Box>
</Page> </Page>
......
import { Center, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import Page from 'ui/shared/Page/Page';
const App = () => {
return (
<Page wrapChildren={ false }>
<Center as="main" bgColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') } h="100%" paddingTop={{ base: '138px', lg: 0 }}>
3rd party app content
</Center>
</Page>
);
};
export default App;
...@@ -5,8 +5,8 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; ...@@ -5,8 +5,8 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import BlockDetails from 'ui/block/BlockDetails'; import BlockDetails from 'ui/block/BlockDetails';
import BlockTxs from 'ui/block/BlockTxs'; import BlockTxs from 'ui/block/BlockTxs';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
const TABS: Array<RoutedTab> = [ const TABS: Array<RoutedTab> = [
...@@ -23,7 +23,7 @@ const BlockPageContent = ({ tab }: Props) => { ...@@ -23,7 +23,7 @@ const BlockPageContent = ({ tab }: Props) => {
return ( return (
<Page> <Page>
<PageHeader text={ `Block #${ router.query.id || '' }` }/> <PageTitle text={ `Block #${ router.query.id || '' }` }/>
<RoutedTabs <RoutedTabs
tabs={ TABS } tabs={ TABS }
defaultActiveTab={ tab } defaultActiveTab={ tab }
......
...@@ -11,8 +11,8 @@ import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem'; ...@@ -11,8 +11,8 @@ import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem';
import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable'; import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable';
import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal'; import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile'; import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
...@@ -116,7 +116,7 @@ const CustomAbiPage: React.FC = () => { ...@@ -116,7 +116,7 @@ const CustomAbiPage: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<PageHeader text="Custom ABI"/> <PageTitle text="Custom ABI"/>
{ content } { content }
</Box> </Box>
</Page> </Page>
......
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Link, Code } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import type { ChangeEvent } from 'react';
import React from 'react';
import * as cookies from 'lib/cookies';
import useNetwork from 'lib/hooks/useNetwork';
import useToast from 'lib/hooks/useToast';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
const Home = () => {
const router = useRouter();
const selectedNetwork = useNetwork();
const toast = useToast();
const [ isFormVisible, setFormVisibility ] = React.useState(false);
const [ token, setToken ] = React.useState('');
React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && selectedNetwork?.isAccountSupported));
}, [ selectedNetwork?.isAccountSupported ]);
const handleTokenChange = React.useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
setToken(event.target.value);
}, []);
const handleSetTokenClick = React.useCallback(() => {
cookies.set(cookies.NAMES.API_TOKEN, token);
setToken('');
toast({
position: 'top-right',
title: 'Success 🥳',
description: 'Successfully set cookie',
status: 'success',
variant: 'subtle',
isClosable: true,
onCloseComplete: () => {
setFormVisibility(false);
},
});
}, [ toast, token ]);
const prodUrl = new URL(`/${ router.query.network_type }/${ router.query.network_sub_type }`, 'https://blockscout.com').toString();
return (
<Page>
<VStack gap={ 4 } alignItems="flex-start" maxW="800px">
<PageTitle text={
`Home Page for ${ selectedNetwork?.name } network`
}/>
{ /* will be deleted when we move to new CI */ }
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md">
!!! Temporary solution for authentication !!!
</AlertTitle>
<AlertDescription mt={ 3 }>
To Sign in go to <Link href={ prodUrl } target="_blank">{ prodUrl }</Link> first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment
</AlertDescription>
</Alert>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button>
</>
) }
</VStack>
</Page>
);
};
export default Home;
...@@ -4,8 +4,8 @@ import React from 'react'; ...@@ -4,8 +4,8 @@ import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import UserAvatar from 'ui/shared/UserAvatar'; import UserAvatar from 'ui/shared/UserAvatar';
const MyProfile = () => { const MyProfile = () => {
...@@ -56,7 +56,7 @@ const MyProfile = () => { ...@@ -56,7 +56,7 @@ const MyProfile = () => {
return ( return (
<Page> <Page>
<PageHeader text="My profile"/> <PageTitle text="My profile"/>
{ content } { content }
</Page> </Page>
); );
......
import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags'; import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags'; import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
const TABS: Array<RoutedTab> = [ const TABS: Array<RoutedTab> = [
...@@ -21,10 +20,8 @@ type Props = { ...@@ -21,10 +20,8 @@ type Props = {
const PrivateTags = ({ tab }: Props) => { const PrivateTags = ({ tab }: Props) => {
return ( return (
<Page> <Page>
<Box h="100%"> <PageTitle text="Private tags"/>
<PageHeader text="Private tags"/> <RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
<RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
</Box>
</Page> </Page>
); );
}; };
......
import { Box, Link, Text, Icon } from '@chakra-ui/react'; import { Link, Text, Icon } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll } from 'react-scroll';
...@@ -9,8 +9,8 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -9,8 +9,8 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import PublicTagsData from 'ui/publicTags/PublicTagsData'; import PublicTagsData from 'ui/publicTags/PublicTagsData';
import PublicTagsForm from 'ui/publicTags/PublicTagsForm/PublicTagsForm'; import PublicTagsForm from 'ui/publicTags/PublicTagsForm/PublicTagsForm';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
type TScreen = 'data' | 'form'; type TScreen = 'data' | 'form';
...@@ -77,16 +77,14 @@ const PublicTagsComponent: React.FC = () => { ...@@ -77,16 +77,14 @@ const PublicTagsComponent: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> { isMobile && screen === 'form' && (
{ isMobile && screen === 'form' && ( <Link display="inline-flex" alignItems="center" mb={ 6 } onClick={ onGoBack }>
<Link display="inline-flex" alignItems="center" mb={ 6 } onClick={ onGoBack }> <Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/> <Text variant="inherit" fontSize="sm" ml={ 2 }>Public tags</Text>
<Text variant="inherit" fontSize="sm" ml={ 2 }>Public tags</Text> </Link>
</Link> ) }
) } <PageTitle text={ header }/>
<PageHeader text={ header }/> { content }
{ content }
</Box>
</Page> </Page>
); );
}; };
......
...@@ -6,8 +6,8 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; ...@@ -6,8 +6,8 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
import ExternalLink from 'ui/shared/ExternalLink'; import ExternalLink from 'ui/shared/ExternalLink';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import TxDetails from 'ui/tx/TxDetails'; import TxDetails from 'ui/tx/TxDetails';
import TxInternals from 'ui/tx/TxInternals'; import TxInternals from 'ui/tx/TxInternals';
...@@ -37,7 +37,7 @@ const TransactionPageContent = ({ tab }: Props) => { ...@@ -37,7 +37,7 @@ const TransactionPageContent = ({ tab }: Props) => {
<Icon as={ eastArrowIcon } boxSize={ 6 } mr={ 2 } transform="rotate(180deg)"/> <Icon as={ eastArrowIcon } boxSize={ 6 } mr={ 2 } transform="rotate(180deg)"/>
Transactions Transactions
</Link> </Link>
<PageHeader text="Transaction details"/> <PageTitle text="Transaction details"/>
<Flex marginLeft="auto" alignItems="center" flexWrap="wrap" columnGap={ 6 } rowGap={ 3 } mb={ 6 }> <Flex marginLeft="auto" alignItems="center" flexWrap="wrap" columnGap={ 6 } rowGap={ 3 } mb={ 6 }>
<ExternalLink title="Open in Tenderly" href="#"/> <ExternalLink title="Open in Tenderly" href="#"/>
<ExternalLink title="Open in Blockchair" href="#"/> <ExternalLink title="Open in Blockchair" href="#"/>
......
...@@ -5,8 +5,8 @@ import React from 'react'; ...@@ -5,8 +5,8 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import TxsPending from 'ui/txs/TxsPending'; import TxsPending from 'ui/txs/TxsPending';
import TxsValidated from 'ui/txs/TxsValidated'; import TxsValidated from 'ui/txs/TxsValidated';
...@@ -25,7 +25,7 @@ const Transactions = ({ tab }: Props) => { ...@@ -25,7 +25,7 @@ const Transactions = ({ tab }: Props) => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<PageHeader text="Transactions"/> <PageTitle text="Transactions"/>
<RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/> <RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
</Box> </Box>
</Page> </Page>
......
...@@ -8,8 +8,8 @@ import useFetch from 'lib/hooks/useFetch'; ...@@ -8,8 +8,8 @@ import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page'; import Page from 'ui/shared/Page/Page';
import PageHeader from 'ui/shared/PageHeader'; import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile'; import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
import AddressModal from 'ui/watchlist/AddressModal/AddressModal'; import AddressModal from 'ui/watchlist/AddressModal/AddressModal';
...@@ -113,7 +113,7 @@ const WatchList: React.FC = () => { ...@@ -113,7 +113,7 @@ const WatchList: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<PageHeader text="Watch list"/> <PageTitle text="Watch list"/>
{ content } { content }
</Box> </Box>
</Page> </Page>
......
import { Button, Circle, Icon, useColorModeValue } from '@chakra-ui/react'; import { Box, Button, Circle, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import filterIcon from 'icons/filter.svg'; import filterIcon from 'icons/filter.svg';
const FilterIcon = <Icon as={ filterIcon } boxSize={ 5 }/>; const FilterIcon = <Icon as={ filterIcon } boxSize={ 5 } mr={{ base: 0, lg: 2 }}/>;
interface Props { interface Props {
isActive: boolean; isActive: boolean;
isCollapsed?: boolean;
appliedFiltersNum?: number; appliedFiltersNum?: number;
onClick: () => void; onClick: () => void;
} }
const FilterButton = ({ isActive, appliedFiltersNum, onClick, isCollapsed }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => { const FilterButton = ({ isActive, appliedFiltersNum, onClick }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const badgeColor = useColorModeValue('white', 'black'); const badgeColor = useColorModeValue('white', 'black');
const badgeBgColor = useColorModeValue('blue.700', 'gray.50'); const badgeBgColor = useColorModeValue('blue.700', 'gray.50');
return ( return (
<Button <Button
ref={ ref } ref={ ref }
leftIcon={ isCollapsed ? undefined : FilterIcon }
rightIcon={ appliedFiltersNum ? <Circle bg={ badgeBgColor } size={ 5 } color={ badgeColor }>{ appliedFiltersNum }</Circle> : undefined } rightIcon={ appliedFiltersNum ? <Circle bg={ badgeBgColor } size={ 5 } color={ badgeColor }>{ appliedFiltersNum }</Circle> : undefined }
size="sm" size="sm"
fontWeight="500" fontWeight="500"
...@@ -30,7 +28,8 @@ const FilterButton = ({ isActive, appliedFiltersNum, onClick, isCollapsed }: Pro ...@@ -30,7 +28,8 @@ const FilterButton = ({ isActive, appliedFiltersNum, onClick, isCollapsed }: Pro
px={ 1.5 } px={ 1.5 }
flexShrink={ 0 } flexShrink={ 0 }
> >
{ isCollapsed ? FilterIcon : 'Filter' } { FilterIcon }
<Box display={{ base: 'none', lg: 'block' }}>Filter</Box>
</Button> </Button>
); );
}; };
......
import { Box, HStack, VStack } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import PageContent from 'ui/shared/Page/PageContent';
import Header from 'ui/snippets/header/Header'; import Header from 'ui/snippets/header/Header';
import NavigationDesktop from 'ui/snippets/navigation/NavigationDesktop'; import NavigationDesktop from 'ui/snippets/navigation/NavigationDesktop';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
wrapChildren?: boolean;
} }
const Page = ({ children }: Props) => { const Page = ({ children, wrapChildren = true }: Props) => {
const isMobile = useIsMobile();
const router = useRouter(); const router = useRouter();
const fetch = useFetch(); const fetch = useFetch();
...@@ -32,30 +32,18 @@ const Page = ({ children }: Props) => { ...@@ -32,30 +32,18 @@ const Page = ({ children }: Props) => {
} }
}, [ networkType, networkSubType ]); }, [ networkType, networkSubType ]);
const renderedChildren = wrapChildren ? (
<PageContent>{ children }</PageContent>
) : children;
return ( return (
<HStack <Flex w="100%" minH="100vh" alignItems="stretch">
w="100%" <NavigationDesktop/>
minH="100vh" <Flex flexDir="column" width="100%">
alignItems="stretch"
>
{ !isMobile && <NavigationDesktop/> }
<VStack
width="100%"
paddingX={ isMobile ? 4 : 8 }
paddingTop={ isMobile ? 0 : 9 }
paddingBottom={ 10 }
spacing={ 0 }
>
<Header/> <Header/>
<Box { renderedChildren }
as="main" </Flex>
w="100%" </Flex>
paddingTop={ isMobile ? '138px' : '52px' }
>
{ children }
</Box>
</VStack>
</HStack>
); );
}; };
......
import { Box } from '@chakra-ui/react';
import React from 'react';
interface Props {
children: React.ReactNode;
}
const PageContent = ({ children }: Props) => {
return (
<Box
as="main"
w="100%"
paddingX={{ base: 4, lg: 12 }}
paddingBottom={ 10 }
paddingTop={{ base: '138px', lg: 0 }}
>
{ children }
</Box>
);
};
export default PageContent;
import { Heading } from '@chakra-ui/react'; import { Heading } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
const PageHeader = ({ text }: {text: string}) => { const PageTitle = ({ text }: {text: string}) => {
return ( return (
<Heading as="h1" size="lg" marginBottom={{ base: 6, lg: 8 }}>{ text }</Heading> <Heading as="h1" size="lg" marginBottom={{ base: 6, lg: 8 }}>{ text }</Heading>
); );
}; };
export default PageHeader; export default PageTitle;
...@@ -6,13 +6,11 @@ import arrowIcon from 'icons/arrows/east-mini.svg'; ...@@ -6,13 +6,11 @@ import arrowIcon from 'icons/arrows/east-mini.svg';
type Props = { type Props = {
currentPage: number; currentPage: number;
maxPage?: number; maxPage?: number;
isMobile?: boolean;
} }
const MAX_PAGE_DEFAULT = 50; const MAX_PAGE_DEFAULT = 50;
const Pagination = ({ currentPage, maxPage, isMobile }: Props) => { const Pagination = ({ currentPage, maxPage }: Props) => {
const pageNumber = ( const pageNumber = (
<Flex alignItems="center"> <Flex alignItems="center">
<Button <Button
...@@ -42,20 +40,21 @@ const Pagination = ({ currentPage, maxPage, isMobile }: Props) => { ...@@ -42,20 +40,21 @@ const Pagination = ({ currentPage, maxPage, isMobile }: Props) => {
</Flex> </Flex>
); );
if (isMobile) { return (
return ( <Flex
<Flex fontSize="sm"
fontSize="sm" width={{ base: '100%', lg: 'auto' }}
width="100%" justifyContent={{ base: 'space-between', lg: 'unset' }}
justifyContent="space-between" alignItems="center"
alignItems="center" >
> <Flex alignItems="center" justifyContent="space-between" w={{ base: '100%', lg: 'auto' }}>
<IconButton <IconButton
variant="outline" variant="outline"
size="sm" size="sm"
aria-label="Next page" aria-label="Next page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> }
mr={ 8 }
/> />
{ pageNumber } { pageNumber }
<IconButton <IconButton
...@@ -64,42 +63,14 @@ const Pagination = ({ currentPage, maxPage, isMobile }: Props) => { ...@@ -64,42 +63,14 @@ const Pagination = ({ currentPage, maxPage, isMobile }: Props) => {
aria-label="Next page" aria-label="Next page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> }
/>
</Flex>
);
}
return (
<Flex
fontSize="sm"
>
<Flex alignItems="center" justifyContent="space-between">
<Button
variant="outline"
size="sm"
aria-label="Next page"
leftIcon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> }
mr={ 8 }
pl={ 1 }
>
Previous
</Button>
{ pageNumber }
<Button
variant="outline"
size="sm"
aria-label="Next page"
rightIcon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> }
ml={ 8 } ml={ 8 }
pr={ 1 } />
>
Next
</Button>
</Flex> </Flex>
<Flex alignItems="center" width="132px" ml={ 16 }> <Flex alignItems="center" width="132px" ml={ 16 } display={{ base: 'none', lg: 'flex' }}>
Go to <Input w="84px" size="xs" ml={ 2 }/> Go to <Input w="84px" size="xs" ml={ 2 }/>
</Flex> </Flex>
</Flex> </Flex>
); );
}; };
......
...@@ -33,7 +33,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => { ...@@ -33,7 +33,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
const defaultIndex = tabs.findIndex(({ routeName }) => routeName === defaultActiveTab); const defaultIndex = tabs.findIndex(({ routeName }) => routeName === defaultActiveTab);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [ activeTab, setActiveTab ] = React.useState<number>(defaultIndex); const [ activeTab ] = React.useState<number>(defaultIndex);
const { tabsCut, tabsList, tabsRefs, listRef } = useAdaptiveTabs(tabs, isMobile); const { tabsCut, tabsList, tabsRefs, listRef } = useAdaptiveTabs(tabs, isMobile);
const router = useRouter(); const router = useRouter();
...@@ -45,8 +45,6 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => { ...@@ -45,8 +45,6 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
const newUrl = link(nextTab.routeName, router.query); const newUrl = link(nextTab.routeName, router.query);
router.push(newUrl, undefined, { shallow: true }); router.push(newUrl, undefined, { shallow: true });
} }
setActiveTab(index);
}, [ tabs, router ]); }, [ tabs, router ]);
return ( return (
...@@ -57,7 +55,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => { ...@@ -57,7 +55,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
whiteSpace="nowrap" whiteSpace="nowrap"
ref={ listRef } ref={ listRef }
overflowY="hidden" overflowY="hidden"
overflowX={ isMobile ? 'auto' : undefined } overflowX={{ base: 'auto', lg: undefined }}
overscrollBehaviorX="contain" overscrollBehaviorX="contain"
css={{ css={{
'scroll-snap-type': 'x mandatory', 'scroll-snap-type': 'x mandatory',
......
import { Icon, IconButton } from '@chakra-ui/react'; import { Icon, IconButton, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import upDownArrow from 'icons/arrows/up-down.svg'; import upDownArrow from 'icons/arrows/up-down.svg';
...@@ -6,9 +6,10 @@ import upDownArrow from 'icons/arrows/up-down.svg'; ...@@ -6,9 +6,10 @@ import upDownArrow from 'icons/arrows/up-down.svg';
type Props = { type Props = {
handleSort: () => void; handleSort: () => void;
isSortActive: boolean; isSortActive: boolean;
className?: string;
} }
const SortButton = ({ handleSort, isSortActive }: Props) => { const SortButton = ({ handleSort, isSortActive, className }: Props) => {
return ( return (
<IconButton <IconButton
icon={ <Icon as={ upDownArrow } boxSize={ 5 }/> } icon={ <Icon as={ upDownArrow } boxSize={ 5 }/> }
...@@ -16,12 +17,12 @@ const SortButton = ({ handleSort, isSortActive }: Props) => { ...@@ -16,12 +17,12 @@ const SortButton = ({ handleSort, isSortActive }: Props) => {
size="sm" size="sm"
variant="outline" variant="outline"
colorScheme="gray-dark" colorScheme="gray-dark"
ml={ 2 }
minWidth="36px" minWidth="36px"
onClick={ handleSort } onClick={ handleSort }
isActive={ isSortActive } isActive={ isSortActive }
className={ className }
/> />
); );
}; };
export default SortButton; export default chakra(SortButton);
import { Center, Icon, Link, Text, chakra } from '@chakra-ui/react';
import React from 'react';
import tokeIcon from 'icons/tokens/toke.svg';
import usdtIcon from 'icons/tokens/usdt.svg';
import useLink from 'lib/link/useLink';
// temporary solution
// don't know where to get icons and addresses yet
const TOKENS = {
USDT: {
fullName: 'Tether USD',
symbol: 'USDT',
icon: usdtIcon,
address: '0x9bD35A17C9C7c8820f89e0277e2046CDC57aCB15',
},
TOKE: {
fullName: 'Tokemak',
symbol: 'TOKE',
icon: tokeIcon,
address: '0x9bD35A17C9C7c8820f89e0277e2046CDC57aCB15',
},
};
interface Props {
symbol: string;
className?: string;
}
const Token = ({ symbol, className }: Props) => {
const token = TOKENS[symbol as keyof typeof TOKENS];
const link = useLink();
if (!token) {
return null;
}
const url = link('token_index', { id: token.address });
return (
<Center className={ className }>
<Icon as={ token.icon } boxSize={ 5 }/>
<Link href={ url } target="_blank" ml={ 1 }>
{ token.fullName }
</Link>
<Text ml={ 1 } variant="secondary">({ token.symbol })</Text>
</Center>
);
};
export default chakra(Token);
import { Image, chakra } from '@chakra-ui/react';
import React from 'react';
import type { Network } from 'types/networks';
import useNetwork from 'lib/hooks/useNetwork';
const EmptyElement = () => null;
const ASSETS_PATH_MAP: Record<string, string> = {
'xdai/mainnet': 'xdai',
'xdai/testnet': 'xdai',
'xdai/optimism': 'optimism',
'xdai/aox': 'arbitrum',
'eth/mainnet': 'ethereum',
'etc/mainnet': 'classic',
'poa/core': 'poa',
};
const getAssetsPath = (network: Network) => {
if (network.assetsNamePath) {
return network.assetsNamePath;
}
const key = [ network.type, network.subType ].filter(Boolean).join('/');
const nameFromMap = ASSETS_PATH_MAP[key];
return nameFromMap || network.type;
};
interface Props {
hash: string;
name: string;
className?: string;
}
const TokenLogo = ({ hash, name, className }: Props) => {
const network = useNetwork();
if (!network) {
return null;
}
const assetsPath = getAssetsPath(network);
const logoSrc = `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/${ assetsPath }/assets/${ hash }/logo.png`;
return <Image className={ className } src={ logoSrc } alt={ `${ name } logo` } fallback={ <EmptyElement/> }/>;
};
export default React.memo(chakra(TokenLogo));
import { Center, Link, Text, chakra } from '@chakra-ui/react';
import React from 'react';
import useLink from 'lib/link/useLink';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
symbol: string;
hash: string;
name: string;
className?: string;
}
const TokenSnippet = ({ symbol, hash, name, className }: Props) => {
const link = useLink();
const url = link('token_index', { id: hash });
return (
<Center className={ className } columnGap={ 1 }>
<TokenLogo boxSize={ 5 } hash={ hash } name={ name }/>
<Link href={ url } target="_blank">
{ name }
</Link>
<Text variant="secondary">({ symbol })</Text>
</Center>
);
};
export default chakra(TokenSnippet);
import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react'; import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop'; import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile'; import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile';
...@@ -11,12 +10,10 @@ import Burger from './Burger'; ...@@ -11,12 +10,10 @@ import Burger from './Burger';
import ColorModeToggler from './ColorModeToggler'; import ColorModeToggler from './ColorModeToggler';
const Header = () => { const Header = () => {
const isMobile = useIsMobile();
const bgColor = useColorModeValue('white', 'black'); const bgColor = useColorModeValue('white', 'black');
return (
if (isMobile) { <>
return ( <Box bgColor={ bgColor } display={{ base: 'block', lg: 'none' }}>
<Box bgColor={ bgColor }>
<Flex <Flex
as="header" as="header"
position="fixed" position="fixed"
...@@ -36,21 +33,22 @@ const Header = () => { ...@@ -36,21 +33,22 @@ const Header = () => {
</Flex> </Flex>
<SearchBar/> <SearchBar/>
</Box> </Box>
); <HStack
} as="header"
width="100%"
return ( alignItems="center"
<HStack justifyContent="center"
as="header" gap={ 12 }
width="100%" display={{ base: 'none', lg: 'flex' }}
alignItems="center" paddingX={ 12 }
justifyContent="center" paddingTop={ 9 }
gap={ 12 } paddingBottom="52px"
> >
<SearchBar/> <SearchBar/>
<ColorModeToggler/> <ColorModeToggler/>
<ProfileMenuDesktop/> <ProfileMenuDesktop/>
</HStack> </HStack>
</>
); );
}; };
......
import { VStack, Text, Stack, Icon, Link, useColorModeValue } from '@chakra-ui/react'; import { Box, VStack, Text, Stack, Icon, Link, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import ghIcon from 'icons/social/git.svg'; import ghIcon from 'icons/social/git.svg';
import statsIcon from 'icons/social/stats.svg'; import statsIcon from 'icons/social/stats.svg';
import tgIcon from 'icons/social/telega.svg'; import tgIcon from 'icons/social/telega.svg';
import twIcon from 'icons/social/tweet.svg'; import twIcon from 'icons/social/tweet.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
const SOCIAL_LINKS = [ const SOCIAL_LINKS = [
...@@ -24,22 +23,13 @@ interface Props { ...@@ -24,22 +23,13 @@ interface Props {
} }
const NavFooter = ({ isCollapsed, hasAccount }: Props) => { const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
const isMobile = useIsMobile(); const isExpanded = isCollapsed === false;
const width = (() => {
if (isMobile) {
return '100%';
}
return isCollapsed ? '20px' : '180px';
})();
const marginTop = (() => { const marginTop = (() => {
if (!hasAccount) { if (!hasAccount) {
return 'auto'; return 'auto';
} }
return isMobile ? 6 : 20; return { base: 6, lg: 20 };
})(); })();
return ( return (
...@@ -48,8 +38,8 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => { ...@@ -48,8 +38,8 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
spacing={ 8 } spacing={ 8 }
borderTop="1px solid" borderTop="1px solid"
borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') } borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
width={ width } width={{ base: '100%', lg: isExpanded ? '180px' : '20px', xl: isCollapsed ? '20px' : '180px' }}
paddingTop={ isMobile ? 6 : 8 } paddingTop={{ base: 6, lg: 8 }}
marginTop={ marginTop } marginTop={ marginTop }
alignItems="flex-start" alignItems="flex-start"
alignSelf="center" alignSelf="center"
...@@ -57,7 +47,7 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => { ...@@ -57,7 +47,7 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
fontSize="xs" fontSize="xs"
{ ...getDefaultTransitionProps({ transitionProperty: 'width' }) } { ...getDefaultTransitionProps({ transitionProperty: 'width' }) }
> >
<Stack direction={ isCollapsed ? 'column' : 'row' }> <Stack direction={{ base: 'row', lg: isExpanded ? 'row' : 'column', xl: isCollapsed ? 'column' : 'row' }}>
{ SOCIAL_LINKS.map(sl => { { SOCIAL_LINKS.map(sl => {
return ( return (
<Link href={ sl.link } key={ sl.link } variant="secondary" w={ 5 } h={ 5 } aria-label={ sl.label }> <Link href={ sl.link } key={ sl.link } variant="secondary" w={ 5 } h={ 5 } aria-label={ sl.label }>
...@@ -66,14 +56,12 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => { ...@@ -66,14 +56,12 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
); );
}) } }) }
</Stack> </Stack>
{ !isCollapsed && ( <Box display={{ base: 'block', lg: isExpanded ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}>
<> <Text variant="secondary" mb={ 8 }>
<Text variant="secondary">
Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks. Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks.
</Text> </Text>
<Text variant="secondary">Version: <Link href={ VERSION_URL } target="_blank">{ BLOCKSCOUT_VERSION }</Link></Text> <Text variant="secondary">Version: <Link href={ VERSION_URL } target="_blank">{ BLOCKSCOUT_VERSION }</Link></Text>
</> </Box>
) }
</VStack> </VStack>
); );
}; };
......
...@@ -2,7 +2,6 @@ import { Link, Icon, Text, HStack, Tooltip, Box } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Link, Icon, Text, HStack, Tooltip, Box } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import useColors from './useColors'; import useColors from './useColors';
...@@ -18,21 +17,15 @@ interface Props { ...@@ -18,21 +17,15 @@ interface Props {
const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => { const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => {
const colors = useColors(); const colors = useColors();
const isMobile = useIsMobile();
const width = (() => {
if (isMobile) {
return '100%';
}
return isCollapsed ? '60px' : '180px'; const isExpanded = isCollapsed === false;
})();
return ( return (
<Box as="li" listStyleType="none" w="100%"> <Box as="li" listStyleType="none" w="100%">
<NextLink href={ url } passHref> <NextLink href={ url } passHref>
<Link <Link
w={ width } w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }}
px={ px || (isCollapsed ? '15px' : 3) } px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
py={ 2.5 } py={ 2.5 }
display="flex" display="flex"
color={ isActive ? colors.text.active : colors.text.default } color={ isActive ? colors.text.active : colors.text.default }
...@@ -53,7 +46,14 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => { ...@@ -53,7 +46,14 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => {
> >
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
<Icon as={ icon } boxSize="30px"/> <Icon as={ icon } boxSize="30px"/>
{ !isCollapsed && <Text variant="inherit" fontSize="sm" lineHeight="20px">{ text }</Text> } <Text
variant="inherit"
fontSize="sm"
lineHeight="20px"
display={{ base: 'block', lg: isExpanded ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}
>
{ text }
</Text>
</HStack> </HStack>
</Tooltip> </Tooltip>
</Link> </Link>
......
import { Flex, Box, VStack, Icon, useColorModeValue, useBreakpointValue } from '@chakra-ui/react'; import { Flex, Box, VStack, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import chevronIcon from 'icons/arrows/east-mini.svg'; import chevronIcon from 'icons/arrows/east-mini.svg';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useNavItems from 'lib/hooks/useNavItems'; import useNavItems from 'lib/hooks/useNavItems';
import useNetwork from 'lib/hooks/useNetwork'; import useNetwork from 'lib/hooks/useNetwork';
import isBrowser from 'lib/isBrowser';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu'; import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu';
...@@ -15,18 +16,24 @@ import NavLink from './NavLink'; ...@@ -15,18 +16,24 @@ import NavLink from './NavLink';
const NavigationDesktop = () => { const NavigationDesktop = () => {
const { mainNavItems, accountNavItems } = useNavItems(); const { mainNavItems, accountNavItems } = useNavItems();
const selectedNetwork = useNetwork(); const selectedNetwork = useNetwork();
const isLargeScreen = useBreakpointValue({ base: false, xl: true });
const navBarCollapsedCookie = cookies.get(cookies.NAMES.NAV_BAR_COLLAPSED);
const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
const hasAccount = selectedNetwork?.isAccountSupported && isAuth;
const [ isCollapsed, setCollapsedState ] = React.useState(navBarCollapsedCookie === 'true'); const isInBrowser = isBrowser();
const [ hasAccount, setHasAccount ] = React.useState(false);
const [ isCollapsed, setCollapsedState ] = React.useState<boolean | undefined>();
React.useEffect(() => { React.useEffect(() => {
if (!navBarCollapsedCookie) { const navBarCollapsedCookie = cookies.get(cookies.NAMES.NAV_BAR_COLLAPSED);
setCollapsedState(!isLargeScreen); const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
if (isInBrowser) {
if (navBarCollapsedCookie === 'true') {
setCollapsedState(true);
}
if (navBarCollapsedCookie === 'false') {
setCollapsedState(false);
}
setHasAccount(Boolean(selectedNetwork?.isAccountSupported && isAuth && isInBrowser));
} }
}, [ isLargeScreen, navBarCollapsedCookie ]); }, [ isInBrowser, selectedNetwork?.isAccountSupported ]);
const handleTogglerClick = React.useCallback(() => { const handleTogglerClick = React.useCallback(() => {
setCollapsedState((flag) => !flag); setCollapsedState((flag) => !flag);
...@@ -40,16 +47,19 @@ const NavigationDesktop = () => { ...@@ -40,16 +47,19 @@ const NavigationDesktop = () => {
borderColor: useColorModeValue('blackAlpha.200', 'whiteAlpha.200'), borderColor: useColorModeValue('blackAlpha.200', 'whiteAlpha.200'),
}; };
const isExpanded = isCollapsed === false;
return ( return (
<Flex <Flex
display={{ base: 'none', lg: 'flex' }}
position="relative" position="relative"
flexDirection="column" flexDirection="column"
alignItems="flex-start" alignItems="flex-start"
borderRight="1px solid" borderRight="1px solid"
borderColor={ containerBorderColor } borderColor={ containerBorderColor }
px={ isCollapsed ? 4 : 6 } px={{ lg: isExpanded ? 6 : 4, xl: isCollapsed ? 4 : 6 }}
py={ 12 } py={ 12 }
width={ isCollapsed ? '92px' : '229px' } width={{ lg: isExpanded ? '229px' : '92px', xl: isCollapsed ? '92px' : '229px' }}
{ ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) } { ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) }
> >
<Box <Box
...@@ -86,12 +96,12 @@ const NavigationDesktop = () => { ...@@ -86,12 +96,12 @@ const NavigationDesktop = () => {
_hover={{ color: 'blue.400' }} _hover={{ color: 'blue.400' }}
borderRadius="base" borderRadius="base"
{ ...chevronIconStyles } { ...chevronIconStyles }
transform={ isCollapsed ? 'rotate(180deg)' : 'rotate(0)' } transform={{ lg: isExpanded ? 'rotate(0)' : 'rotate(180deg)', xl: isCollapsed ? 'rotate(180deg)' : 'rotate(0)' }}
{ ...getDefaultTransitionProps({ transitionProperty: 'transform, left' }) } { ...getDefaultTransitionProps({ transitionProperty: 'transform, left' }) }
transformOrigin="center" transformOrigin="center"
position="absolute" position="absolute"
top="104px" top="104px"
left={ isCollapsed ? '80px' : '216px' } left={{ lg: isExpanded ? '216px' : '80px', xl: isCollapsed ? '80px' : '216px' }}
cursor="pointer" cursor="pointer"
onClick={ handleTogglerClick } onClick={ handleTogglerClick }
/> />
......
...@@ -57,7 +57,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => { ...@@ -57,7 +57,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
<NextLink href={ href } passHref> <NextLink href={ href } passHref>
<Box <Box
as="a" as="a"
width={ isCollapsed ? '0' : '113px' } width={{ base: '113px', lg: isCollapsed === false ? '113px' : 0, xl: isCollapsed ? '0' : '113px' }}
display="inline-flex" display="inline-flex"
overflow="hidden" overflow="hidden"
onClick={ onClick } onClick={ onClick }
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import NetworkMenuButton from './NetworkMenuButton'; import NetworkMenuButton from './NetworkMenuButton';
import NetworkMenuContentDesktop from './NetworkMenuContentDesktop'; import NetworkMenuContentDesktop from './NetworkMenuContentDesktop';
interface Props { interface Props {
isCollapsed: boolean; isCollapsed?: boolean;
} }
const NetworkMenu = ({ isCollapsed }: Props) => { const NetworkMenu = ({ isCollapsed }: Props) => {
...@@ -13,7 +13,7 @@ const NetworkMenu = ({ isCollapsed }: Props) => { ...@@ -13,7 +13,7 @@ const NetworkMenu = ({ isCollapsed }: Props) => {
{ ({ isOpen }) => ( { ({ isOpen }) => (
<> <>
<PopoverTrigger> <PopoverTrigger>
<Box marginLeft={ isCollapsed ? '0px' : '7px' }> <Box marginLeft={{ base: '7px', lg: isCollapsed === false ? '7px' : '0px', xl: isCollapsed ? '0px' : '7px' }}>
<NetworkMenuButton isActive={ isOpen }/> <NetworkMenuButton isActive={ isOpen }/>
</Box> </Box>
</PopoverTrigger> </PopoverTrigger>
......
import type { ChangeEvent, FormEvent } from 'react'; import type { ChangeEvent, FormEvent } from 'react';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
import SearchBarDesktop from './SearchBarDesktop'; import SearchBarDesktop from './SearchBarDesktop';
...@@ -10,7 +9,6 @@ import SearchBarMobile from './SearchBarMobile'; ...@@ -10,7 +9,6 @@ import SearchBarMobile from './SearchBarMobile';
const SearchBar = () => { const SearchBar = () => {
const [ value, setValue ] = React.useState(''); const [ value, setValue ] = React.useState('');
const link = useLink(); const link = useLink();
const isMobile = useIsMobile();
const handleChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => { const handleChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value); setValue(event.target.value);
...@@ -22,14 +20,11 @@ const SearchBar = () => { ...@@ -22,14 +20,11 @@ const SearchBar = () => {
window.location.assign(url); window.location.assign(url);
}, [ link, value ]); }, [ link, value ]);
if (isMobile) {
return (
<SearchBarMobile onChange={ handleChange } onSubmit={ handleSubmit }/>
);
}
return ( return (
<SearchBarDesktop onChange={ handleChange } onSubmit={ handleSubmit }/> <>
<SearchBarDesktop onChange={ handleChange } onSubmit={ handleSubmit }/>
<SearchBarMobile onChange={ handleChange } onSubmit={ handleSubmit }/>
</>
); );
}; };
......
import { InputGroup, Input, InputLeftAddon, InputLeftElement, Icon, useColorModeValue } from '@chakra-ui/react'; import { InputGroup, Input, InputLeftAddon, InputLeftElement, Icon, chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ChangeEvent, FormEvent } from 'react'; import type { ChangeEvent, FormEvent } from 'react';
...@@ -11,7 +11,7 @@ interface Props { ...@@ -11,7 +11,7 @@ interface Props {
const SearchBarDesktop = ({ onChange, onSubmit }: Props) => { const SearchBarDesktop = ({ onChange, onSubmit }: Props) => {
return ( return (
<form noValidate onSubmit={ onSubmit }> <chakra.form noValidate onSubmit={ onSubmit } display={{ base: 'none', lg: 'block' }} w="100%">
<InputGroup> <InputGroup>
<InputLeftAddon w="111px">All filters</InputLeftAddon> <InputLeftAddon w="111px">All filters</InputLeftAddon>
<InputLeftElement w={ 6 } ml="132px" mr={ 2.5 }> <InputLeftElement w={ 6 } ml="132px" mr={ 2.5 }>
...@@ -25,7 +25,7 @@ const SearchBarDesktop = ({ onChange, onSubmit }: Props) => { ...@@ -25,7 +25,7 @@ const SearchBarDesktop = ({ onChange, onSubmit }: Props) => {
borderColor={ useColorModeValue('blackAlpha.100', 'whiteAlpha.200') } borderColor={ useColorModeValue('blackAlpha.100', 'whiteAlpha.200') }
/> />
</InputGroup> </InputGroup>
</form> </chakra.form>
); );
}; };
......
...@@ -61,6 +61,8 @@ const SearchBarMobile = ({ onChange, onSubmit }: Props) => { ...@@ -61,6 +61,8 @@ const SearchBarMobile = ({ onChange, onSubmit }: Props) => {
transform={ isVisible ? 'translateY(0)' : 'translateY(-100%)' } transform={ isVisible ? 'translateY(0)' : 'translateY(-100%)' }
transitionProperty="transform" transitionProperty="transform"
transitionDuration="slow" transitionDuration="slow"
display={{ base: 'block', lg: 'none' }}
w="100%"
> >
<InputGroup size="sm"> <InputGroup size="sm">
<InputLeftElement > <InputLeftElement >
......
...@@ -4,14 +4,18 @@ import React from 'react'; ...@@ -4,14 +4,18 @@ import React from 'react';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import Token from 'ui/shared/Token'; import TokenSnippet from 'ui/shared/TokenSnippet';
interface Props { interface Props {
from: string; from: string;
to: string; to: string;
token: string;
amount: number; amount: number;
usd: number; usd: number;
token: {
symbol: string;
hash: string;
name: string;
};
} }
const TokenTransfer = ({ from, to, amount, usd, token }: Props) => { const TokenTransfer = ({ from, to, amount, usd, token }: Props) => {
...@@ -26,7 +30,7 @@ const TokenTransfer = ({ from, to, amount, usd, token }: Props) => { ...@@ -26,7 +30,7 @@ const TokenTransfer = ({ from, to, amount, usd, token }: Props) => {
<Text fontWeight={ 600 } as="span">{ amount }</Text>{ space } <Text fontWeight={ 600 } as="span">{ amount }</Text>{ space }
<Text fontWeight={ 400 } variant="secondary" as="span">(${ usd.toFixed(2) })</Text> <Text fontWeight={ 400 } variant="secondary" as="span">(${ usd.toFixed(2) })</Text>
</Text> </Text>
<Token symbol={ token }/> <TokenSnippet { ...token }/>
</Flex> </Flex>
); );
}; };
......
...@@ -8,6 +8,7 @@ import flameIcon from 'icons/flame.svg'; ...@@ -8,6 +8,7 @@ import flameIcon from 'icons/flame.svg';
import errorIcon from 'icons/status/error.svg'; import errorIcon from 'icons/status/error.svg';
import successIcon from 'icons/status/success.svg'; import successIcon from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useNetwork from 'lib/hooks/useNetwork';
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';
...@@ -17,7 +18,7 @@ import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; ...@@ -17,7 +18,7 @@ import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import PrevNext from 'ui/shared/PrevNext'; import PrevNext from 'ui/shared/PrevNext';
import RawInputData from 'ui/shared/RawInputData'; import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Token from 'ui/shared/Token'; import TokenSnippet from 'ui/shared/TokenSnippet';
import type { Props as TxStatusProps } from 'ui/shared/TxStatus'; import type { Props as TxStatusProps } from 'ui/shared/TxStatus';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization'; import Utilization from 'ui/shared/Utilization';
...@@ -25,6 +26,8 @@ import TokenTransfer from 'ui/tx/TokenTransfer'; ...@@ -25,6 +26,8 @@ import TokenTransfer from 'ui/tx/TokenTransfer';
import TxDecodedInputData from 'ui/tx/TxDecodedInputData'; import TxDecodedInputData from 'ui/tx/TxDecodedInputData';
const TxDetails = () => { const TxDetails = () => {
const selectedNetwork = useNetwork();
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleCutClick = React.useCallback(() => { const handleCutClick = React.useCallback(() => {
...@@ -108,14 +111,14 @@ const TxDetails = () => { ...@@ -108,14 +111,14 @@ const TxDetails = () => {
<Icon as={ errorIcon } boxSize={ 4 } ml={ 2 } color="red.500" cursor="pointer"/> <Icon as={ errorIcon } boxSize={ 4 } ml={ 2 } color="red.500" cursor="pointer"/>
</chakra.span> </chakra.span>
</Tooltip> </Tooltip>
<Token symbol="USDT" ml={ 3 }/> <TokenSnippet symbol="UP" name="User Pay" hash="0xA17ed5dFc62D0a3E74D69a0503AE9FdA65d9f212" ml={ 3 }/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Token transferred" title="Token transferred"
hint="List of token transferred in the transaction." hint="List of token transferred in the transaction."
> >
<Flex flexDirection="column" alignItems="flex-start" rowGap={ 5 } w="100%"> <Flex flexDirection="column" alignItems="flex-start" rowGap={ 5 } w="100%">
{ tx.transferred_tokens.map((item) => <TokenTransfer key={ item.token } { ...item }/>) } { tx.transferred_tokens.map((item) => <TokenTransfer key={ item.token.hash } { ...item }/>) }
</Flex> </Flex>
</DetailsInfoItem> </DetailsInfoItem>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/> <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/>
...@@ -123,21 +126,21 @@ const TxDetails = () => { ...@@ -123,21 +126,21 @@ const TxDetails = () => {
title="Value" title="Value"
hint="Value sent in the native token (and USD) if applicable." hint="Value sent in the native token (and USD) if applicable."
> >
<Text>{ tx.amount.value } Ether</Text> <Text>{ tx.amount.value } { selectedNetwork?.currency }</Text>
<Text variant="secondary" ml={ 1 }>(${ tx.amount.value_usd.toFixed(2) })</Text> <Text variant="secondary" ml={ 1 }>(${ tx.amount.value_usd.toFixed(2) })</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Transaction fee" title="Transaction fee"
hint="Total transaction fee." hint="Total transaction fee."
> >
<Text>{ tx.fee.value } Ether</Text> <Text>{ tx.fee.value } { selectedNetwork?.currency }</Text>
<Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text> <Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Gas price" title="Gas price"
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage." hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
> >
<Text mr={ 1 }>{ tx.gas_price.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text> <Text mr={ 1 }>{ tx.gas_price.toLocaleString('en', { minimumFractionDigits: 18 }) } { selectedNetwork?.currency }</Text>
<Text variant="secondary">({ (tx.gas_price * Math.pow(10, 18)).toFixed(0) } Gwei)</Text> <Text variant="secondary">({ (tx.gas_price * Math.pow(10, 18)).toFixed(0) } Gwei)</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
...@@ -171,10 +174,10 @@ const TxDetails = () => { ...@@ -171,10 +174,10 @@ const TxDetails = () => {
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Burnt fees" title="Burnt fees"
hint="Amount of ETH burned for this transaction. Equals Block Base Fee per Gas * Gas Used." hint={ `Amount of ${ selectedNetwork?.currency } burned for this transaction. Equals Block Base Fee per Gas * Gas Used.` }
> >
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
<Text ml={ 1 } mr={ 1 }>{ tx.burnt_fees.value.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text> <Text ml={ 1 } mr={ 1 }>{ tx.burnt_fees.value.toLocaleString('en', { minimumFractionDigits: 18 }) } { selectedNetwork?.currency }</Text>
<Text variant="secondary">(${ tx.burnt_fees.value_usd.toFixed(2) })</Text> <Text variant="secondary">(${ tx.burnt_fees.value_usd.toFixed(2) })</Text>
</DetailsInfoItem> </DetailsInfoItem>
<GridItem colSpan={{ base: undefined, lg: 2 }}> <GridItem colSpan={{ base: undefined, lg: 2 }}>
......
...@@ -3,7 +3,6 @@ import React from 'react'; ...@@ -3,7 +3,6 @@ import React from 'react';
import type { TxInternalsType } from 'types/api/tx'; import type { TxInternalsType } from 'types/api/tx';
import useIsMobile from 'lib/hooks/useIsMobile';
import FilterButton from 'ui/shared/FilterButton'; import FilterButton from 'ui/shared/FilterButton';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
...@@ -15,14 +14,12 @@ interface Props { ...@@ -15,14 +14,12 @@ interface Props {
const TxInternalsFilter = ({ onFilterChange, defaultFilters, appliedFiltersNum }: Props) => { const TxInternalsFilter = ({ onFilterChange, defaultFilters, appliedFiltersNum }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
const isMobile = useIsMobile();
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy> <Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger> <PopoverTrigger>
<FilterButton <FilterButton
isActive={ isOpen || Number(appliedFiltersNum) > 0 } isActive={ isOpen || Number(appliedFiltersNum) > 0 }
isCollapsed={ isMobile }
onClick={ onToggle } onClick={ onToggle }
appliedFiltersNum={ appliedFiltersNum } appliedFiltersNum={ appliedFiltersNum }
/> />
......
...@@ -2,12 +2,15 @@ import { Box } from '@chakra-ui/react'; ...@@ -2,12 +2,15 @@ import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { data as txData } from 'data/txInternal'; import type { data as txData } from 'data/txInternal';
import useNetwork from 'lib/hooks/useNetwork';
import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem'; import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem';
const TxInternalsList = ({ data }: { data: typeof txData}) => { const TxInternalsList = ({ data }: { data: typeof txData}) => {
const selectedNetwork = useNetwork();
return ( return (
<Box mt={ 6 }> <Box mt={ 6 }>
{ data.map((item) => <TxInternalsListItem key={ item.id } { ...item }/>) } { data.map((item) => <TxInternalsListItem key={ item.id } { ...item } currency={ selectedNetwork?.currency }/>) }
</Box> </Box>
); );
}; };
......
...@@ -12,9 +12,9 @@ import AddressIcon from 'ui/shared/address/AddressIcon'; ...@@ -12,9 +12,9 @@ import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
type Props = ArrayElement<typeof data>; type Props = ArrayElement<typeof data> & { currency?: string };
const TxInternalsListItem = ({ type, status, from, to, value, gasLimit }: Props) => { const TxInternalsListItem = ({ type, status, from, to, value, gasLimit, currency }: Props) => {
return ( return (
<AccountListItemMobile rowGap={ 3 }> <AccountListItemMobile rowGap={ 3 }>
<Flex> <Flex>
...@@ -33,7 +33,7 @@ const TxInternalsListItem = ({ type, status, from, to, value, gasLimit }: Props) ...@@ -33,7 +33,7 @@ const TxInternalsListItem = ({ type, status, from, to, value, gasLimit }: Props)
</Address> </Address>
</Box> </Box>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Value xDAI</Text> <Text fontSize="sm" fontWeight={ 500 }>Value { currency }</Text>
<Text fontSize="sm" variant="secondary">{ value }</Text> <Text fontSize="sm" variant="secondary">{ value }</Text>
</HStack> </HStack>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
......
...@@ -2,9 +2,12 @@ import { Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react'; ...@@ -2,9 +2,12 @@ import { Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { data as txData } from 'data/txInternal'; import type { data as txData } from 'data/txInternal';
import useNetwork from 'lib/hooks/useNetwork';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem'; import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
const TxInternalsTable = ({ data }: { data: typeof txData}) => { const TxInternalsTable = ({ data }: { data: typeof txData}) => {
const selectedNetwork = useNetwork();
return ( return (
<TableContainer width="100%" mt={ 6 }> <TableContainer width="100%" mt={ 6 }>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
...@@ -14,7 +17,7 @@ const TxInternalsTable = ({ data }: { data: typeof txData}) => { ...@@ -14,7 +17,7 @@ const TxInternalsTable = ({ data }: { data: typeof txData}) => {
<Th width="20%">From</Th> <Th width="20%">From</Th>
<Th width="24px" px={ 0 }/> <Th width="24px" px={ 0 }/>
<Th width="20%">To</Th> <Th width="20%">To</Th>
<Th width="16%" isNumeric>Value</Th> <Th width="16%" isNumeric>Value { selectedNetwork?.currency }</Th>
<Th width="16%" isNumeric>Gas limit</Th> <Th width="16%" isNumeric>Gas limit</Th>
</Tr> </Tr>
</Thead> </Thead>
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type ArrayElement from 'types/utils/ArrayElement';
import type { data } from 'data/txState'; import type { data } from 'data/txState';
import useNetwork from 'lib/hooks/useNetwork';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import AccountListItemMobile from 'ui/shared/AccountListItemMobile'; import AccountListItemMobile from 'ui/shared/AccountListItemMobile';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
...@@ -16,6 +17,8 @@ import TxStateStorageItem from './TxStateStorageItem'; ...@@ -16,6 +17,8 @@ import TxStateStorageItem from './TxStateStorageItem';
type Props = ArrayElement<typeof data>; type Props = ArrayElement<typeof data>;
const TxStateListItem = ({ storage, address, miner, after, before, diff }: Props) => { const TxStateListItem = ({ storage, address, miner, after, before, diff }: Props) => {
const selectedNetwork = useNetwork();
const hasStorageData = Boolean(storage?.length); const hasStorageData = Boolean(storage?.length);
return ( return (
...@@ -59,20 +62,20 @@ const TxStateListItem = ({ storage, address, miner, after, before, diff }: Props ...@@ -59,20 +62,20 @@ const TxStateListItem = ({ storage, address, miner, after, before, diff }: Props
<Flex rowGap={ 2 } flexDir="column" fontSize="sm"> <Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>Before</Text> <Text fontWeight={ 600 }>Before</Text>
<Flex> <Flex>
<Text>{ before.balance } ETH</Text> <Text>{ before.balance } { selectedNetwork?.currency }</Text>
<TextSeparator/> <TextSeparator/>
{ typeof before.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ before.nonce }</Text> } { typeof before.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ before.nonce }</Text> }
</Flex> </Flex>
</Flex> </Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm"> <Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>After</Text> <Text fontWeight={ 600 }>After</Text>
<Text>{ after.balance } ETH</Text> <Text>{ after.balance } { selectedNetwork?.currency }</Text>
{ typeof after.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ after.nonce }</Text> } { typeof after.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ after.nonce }</Text> }
</Flex> </Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm"> <Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>State difference</Text> <Text fontWeight={ 600 }>State difference</Text>
<Stat> <Stat>
{ diff } ETH { diff } { selectedNetwork?.currency }
<StatArrow ml={ 2 } type={ Number(diff) > 0 ? 'increase' : 'decrease' }/> <StatArrow ml={ 2 } type={ Number(diff) > 0 ? 'increase' : 'decrease' }/>
</Stat> </Stat>
</Flex> </Flex>
......
...@@ -9,11 +9,12 @@ import { ...@@ -9,11 +9,12 @@ import {
import React from 'react'; import React from 'react';
import { data } from 'data/txState'; import { data } from 'data/txState';
import useNetwork from 'lib/hooks/useNetwork';
import TxStateTableItem from 'ui/tx/state/TxStateTableItem'; import TxStateTableItem from 'ui/tx/state/TxStateTableItem';
const CURRENCY = 'ETH';
const TxStateTable = () => { const TxStateTable = () => {
const selectedNetwork = useNetwork();
return ( return (
<TableContainer width="100%" mt={ 6 }> <TableContainer width="100%" mt={ 6 }>
<Table variant="simple" minWidth="950px" size="sm"> <Table variant="simple" minWidth="950px" size="sm">
...@@ -22,9 +23,9 @@ const TxStateTable = () => { ...@@ -22,9 +23,9 @@ const TxStateTable = () => {
<Th width="92px">Storage</Th> <Th width="92px">Storage</Th>
<Th width="146px">Address</Th> <Th width="146px">Address</Th>
<Th width="120px">Miner</Th> <Th width="120px">Miner</Th>
<Th width="33%" isNumeric>{ `After ${ CURRENCY }` }</Th> <Th width="33%" isNumeric>{ `After ${ selectedNetwork?.currency }` }</Th>
<Th width="33%" isNumeric>{ `Before ${ CURRENCY }` }</Th> <Th width="33%" isNumeric>{ `Before ${ selectedNetwork?.currency }` }</Th>
<Th width="33%" isNumeric>{ `State difference ${ CURRENCY }` }</Th> <Th width="33%" isNumeric>{ `State difference ${ selectedNetwork?.currency }` }</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -4,11 +4,14 @@ import React from 'react'; ...@@ -4,11 +4,14 @@ import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type ArrayElement from 'types/utils/ArrayElement';
import type { txs } from 'data/txs'; import type { txs } from 'data/txs';
import useNetwork from 'lib/hooks/useNetwork';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization'; import Utilization from 'ui/shared/Utilization';
const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => {
const selectedNetwork = useNetwork();
const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200'); const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
const sectionProps = { const sectionProps = {
borderBottom: '1px solid', borderBottom: '1px solid',
...@@ -30,7 +33,7 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { ...@@ -30,7 +33,7 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => {
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Transaction fee</Text> <Text { ...sectionTitleProps }>Transaction fee</Text>
<Flex> <Flex>
<Text>{ tx.fee.value } Ether</Text> <Text>{ tx.fee.value } { selectedNetwork?.currency }</Text>
<Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text> <Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text>
</Flex> </Flex>
</Box> </Box>
......
import { Box, HStack } from '@chakra-ui/react'; import { Box, HStack, Show } from '@chakra-ui/react';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import { txs } from 'data/txs'; import { txs } from 'data/txs';
import useIsMobile from 'lib/hooks/useIsMobile';
import FilterButton from 'ui/shared/FilterButton'; import FilterButton from 'ui/shared/FilterButton';
import FilterInput from 'ui/shared/FilterInput'; import FilterInput from 'ui/shared/FilterInput';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
...@@ -19,8 +18,6 @@ type Props = { ...@@ -19,8 +18,6 @@ type Props = {
} }
const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => { const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => {
const isMobile = useIsMobile();
const [ sorting, setSorting ] = useState<Sort>(); const [ sorting, setSorting ] = useState<Sort>();
const [ sortedTxs, setSortedTxs ] = useState(txs); const [ sortedTxs, setSortedTxs ] = useState(txs);
...@@ -76,7 +73,6 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -76,7 +73,6 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
{ /* TODO */ } { /* TODO */ }
<FilterButton <FilterButton
isActive={ false } isActive={ false }
isCollapsed={ isMobile }
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={ () => {} } onClick={ () => {} }
appliedFiltersNum={ 0 } appliedFiltersNum={ 0 }
...@@ -86,6 +82,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -86,6 +82,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
handleSort={ () => {} } handleSort={ () => {} }
isSortActive={ Boolean(sorting) } isSortActive={ Boolean(sorting) }
display={{ base: 'block', lg: 'none' }}
/> />
) } ) }
<FilterInput <FilterInput
...@@ -96,11 +93,10 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -96,11 +93,10 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
placeholder="Search by addresses, hash, method..." placeholder="Search by addresses, hash, method..."
/> />
</HStack> </HStack>
{ isMobile ? <Show below="lg">{ sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) }</Show>
sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) : <Show above="lg"><TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/></Show>
<TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/> }
<Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}> <Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}>
<Pagination currentPage={ 1 } isMobile={ isMobile }/> <Pagination currentPage={ 1 }/>
</Box> </Box>
</> </>
); );
......
...@@ -18,6 +18,7 @@ import type { txs } from 'data/txs'; ...@@ -18,6 +18,7 @@ import type { txs } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useNetwork from 'lib/hooks/useNetwork';
import useLink from 'lib/link/useLink'; import useLink from 'lib/link/useLink';
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';
...@@ -29,6 +30,7 @@ import TxType from 'ui/txs/TxType'; ...@@ -29,6 +30,7 @@ import TxType from 'ui/txs/TxType';
const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const selectedNetwork = useNetwork();
const iconColor = useColorModeValue('blue.600', 'blue.300'); const iconColor = useColorModeValue('blue.600', 'blue.300');
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200'); const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
...@@ -106,11 +108,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -106,11 +108,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Address> </Address>
</Flex> </Flex>
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Value xDAI </Text> <Text as="span">Value { selectedNetwork?.currency } </Text>
<Text as="span" variant="secondary">{ tx.amount.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ tx.amount.value.toFixed(8) }</Text>
</Box> </Box>
<Box mt={ 2 } mb={ 3 }> <Box mt={ 2 } mb={ 3 }>
<Text as="span">Fee xDAI </Text> <Text as="span">Fee { selectedNetwork?.currency } </Text>
<Text as="span" variant="secondary">{ tx.fee.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ tx.fee.value.toFixed(8) }</Text>
</Box> </Box>
</Box> </Box>
......
...@@ -5,11 +5,10 @@ import type { Sort } from 'types/client/txs-sort'; ...@@ -5,11 +5,10 @@ import type { Sort } from 'types/client/txs-sort';
import type { txs as data } from 'data/txs'; import type { txs as data } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useNetwork from 'lib/hooks/useNetwork';
import TxsTableItem from './TxsTableItem'; import TxsTableItem from './TxsTableItem';
const CURRENCY = 'xDAI';
type Props = { type Props = {
txs: typeof data; txs: typeof data;
sort: (field: 'val' | 'fee') => () => void; sort: (field: 'val' | 'fee') => () => void;
...@@ -17,6 +16,8 @@ type Props = { ...@@ -17,6 +16,8 @@ type Props = {
} }
const TxsTable = ({ txs, sort, sorting }: Props) => { const TxsTable = ({ txs, sort, sorting }: Props) => {
const selectedNetwork = useNetwork();
return ( return (
<TableContainer width="100%" mt={ 6 }> <TableContainer width="100%" mt={ 6 }>
<Table variant="simple" minWidth="810px" size="xs"> <Table variant="simple" minWidth="810px" size="xs">
...@@ -34,14 +35,14 @@ const TxsTable = ({ txs, sort, sorting }: Props) => { ...@@ -34,14 +35,14 @@ const TxsTable = ({ txs, sort, sorting }: Props) => {
<Link onClick={ sort('val') } display="flex" justifyContent="end"> <Link onClick={ sort('val') } display="flex" justifyContent="end">
{ sorting === 'val-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> } { sorting === 'val-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'val-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> } { sorting === 'val-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Value ${ CURRENCY }` } { `Value ${ selectedNetwork?.currency }` }
</Link> </Link>
</Th> </Th>
<Th width="18%" isNumeric pr={ 5 }> <Th width="18%" isNumeric pr={ 5 }>
<Link onClick={ sort('fee') } display="flex" justifyContent="end"> <Link onClick={ sort('fee') } display="flex" justifyContent="end">
{ sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> } { sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> } { sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Fee ${ CURRENCY }` } { `Fee ${ selectedNetwork?.currency }` }
</Link> </Link>
</Th> </Th>
</Tr> </Tr>
......
...@@ -13,8 +13,8 @@ import { ...@@ -13,8 +13,8 @@ import {
PopoverContent, PopoverContent,
PopoverBody, PopoverBody,
Portal, Portal,
useBreakpointValue,
useColorModeValue, useColorModeValue,
Show,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
...@@ -37,8 +37,6 @@ import TxType from './TxType'; ...@@ -37,8 +37,6 @@ import TxType from './TxType';
const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
const link = useLink(); const link = useLink();
const isLargeScreen = useBreakpointValue({ base: false, xl: true });
const addressFrom = ( const addressFrom = (
<Address> <Address>
<Tooltip label={ tx.address_from.type }> <Tooltip label={ tx.address_from.type }>
...@@ -108,19 +106,18 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -108,19 +106,18 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
<Td> <Td>
<Link href={ link('block_index', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link> <Link href={ link('block_index', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
</Td> </Td>
{ isLargeScreen ? ( <Show above="xl">
<> <Td>
<Td> { addressFrom }
{ addressFrom } </Td>
</Td> <Td>
<Td> <Icon as={ rightArrowIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/> </Td>
</Td> <Td>
<Td> { addressTo }
{ addressTo } </Td>
</Td> </Show>
</> <Show below="xl">
) : (
<Td colSpan={ 3 }> <Td colSpan={ 3 }>
<Box> <Box>
{ addressFrom } { addressFrom }
...@@ -135,7 +132,7 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -135,7 +132,7 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
{ addressTo } { addressTo }
</Box> </Box>
</Td> </Td>
) } </Show>
<Td isNumeric> <Td isNumeric>
{ tx.amount.value.toFixed(8) } { tx.amount.value.toFixed(8) }
</Td> </Td>
......
...@@ -3,17 +3,23 @@ import React, { useCallback } from 'react'; ...@@ -3,17 +3,23 @@ import React, { useCallback } from 'react';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import type { Path, ControllerRenderProps, FieldValues, Control } from 'react-hook-form'; import type { Path, ControllerRenderProps, FieldValues, Control } from 'react-hook-form';
import useNetwork from 'lib/hooks/useNetwork';
import CheckboxInput from 'ui/shared/CheckboxInput'; import CheckboxInput from 'ui/shared/CheckboxInput';
// does it depend on the network? // does it depend on the network?
const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const; const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const;
const NOTIFICATIONS_NAMES = [ 'xDAI', 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ];
type Props<Inputs extends FieldValues> = { type Props<Inputs extends FieldValues> = {
control: Control<Inputs>; control: Control<Inputs>;
} }
export default function AddressFormNotifications<Inputs extends FieldValues, Checkboxes extends Path<Inputs>>({ control }: Props<Inputs>) { export default function AddressFormNotifications<Inputs extends FieldValues, Checkboxes extends Path<Inputs>>({ control }: Props<Inputs>) {
const selectedNetwork = useNetwork();
const NOTIFICATIONS_NAMES = React.useMemo(() => {
return [ selectedNetwork?.currency, 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ];
}, [ selectedNetwork?.currency ]);
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const renderCheckbox = useCallback((text: string) => ({ field }: {field: ControllerRenderProps<Inputs, Checkboxes>}) => ( const renderCheckbox = useCallback((text: string) => ({ field }: {field: ControllerRenderProps<Inputs, Checkboxes>}) => (
<CheckboxInput<Inputs, Checkboxes> text={ text } field={ field }/> <CheckboxInput<Inputs, Checkboxes> text={ text } field={ field }/>
......
import { HStack, VStack, Image, Text, Icon, useColorModeValue } from '@chakra-ui/react'; import { HStack, VStack, Text, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TWatchlistItem } from 'types/client/account'; import type { TWatchlistItem } from 'types/client/account';
import TokensIcon from 'icons/tokens.svg'; import TokensIcon from 'icons/tokens.svg';
// import WalletIcon from 'icons/wallet.svg'; // import WalletIcon from 'icons/wallet.svg';
import useNetwork from 'lib/hooks/useNetwork';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import AddressSnippet from 'ui/shared/AddressSnippet'; import AddressSnippet from 'ui/shared/AddressSnippet';
import TokenLogo from 'ui/shared/TokenLogo';
// now this component works only for xDAI
// for other networks later we will use config or smth
const DECIMALS = 18; const DECIMALS = 18;
const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => { const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
const mainTextColor = useColorModeValue('gray.700', 'gray.50'); const mainTextColor = useColorModeValue('gray.700', 'gray.50');
const selectedNetwork = useNetwork();
const nativeBalance = ((item.address_balance || 0) / 10 ** DECIMALS).toFixed(1); const nativeBalance = ((item.address_balance || 0) / 10 ** DECIMALS).toFixed(1);
const nativeBalanceUSD = item.exchange_rate ? `$${ Number(nativeBalance) * item.exchange_rate } USD` : 'N/A'; const nativeBalanceUSD = item.exchange_rate ? `$${ Number(nativeBalance) * item.exchange_rate } USD` : 'N/A';
...@@ -23,8 +24,8 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => { ...@@ -23,8 +24,8 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
<VStack spacing={ 2 } align="stretch" overflow="hidden" fontWeight={ 500 } color="gray.700"> <VStack spacing={ 2 } align="stretch" overflow="hidden" fontWeight={ 500 } color="gray.700">
<AddressSnippet address={ item.address_hash }/> <AddressSnippet address={ item.address_hash }/>
<HStack spacing={ 0 } fontSize="sm" h={ 6 } pl={ infoItemsPaddingLeft }> <HStack spacing={ 0 } fontSize="sm" h={ 6 } pl={ infoItemsPaddingLeft }>
<Image src="/xdai.png" srcSet="/xdai@2x.png 2x" alt="chain-logo" marginRight="10px" w="16px" h="16px"/> { selectedNetwork && <TokenLogo hash={ selectedNetwork.nativeTokenAddress } name={ selectedNetwork.name } boxSize={ 4 } mr="10px"/> }
<Text color={ mainTextColor }>{ `xDAI balance:${ nbsp }` + nativeBalance }</Text> <Text color={ mainTextColor }>{ `${ selectedNetwork?.currency } balance:${ nbsp }` + nativeBalance }</Text>
<Text variant="secondary">{ `${ nbsp }(${ nativeBalanceUSD })` }</Text> <Text variant="secondary">{ `${ nbsp }(${ nativeBalanceUSD })` }</Text>
</HStack> </HStack>
{ item.tokens_count && ( { item.tokens_count && (
......
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