Commit 8a487831 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into fix-ci-cd

parents 85446f14 33b1ad24
...@@ -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},{"name":"Optimism on Gnosis Chain","shortName":"OoG","type":"xdai","subType":"optimism","group":"mainnets","icon":"https://www.fillmurray.com/60/60"},{"name":"Arbitrum on xDai","type":"xdai","subType":"aox","group":"mainnets"},{"name":"Ethereum","shortName":"ETH","type":"eth","subType":"mainnet","group":"mainnets"},{"name":"Ethereum Classic","shortName":"ETC","type":"etc","subType":"mainnet","group":"mainnets"},{"name":"POA","shortName":"POA","type":"poa","subType":"core","group":"mainnets","isAccountSupported":true},{"name":"RSK","shortName":"RBTC","type":"rsk","subType":"mainnet","group":"mainnets"},{"name":"Gnosis Chain Testnet","type":"xdai","subType":"testnet","group":"testnets"},{"name":"POA Sokol","shortName":"POA","type":"poa","subType":"sokol","group":"testnets"},{"name":"ARTIS Σ1","type":"artis","subType":"sigma1","group":"other"},{"name":"LUKSO L14","shortName":"POA","type":"lukso","subType":"l14","group":"other"}] 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}]
\ No newline at end of file
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
# lint js/ts files
echo 🧿 Running file linter...
npx lint-staged npx lint-staged
# format svg
echo 🧿 Running svg formatter...
for file in `git diff --diff-filter=ACMRT --cached --name-only | grep ".svg\$"`
do
echo "Formatting $file"
./node_modules/.bin/svgo -q $file
git add $file
done
echo ✅ All pre-commit jobs are done
\ No newline at end of file
...@@ -32,13 +32,14 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -32,13 +32,14 @@ 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},{"name":"Optimism on Gnosis Chain","type":"xdai","subType":"optimism","group":"mainnets"},{"name":"Arbitrum on xDai","type":"xdai","subType":"aox","group":"mainnets"},{"name":"Ethereum","type":"eth","subType":"mainnet","group":"mainnets"},{"name":"Ethereum Classic","type":"etc","subType":"mainnet","group":"mainnets"},{"name":"POA","type":"poa","subType":"core","group":"mainnets"},{"name":"RSK","type":"rsk","subType":"mainnet","group":"mainnets"},{"name":"Gnosis Chain Testnet","type":"xdai","subType":"testnet","group":"testnets","isAccountSupported":true},{"name":"POA Sokol","type":"poa","subType":"sokol","group":"testnets"},{"name":"ARTIS Σ1","type":"artis","subType":"sigma1","group":"other"},{"name":"LUKSO L14","type":"lukso","subType":"l14","group":"other"}]` | | 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}]` |
### 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` |
| 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"` |
......
import type { AppItemOverview } from 'types/client/apps';
import { APP_CATEGORIES } from 'ui/apps/constants';
export const TEMPORARY_DEMO_APPS: Array<AppItemOverview> = [
{
author: 'xDaichain',
id: 'easy-staking',
title: 'Easy Staking',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'Accessible DeFi staking platform for STAKE holders on Ethereum',
site: 'https://easy-staking.xdaichain.com/',
// eslint-disable-next-line max-len
description: 'Easy Staking allows users to place STAKE into a contract and receive STAKE emissions on Ethereum. It provides an accessible staking mechanism for users and increases STAKE utility and DeFi composability. EasyStaking also:\n\n- Incentivizes liquidity providers on decentralized exchanges through unique reward mechanisms\n- Creates staking opportunities via hardware wallets and other Ethereum applications\n- Provides staking opportunities with no minimum STAKE requirements to participate\n- Limits total circulating supply',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'curve',
title: 'Curve',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
// eslint-disable-next-line max-len
shortDescription: 'Curve is an exchange liquidity pool on Ethereum designed for: extremely efficient stablecoin trading, low risk, supplemental fee income for liquidity providers, without an opportunity cost.',
site: 'https://xdai.curve.fi/',
// eslint-disable-next-line max-len
description: 'Curve is an exchange liquidity pool on Ethereum designed for: extremely efficient stablecoin trading, low risk, supplemental fee income for liquidity providers, without an opportunity cost.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'honwyswap',
title: 'HonwySwap',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
// eslint-disable-next-line max-len
shortDescription: 'Honeyswap is a decentralized exchange built on the Gnosis Chain, this enables users to experience fast and secure transactions with incredibly low fees. Multiple tokens are available with which you can swap and add liquidity.',
site: 'https://honeyswap.org/',
// eslint-disable-next-line max-len
description: 'Honeyswap is a decentralized exchange built on the Gnosis Chain, this enables users to experience fast and secure transactions with incredibly low fees. Multiple tokens are available with which you can swap and add liquidity.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'sushi',
title: 'Sushi',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'Swap, yield, lend, borrow, leverage, limit, launch all on one community-driven ecosystem',
site: 'https://app.sushi.com/',
description: 'Swap, yield, lend, borrow, leverage, limit, launch all on one community-driven ecosystem',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'bao-finance',
title: 'Bao Finance',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'Yield Farming for Synthetic Assets from LP tokens',
site: 'https://farms.baoswap.xyz/',
description: 'Yield Farming for Synthetic Assets from LP tokens',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'component',
title: 'Component',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
// eslint-disable-next-line max-len
shortDescription: 'During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.',
site: 'https://xdai.component.finance',
// eslint-disable-next-line max-len
description: 'During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'pooltogether',
title: 'PoolTogether',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'View, deposit and withdraw for all V3 Pools',
site: 'https://app.pooltogether.com/',
description: 'View, deposit and withdraw for all V3 Pools',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'swapr',
title: 'Swapr',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'A governance-enabled automated market maker with adjustable fees.',
site: 'https://swapr.eth.limo',
description: 'A governance-enabled automated market maker with adjustable fees.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'levinswap',
title: 'Levinswap',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
// eslint-disable-next-line max-len
shortDescription: 'AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.',
site: 'https://app.levinswap.org/',
description: 'AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'omen',
title: 'Omen',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'Decentralized prediction markets on Ethereum',
site: 'https://xdai.omen.eth.link/',
description: 'Decentralized prediction markets on Ethereum',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'nifty-ink',
title: 'Nifty Ink',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum',
site: 'https://nifty.ink/explore',
description: 'NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'treasure-chess',
title: 'Treasure Chess',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'Every chess game is one-of-a-kind. Make yours a collectible.',
site: 'https://treasure.chess.com/',
description: 'Every chess game is one-of-a-kind. Make yours a collectible.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'unique-one',
title: 'Unique.One',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
// eslint-disable-next-line max-len
shortDescription: 'A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.',
site: 'https://www.unique.one/',
// eslint-disable-next-line max-len
description: 'A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'cold-truth-culture',
title: 'Cold Truth Culture',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'A Community That Empowers NFT Artists',
site: 'https://www.coldtruthculture.io/',
description: 'A Community That Empowers NFT Artists',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'xdai-bridge',
title: 'xDai Bridge',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'Token bridge between the Gnosis Chain and the Ethereum network',
site: 'https://bridge.gnosischain.com/',
description: 'Token bridge between the Gnosis Chain and the Ethereum network',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'omni-bridge',
title: 'OmniBridge',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.',
site: 'https://omni.gnosischain.com/bridge',
description: 'The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'gnosis-safe',
title: 'Gnosis Safe',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[0], APP_CATEGORIES[1] ],
shortDescription: 'Gnosis Safe is the most trusted platform to manage digital assets on Ethereum',
site: 'https://gnosis-safe.io/',
description: 'Gnosis Safe is the most trusted platform to manage digital assets on Ethereum',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'multisender',
title: 'Multisender',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
// eslint-disable-next-line max-len
shortDescription: 'Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.',
site: 'https://multisender.app/',
// eslint-disable-next-line max-len
description: 'Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'disperse',
title: 'Disperse',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
shortDescription: 'Distribute ether or tokens to multiple addresses',
site: 'https://disperse.app/',
description: 'Distribute ether or tokens to multiple addresses',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
{
author: 'xDaichain',
id: 'symmetric',
title: 'Symmetric',
logo: 'https://www.fillmurray.com/144/144',
categories: [ APP_CATEGORIES[1], APP_CATEGORIES[2] ],
// eslint-disable-next-line max-len
shortDescription: 'DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.',
site: 'https://symmetric.finance/',
// eslint-disable-next-line max-len
description: 'DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.',
url: 'https://blockscout-allowance-mainnet-stage.vercel.app/',
twitter: 'https://twitter.com/EasyStaking',
telegram: 'https://t.me/easystaking',
github: 'https://github.com/mikhin',
},
];
<svg viewBox="0 0 240 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M82.667 4H56a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4ZM56 0a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V8a8 8 0 0 0-8-8H56Zm122.667 4H152a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4ZM152 0a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V8a8 8 0 0 0-8-8H152ZM56 52h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4V56a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H56a8 8 0 0 1-8-8V56Zm34.667 44H56a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V104a4 4 0 0 0-4-4ZM56 96a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V104a8 8 0 0 0-8-8H56Zm0 52h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4V152a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H56a8 8 0 0 1-8-8V152ZM178.667 52H152a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V56a4 4 0 0 0-4-4ZM152 48a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V56a8 8 0 0 0-8-8H152Zm0 52h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H152a4 4 0 0 1-4-4V104a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H152a8 8 0 0 1-8-8V104Zm34.667 44H152a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V152a4 4 0 0 0-4-4ZM152 144a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V152a8 8 0 0 0-8-8H152ZM8 28h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V32Zm104-4h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H104a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H104a8 8 0 0 1-8-8V32Zm130.667-4H200a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V32a4 4 0 0 0-4-4ZM200 24a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V32a8 8 0 0 0-8-8H200Zm-96 52h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H104a4 4 0 0 1-4-4V80a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H104a8 8 0 0 1-8-8V80Zm34.667 44H104a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V128a4 4 0 0 0-4-4ZM104 120a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V128a8 8 0 0 0-8-8H104Zm96-44h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H200a4 4 0 0 1-4-4V80a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H200a8 8 0 0 1-8-8V80Zm34.667 44H200a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V128a4 4 0 0 0-4-4ZM200 120a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V128a8 8 0 0 0-8-8H200ZM8 76h26.667a4 4 0 0 1 4 4v26.667a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V80a4 4 0 0 1 4-4Zm-8 4a8 8 0 0 1 8-8h26.667a8 8 0 0 1 8 8v26.667a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V80Zm34.667 44H8a4 4 0 0 0-4 4v26.667a4 4 0 0 0 4 4h26.667a4 4 0 0 0 4-4V128a4 4 0 0 0-4-4ZM8 120a8 8 0 0 0-8 8v26.667a8 8 0 0 0 8 8h26.667a8 8 0 0 0 8-8V128a8 8 0 0 0-8-8H8Z" fill="url(#empty_search_result_svg__a)"/>
<path d="m135.337 94.376 11.182 11.242-3.694 3.715-11.18-11.245a23.31 23.31 0 0 1-14.666 5.17c-12.971 0-23.498-10.586-23.498-23.629C93.48 66.586 104.008 56 116.979 56c12.971 0 23.499 10.586 23.499 23.629a23.614 23.614 0 0 1-5.141 14.747Zm-5.238-1.948a18.372 18.372 0 0 0 5.157-12.799c0-10.155-8.18-18.378-18.277-18.378-10.099 0-18.276 8.223-18.276 18.378 0 10.153 8.177 18.378 18.276 18.378a18.166 18.166 0 0 0 12.729-5.185l.391-.394Z" fill="#A0AEC0"/>
<defs>
<radialGradient id="empty_search_result_svg__a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="rotate(103.716 25.035 85.404) scale(58.3301 137.703)">
<stop offset=".081" stop-color="#CBD5E0" stop-opacity="0"/>
<stop offset=".563" stop-color="#CBD5E0" stop-opacity=".54"/>
<stop offset="1" stop-color="#CBD5E0" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>
<svg viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.76 17.333a.604.604 0 0 1-.294-.075l.293.075Zm.004 0a.625.625 0 0 0 .477-.234.671.671 0 0 0 .14-.538l-.853-5.21 3.615-3.689a.69.69 0 0 0 .16-.677.663.663 0 0 0-.194-.301.617.617 0 0 0-.316-.149l-4.884-.743a.208.208 0 0 1-.157-.117l-2.186-4.64a.65.65 0 0 0-.233-.269.61.61 0 0 0-.666 0 .65.65 0 0 0-.232.269l-2.186 4.64a.208.208 0 0 1-.158.117l-4.884.743a.618.618 0 0 0-.316.149.663.663 0 0 0-.193.3.69.69 0 0 0 .16.678l3.54 3.614a.208.208 0 0 1 .058.18l-.837 5.105a.69.69 0 0 0 .038.36.657.657 0 0 0 .213.286.613.613 0 0 0 .663.05L8.9 14.854a.208.208 0 0 1 .2 0l4.366 2.405m-7.795-2.915c-.028.172.154.3.307.216L8.9 12.95a.208.208 0 0 1 .2 0l2.923 1.61a.208.208 0 0 0 .306-.216l-.566-3.452a.208.208 0 0 1 .057-.18l2.486-2.536a.208.208 0 0 0-.118-.351l-3.408-.519a.208.208 0 0 1-.157-.117L9.189 4.145a.208.208 0 0 0-.377 0L7.378 7.19a.208.208 0 0 1-.158.117l-3.408.519a.208.208 0 0 0-.117.351l2.485 2.537a.208.208 0 0 1 .057.18l-.566 3.45Zm8.093 2.99h-.003.003Z" fill="#4A5568"/>
</svg>
...@@ -18,3 +18,4 @@ export const disk = String.fromCharCode(8226); // диск • ...@@ -18,3 +18,4 @@ export const disk = String.fromCharCode(8226); // диск •
export const minus = String.fromCharCode(8722); // минус − export const minus = String.fromCharCode(8722); // минус −
export const leftLineArrow = String.fromCharCode(8592); // стрелка ← export const leftLineArrow = String.fromCharCode(8592); // стрелка ←
export const rightLineArrow = String.fromCharCode(8594); // стрелка → export const rightLineArrow = String.fromCharCode(8594); // стрелка →
export const apos = String.fromCharCode(39); // апостроф '
...@@ -60,6 +60,7 @@ export default NETWORKS; ...@@ -60,6 +60,7 @@ export default NETWORKS;
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// isAccountSupported: true, // isAccountSupported: true,
// chainId: 100
// }, // },
// { // {
// name: 'Optimism on Gnosis Chain', // name: 'Optimism on Gnosis Chain',
...@@ -68,12 +69,14 @@ export default NETWORKS; ...@@ -68,12 +69,14 @@ export default NETWORKS;
// subType: 'optimism', // subType: 'optimism',
// group: 'mainnets', // group: 'mainnets',
// icon: 'https://www.fillmurray.com/60/60' // icon: 'https://www.fillmurray.com/60/60'
// chainId: 300
// }, // },
// { // {
// name: 'Arbitrum on xDai', // name: 'Arbitrum on xDai',
// type: 'xdai', // type: 'xdai',
// subType: 'aox', // subType: 'aox',
// group: 'mainnets', // group: 'mainnets',
// chainId: 200
// }, // },
// { // {
// name: 'Ethereum', // name: 'Ethereum',
...@@ -81,6 +84,7 @@ export default NETWORKS; ...@@ -81,6 +84,7 @@ export default NETWORKS;
// type: 'eth', // type: 'eth',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 1
// }, // },
// { // {
// name: 'Ethereum Classic', // name: 'Ethereum Classic',
...@@ -88,6 +92,7 @@ export default NETWORKS; ...@@ -88,6 +92,7 @@ export default NETWORKS;
// type: 'etc', // type: 'etc',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 61
// }, // },
// { // {
// name: 'POA', // name: 'POA',
...@@ -95,6 +100,7 @@ export default NETWORKS; ...@@ -95,6 +100,7 @@ export default NETWORKS;
// type: 'poa', // type: 'poa',
// subType: 'core', // subType: 'core',
// group: 'mainnets', // group: 'mainnets',
// chainId: 99
// }, // },
// { // {
// name: 'RSK', // name: 'RSK',
...@@ -102,6 +108,7 @@ export default NETWORKS; ...@@ -102,6 +108,7 @@ export default NETWORKS;
// type: 'rsk', // type: 'rsk',
// subType: 'mainnet', // subType: 'mainnet',
// group: 'mainnets', // group: 'mainnets',
// chainId: 30
// }, // },
// { // {
// name: 'Gnosis Chain Testnet', // name: 'Gnosis Chain Testnet',
...@@ -116,12 +123,14 @@ export default NETWORKS; ...@@ -116,12 +123,14 @@ export default NETWORKS;
// type: 'poa', // type: 'poa',
// subType: 'sokol', // subType: 'sokol',
// group: 'testnets', // group: 'testnets',
// chainId: 77
// }, // },
// { // {
// name: 'ARTIS Σ1', // name: 'ARTIS Σ1',
// type: 'artis', // type: 'artis',
// subType: 'sigma1', // subType: 'sigma1',
// group: 'other', // group: 'other',
// chainId: 246529
// }, // },
// { // {
// name: 'LUKSO L14', // name: 'LUKSO L14',
...@@ -129,5 +138,6 @@ export default NETWORKS; ...@@ -129,5 +138,6 @@ export default NETWORKS;
// type: 'lukso', // type: 'lukso',
// subType: 'l14', // subType: 'l14',
// group: 'other', // group: 'other',
// chainId: 22
// }, // },
// ]; // ];
import Head from 'next/head';
import React from 'react';
import Apps from 'ui/pages/Apps';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
const AppsPage = () => {
return (
<Page>
<PageHeader text="Apps"/>
<Head><title>Apps</title></Head>
<Apps/>
</Page>
);
};
export default AppsPage;
...@@ -23,6 +23,7 @@ const sizes = { ...@@ -23,6 +23,7 @@ const sizes = {
minH: 6, minH: 6,
minW: 6, minW: 6,
fontSize: 'sm', fontSize: 'sm',
lineHeight: 'sm',
px: 2, px: 2,
py: '2px', py: '2px',
}, },
...@@ -33,7 +34,7 @@ const sizes = { ...@@ -33,7 +34,7 @@ const sizes = {
}; };
const baseStyleContainer = defineStyle({ const baseStyleContainer = defineStyle({
display: 'inline-flex', display: 'inline-block',
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
borderRadius: 'sm', borderRadius: 'sm',
......
const breakpoints = { const breakpoints = {
// maybe we need them in future // maybe we need them in future
// sm: '320px', sm: '414px',
// md: '768px', // md: '768px',
lg: '1000px', lg: '1000px',
xl: '1440px', xl: '1440px',
// these breakpoint are needed just to make others work // these breakpoint are needed just to make others work
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"baseUrl": ".", "baseUrl": ".",
"paths": {
"~/*": ["./*"]
},
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "additional.d.ts", "decs.d.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "additional.d.ts", "decs.d.ts"],
"exclude": ["node_modules"], "exclude": ["node_modules"],
......
export type AppCategory = {
id: string;
name: string;
}
export type AppItemPreview = {
id: string;
title: string;
logo: string;
shortDescription: string;
categories: Array<AppCategory>;
}
export type AppItemOverview = AppItemPreview & {
author: string;
url: string;
description: string;
site?: string;
twitter?: string;
telegram?: string;
github?: string;
}
...@@ -4,6 +4,8 @@ export type NetworkGroup = 'mainnets' | 'testnets' | 'other'; ...@@ -4,6 +4,8 @@ export type NetworkGroup = 'mainnets' | 'testnets' | 'other';
export interface Network { export interface Network {
name: string; name: string;
// https://chainlist.org/
chainId?: number;
shortName?: string; shortName?: string;
// basePath = /<type>/<subType>, e.g. /xdai/mainnet // basePath = /<type>/<subType>, e.g. /xdai/mainnet
type: string; type: string;
......
import { Box, Heading, Image, Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import type { AppItemPreview } from 'types/client/apps';
const AppCard = ({ title, logo, shortDescription, categories }: AppItemPreview) => {
const categoriesLabel = categories.map(c => c.name).join(', ');
return (
<Box
borderRadius={{ base: 'none', sm: 'md' }}
height="100%"
padding={{ base: '16px', sm: '20px' }}
boxShadow={ `0 0 0 1px ${ useColorModeValue('var(--chakra-colors-gray-200)', 'var(--chakra-colors-gray-600)') }` }
>
<Box overflow="hidden" height="100%">
<Box
marginBottom={ 4 }
w={{ base: '64px', sm: '96px' }}
h={{ base: '64px', sm: '96px' }}
>
<Image
src={ logo }
alt={ `${ title } app icon` }
/>
</Box>
<Heading
as="h3"
marginBottom={ 2 }
fontSize={{ base: 'sm', sm: 'lg' }}
fontWeight="semibold"
>
{ title }
</Heading>
<Text
marginBottom={ 2 }
variant="secondary"
fontSize="xs"
>
{ categoriesLabel }
</Text>
<Text
fontSize={{ base: 'xs', sm: 'sm' }}
lineHeight="20px"
noOfLines={ 4 }
>
{ shortDescription }
</Text>
</Box>
</Box>
);
};
export default AppCard;
import { Grid, GridItem, VisuallyHidden, Heading } from '@chakra-ui/react';
import React from 'react';
import type { AppItemPreview } from 'types/client/apps';
import AppCard from 'ui/apps/AppCard';
import EmptySearchResult from 'ui/apps/EmptySearchResult';
type Props = {
apps: Array<AppItemPreview>;
}
const AppList = ({ apps }: Props) => {
return (
<>
<VisuallyHidden>
<Heading as="h2">App list</Heading>
</VisuallyHidden>
{ apps.length > 0 ? (
<Grid
templateColumns={{
base: 'repeat(auto-fill, minmax(160px, 1fr))',
sm: 'repeat(auto-fill, minmax(200px, 1fr))',
lg: 'repeat(auto-fill, minmax(260px, 1fr))',
}}
autoRows="1fr"
gap={{ base: '1px', sm: '24px' }}
>
{ apps.map((app) => (
<GridItem
key={ app.id }
>
<AppCard
id={ app.id }
title={ app.title }
logo={ app.logo }
shortDescription={ app.shortDescription }
categories={ app.categories }
/>
</GridItem>
)) }
</Grid>
) : (
<EmptySearchResult/>
) }
</>
);
};
export default AppList;
import { LinkIcon, StarIcon } from '@chakra-ui/icons';
import {
Box, Button, Heading, Icon, IconButton, Image, Link, List, Modal, ModalBody,
ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Tag, Text,
} from '@chakra-ui/react';
import type { FunctionComponent } from 'react';
import React, { useCallback } from 'react';
import type { AppCategory, AppItemOverview } from 'types/client/apps';
import { TEMPORARY_DEMO_APPS } from 'data/apps';
import ghIcon from 'icons/social/git.svg';
import tgIcon from 'icons/social/telega.svg';
import twIcon from 'icons/social/tweet.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import { nbsp } from 'lib/html-entities';
type Props = {
id: string | null;
onClose: () => void;
}
const AppModal = ({
id,
onClose,
}: Props) => {
const handleFavorite = useCallback(() => {
// TODO: implement
}, []);
if (!id) {
return null;
}
const {
title,
author,
description,
url,
site,
github,
telegram,
twitter,
logo,
categories,
} = TEMPORARY_DEMO_APPS.find(app => app.id === id) as AppItemOverview;
const isFavorite = false;
const socialLinks = [
Boolean(telegram) && {
icon: tgIcon,
url: telegram,
},
Boolean(twitter) && {
icon: twIcon,
url: twitter,
},
Boolean(github) && {
icon: ghIcon,
url: github,
},
].filter(Boolean) as Array<{ icon: FunctionComponent; url: string }>;
return (
<Modal
isOpen={ Boolean(id) }
onClose={ onClose }
size={{ base: 'full', lg: 'md' }}
isCentered
>
<ModalOverlay/>
<ModalContent>
<ModalHeader
display="grid"
gridTemplateColumns={{ base: 'auto 1fr' }}
paddingRight={{ sm: 12 }}
>
<Box
w={{ base: '72px', sm: '144px' }}
h={{ base: '72px', sm: '144px' }}
marginRight={{ base: 6, sm: 8 }}
gridRow={{ base: '1 / 3', sm: '1 / 4' }}
>
<Image
src={ logo }
alt={ `${ title } app icon` }
/>
</Box>
<Heading
as="h2"
gridColumn={ 2 }
fontSize={{ base: '2xl', sm: '3xl' }}
fontWeight="medium"
lineHeight={ 1 }
color="blue.600"
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
{ title }
</Heading>
<Text
variant="secondary"
gridColumn={ 2 }
fontSize="sm"
fontWeight="normal"
lineHeight={ 1 }
>
By{ nbsp }{ author }
</Text>
<Box
gridColumn={{ base: '1 / 3', sm: 2 }}
marginTop={{ base: 6, sm: 0 }}
>
<Box display="flex">
<Button
href={ url }
as="a"
size="sm"
marginRight={ 2 }
width={{ base: '100%', sm: 'auto' }}
>
Launch app
</Button>
<IconButton
aria-label="Mark as favorite"
title="Mark as favorite"
variant="outline"
colorScheme="gray"
w={ 9 }
h={ 8 }
onClick={ handleFavorite }
icon={ isFavorite ?
<Icon as={ StarIcon } w={ 4 } h={ 4 } color="yellow.400"/> :
<Icon as={ starOutlineIcon } w={ 4 } h={ 4 }/> }
/>
</Box>
</Box>
</ModalHeader>
<ModalCloseButton/>
<ModalBody>
<Heading
as="h3"
fontSize="2xl"
marginBottom={ 4 }
>
Overview
</Heading>
<Box marginBottom={ 2 }>
{ categories.map((category: AppCategory) => (
<Tag
colorScheme="blue"
marginRight={ 2 }
marginBottom={ 2 }
key={ category.id }
>
{ category.name }
</Tag>
)) }
</Box>
<Text>{ description }</Text>
</ModalBody>
<ModalFooter
display="flex"
flexDirection={{ base: 'column', sm: 'row' }}
alignItems={{ base: 'flex-start', sm: 'center' }}
>
{ site && (
<Link
isExternal
href={ site }
display="flex"
alignItems="center"
paddingRight={{ sm: 2 }}
marginBottom={{ base: 3, sm: 0 }}
maxW="100%"
overflow="hidden"
>
<Icon
as={ LinkIcon }
display="inline"
verticalAlign="baseline"
boxSize={ 3 }
marginRight={ 2 }
/>
<Text
color="inherit"
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
{ site }
</Text>
</Link>
) }
{ socialLinks.length && (
<List
marginLeft={{ sm: 'auto' }}
display="grid"
gridAutoFlow="column"
columnGap={ 2 }
>
{ socialLinks.map(({ icon, url }) => (
<Link
aria-label={ `Link to ${ url }` }
title={ url }
key={ url }
href={ url }
display="flex"
alignItems="center"
justifyContent="center"
isExternal
w={ 10 }
h={ 10 }
>
<Icon
as={ icon }
w="20px"
h="20px"
display="block"
/>
</Link>
)) }
</List>
) }
</ModalFooter>
</ModalContent>
</Modal>
);
};
export default AppModal;
import { Box, Heading, Icon, Text } from '@chakra-ui/react';
import React from 'react';
import emptyIcon from 'icons/empty_search_result.svg';
import { apos } from 'lib/html-entities';
const EmptySearchResult = () => {
return (
<Box
display="flex"
flexDirection="column"
alignItems="center"
>
<Icon
as={ emptyIcon }
boxSize={ 60 }
display="block"
/>
<Heading
as="h3"
marginBottom={ 2 }
fontSize={{ base: '2xl', sm: '3xl' }}
fontWeight="semibold"
>
No results
</Heading>
<Text
fontSize={{ base: 'sm' }}
variant="secondary"
align="center"
>
Couldn{ apos }t find an app that matches your filter query.
</Text>
</Box>
);
};
export default EmptySearchResult;
import { SearchIcon } from '@chakra-ui/icons';
import { Input, InputGroup, InputLeftElement, useColorModeValue } from '@chakra-ui/react';
import type { ChangeEvent } from 'react';
import React, { useCallback, useState } from 'react';
type Props = {
onChange: (q: string) => void;
}
const FilterInput = ({ onChange }: Props) => {
const [ filterQuery, setFilterQuery ] = useState('');
const handleFilterQueryChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
setFilterQuery(value);
onChange(value);
}, [ onChange ]);
return (
<InputGroup
size="sm"
>
<InputLeftElement
pointerEvents="none"
>
<SearchIcon color={ useColorModeValue('blackAlpha.600', 'whiteAlpha.600') }/>
</InputLeftElement>
<Input
size="sm"
value={ filterQuery }
onChange={ handleFilterQueryChange }
marginBottom={{ base: '4', lg: '6' }}
/>
</InputGroup>
);
};
export default FilterInput;
import type { AppCategory } from 'types/client/apps';
export const APP_CATEGORIES: Array<AppCategory> = [
{
id: 'defi',
name: 'DeFi',
},
{
id: 'exchanges',
name: 'Exchanges',
},
{
id: 'finance',
name: 'Finance',
},
{
id: 'games',
name: 'Games',
},
{
id: 'marketplaces',
name: 'Marketplaces',
},
{
id: 'nft',
name: 'NFT',
},
{
id: 'security',
name: 'Security',
},
{
id: 'social',
name: 'Social',
},
{
id: 'tools',
name: 'Tools',
},
{
id: 'Yield-farming',
name: 'yield-farming',
},
];
import { Link, Icon, Text, HStack, Tooltip } from '@chakra-ui/react'; 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';
...@@ -28,13 +28,13 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => { ...@@ -28,13 +28,13 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => {
})(); })();
return ( return (
<Box as="li" listStyleType="none" w="100%">
<NextLink href={ url } passHref> <NextLink href={ url } passHref>
<Link <Link
as="li"
listStyleType="none"
w={ width } w={ width }
px={ px || (isCollapsed ? '15px' : 3) } px={ px || (isCollapsed ? '15px' : 3) }
py={ 2.5 } py={ 2.5 }
display="flex"
color={ isActive ? colors.text.active : colors.text.default } color={ isActive ? colors.text.active : colors.text.default }
bgColor={ isActive ? colors.bg.active : colors.bg.default } bgColor={ isActive ? colors.bg.active : colors.bg.default }
_hover={{ color: isActive ? colors.text.active : colors.text.hover }} _hover={{ color: isActive ? colors.text.active : colors.text.hover }}
...@@ -58,6 +58,7 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => { ...@@ -58,6 +58,7 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px }: Props) => {
</Tooltip> </Tooltip>
</Link> </Link>
</NextLink> </NextLink>
</Box>
); );
}; };
......
import debounce from 'lodash/debounce';
import React, { useCallback, useState } from 'react';
import type { AppItemOverview } from 'types/client/apps';
import { TEMPORARY_DEMO_APPS } from 'data/apps';
import AppList from 'ui/apps/AppList';
import AppModal from 'ui/apps/AppModal';
import FilterInput from 'ui/apps/FilterInput';
const defaultDisplayedApps = [ ...TEMPORARY_DEMO_APPS ]
.sort((a, b) => a.title.localeCompare(b.title));
const Apps = () => {
const [ displayedApps, setDisplayedApps ] = useState<Array<AppItemOverview>>(defaultDisplayedApps);
const [ displayedAppId, setDisplayedAppId ] = useState<string | null>('component');
const filterApps = (q: string) => {
const apps = displayedApps
.filter(app => app.title.toLowerCase().includes(q.toLowerCase()));
setDisplayedApps(apps);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const debounceFilterApps = useCallback(debounce(q => filterApps(q), 500), []);
const clearDisplayedAppId = useCallback(() => setDisplayedAppId(null), []);
return (
<>
<FilterInput onChange={ debounceFilterApps }/>
<AppList apps={ displayedApps }/>
<AppModal
id={ displayedAppId }
onClose={ clearDisplayedAppId }
/>
</>
);
};
export default Apps;
import { Box } from '@chakra-ui/react';
import React from 'react';
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
const AddressIcon = ({ address }: {address: string}) => {
return (
<Box width="24px" display="inline-flex">
<Jazzicon diameter={ 24 } seed={ jsNumberForAddress(address) }/>
</Box>
);
};
export default AddressIcon;
import { Flex, Link } from '@chakra-ui/react';
import type { HTMLChakraProps } from '@chakra-ui/system';
import React from 'react';
import useLink from 'lib/link/useLink';
import AddressWithDots from './AddressWithDots';
import CopyToClipboard from './CopyToClipboard';
const FONT_WEIGHT = '600';
interface Props extends HTMLChakraProps<'div'> {
address: string;
type?: 'address' | 'transaction' | 'token';
fontWeight?: string;
truncated?: boolean;
withCopy?: boolean;
}
const AddressLinkWithTooltip = ({ address, type = 'address', truncated, withCopy = true, ...styles }: Props) => {
const link = useLink();
let url;
if (type === 'transaction') {
url = link('tx_index', { id: address });
} else if (type === 'token') {
url = link('token_index', { id: address });
} else {
url = link('address_index', { id: address });
}
return (
<Flex columnGap={ 2 } alignItems="center" overflow="hidden" maxW="100%" { ...styles }>
<Link
href={ url }
target="_blank"
overflow="hidden"
fontWeight={ styles.fontWeight || FONT_WEIGHT }
lineHeight="24px"
whiteSpace="nowrap"
>
<AddressWithDots address={ address } fontWeight={ styles.fontWeight || FONT_WEIGHT } truncated={ truncated }/>
</Link>
{ withCopy && <CopyToClipboard text={ address }/> }
</Flex>
);
};
export default React.memo(AddressLinkWithTooltip);
import { Box, HStack, Text } from '@chakra-ui/react'; import { Text, Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import AddressIcon from 'ui/shared/AddressIcon'; import Address from 'ui/shared/address/Address';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface Props { interface Props {
address: string; address: string;
...@@ -11,13 +13,14 @@ interface Props { ...@@ -11,13 +13,14 @@ interface Props {
const AddressSnippet = ({ address, subtitle }: Props) => { const AddressSnippet = ({ address, subtitle }: Props) => {
return ( return (
<HStack spacing={ 4 } key={ address } overflow="hidden" alignItems="start" maxW="100%"> <Box maxW="100%">
<AddressIcon address={ address }/> <Address>
<Box overflow="hidden"> <AddressIcon hash={ address }/>
<AddressLinkWithTooltip address={ address }/> <AddressLink hash={ address } fontWeight="600" ml={ 2 }/>
{ subtitle && <Text fontSize="sm" variant="secondary" mt={ 0.5 }>{ subtitle }</Text> } <CopyToClipboard text={ address } ml={ 1 }/>
</Address>
{ subtitle && <Text fontSize="sm" variant="secondary" mt={ 0.5 } ml={{ base: 0, lg: 8 }}>{ subtitle }</Text> }
</Box> </Box>
</HStack>
); );
}; };
......
import { IconButton, Tooltip, useClipboard } from '@chakra-ui/react'; import { IconButton, Tooltip, useClipboard, chakra } from '@chakra-ui/react';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import CopyIcon from 'icons/copy.svg'; import CopyIcon from 'icons/copy.svg';
const CopyToClipboard = ({ text }: {text: string}) => { const CopyToClipboard = ({ text, className }: {text: string; className?: string}) => {
const { hasCopied, onCopy } = useClipboard(text, 3000); const { hasCopied, onCopy } = useClipboard(text, 3000);
const [ copied, setCopied ] = useState(false); const [ copied, setCopied ] = useState(false);
...@@ -26,9 +26,10 @@ const CopyToClipboard = ({ text }: {text: string}) => { ...@@ -26,9 +26,10 @@ const CopyToClipboard = ({ text }: {text: string}) => {
display="inline-block" display="inline-block"
flexShrink={ 0 } flexShrink={ 0 }
onClick={ onCopy } onClick={ onCopy }
className={ className }
/> />
</Tooltip> </Tooltip>
); );
}; };
export default CopyToClipboard; export default chakra(CopyToClipboard);
import { Tooltip } from '@chakra-ui/react';
import React from 'react';
interface Props {
hash: string;
}
const HashStringShorten = ({ hash }: Props) => {
return (
<Tooltip label={ hash }>
{ hash.slice(0, 4) + '...' + hash.slice(-4) }
</Tooltip>
);
};
export default HashStringShorten;
// this component trims address like 0x123...4567 (always 4 chars after dots) // this component trims hash string like 0x123...4567 (always 4 chars after dots)
// or shows full address, if fits // or shows full hash string, if fits
// i can't do this with pure css. if you can, feel free to replace it // i can't do this with pure css. if you can, feel free to replace it
...@@ -20,28 +20,20 @@ const TAIL_LENGTH = 4; ...@@ -20,28 +20,20 @@ const TAIL_LENGTH = 4;
const HEAD_MIN_LENGTH = 4; const HEAD_MIN_LENGTH = 4;
interface Props { interface Props {
address: string; hash: string;
fontWeight: string | number; fontWeight?: string | number;
truncated?: boolean;
} }
const shortenAddress = (address: string) => address.slice(0, 4) + '...' + address.slice(-4); const HashStringShortenDynamic = ({ hash, fontWeight = '400' }: Props) => {
const elementRef = useRef<HTMLSpanElement>(null);
const AddressWithDots = ({ address, fontWeight, truncated }: Props) => { const [ displayedString, setDisplayedString ] = React.useState(hash);
const addressRef = useRef<HTMLSpanElement>(null);
const [ displayedAddress, setAddress ] = React.useState(truncated ? shortenAddress(address) : address);
const isFontFaceLoaded = useFontFaceObserver([ const isFontFaceLoaded = useFontFaceObserver([
{ family: BODY_TYPEFACE, weight: String(fontWeight) as FontFace['weight'] }, { family: BODY_TYPEFACE, weight: String(fontWeight) as FontFace['weight'] },
]); ]);
const calculateString = useCallback(() => { const calculateString = useCallback(() => {
const addressEl = addressRef.current; const parent = elementRef?.current?.parentNode as HTMLElement;
if (!addressEl) {
return;
}
const parent = addressRef?.current?.parentNode as HTMLElement;
if (!parent) { if (!parent) {
return; return;
} }
...@@ -49,35 +41,34 @@ const AddressWithDots = ({ address, fontWeight, truncated }: Props) => { ...@@ -49,35 +41,34 @@ const AddressWithDots = ({ address, fontWeight, truncated }: Props) => {
const shadowEl = document.createElement('span'); const shadowEl = document.createElement('span');
shadowEl.style.opacity = '0'; shadowEl.style.opacity = '0';
parent.appendChild(shadowEl); parent.appendChild(shadowEl);
shadowEl.textContent = address; shadowEl.textContent = hash;
const parentWidth = getWidth(parent); const parentWidth = getWidth(parent);
if (getWidth(shadowEl) > parentWidth) { if (getWidth(shadowEl) > parentWidth) {
for (let i = 1; i <= address.length - TAIL_LENGTH - HEAD_MIN_LENGTH; i++) { for (let i = 1; i <= hash.length - TAIL_LENGTH - HEAD_MIN_LENGTH; i++) {
const res = address.slice(0, address.length - i - TAIL_LENGTH) + '...' + address.slice(-TAIL_LENGTH); const res = hash.slice(0, hash.length - i - TAIL_LENGTH) + '...' + hash.slice(-TAIL_LENGTH);
shadowEl.textContent = res; shadowEl.textContent = res;
if (getWidth(shadowEl) < parentWidth || i === address.length - TAIL_LENGTH - HEAD_MIN_LENGTH) { if (getWidth(shadowEl) < parentWidth || i === hash.length - TAIL_LENGTH - HEAD_MIN_LENGTH) {
setAddress(res); setDisplayedString(res);
break; break;
} }
} }
} else { } else {
setAddress(address); setDisplayedString(hash);
} }
parent.removeChild(shadowEl); parent.removeChild(shadowEl);
}, [ address ]); }, [ hash ]);
// we want to do recalculation when isFontFaceLoaded flag is changed // we want to do recalculation when isFontFaceLoaded flag is changed
// but we don't want to create more resize event listeners // but we don't want to create more resize event listeners
// that's why there are separate useEffect hooks // that's why there are separate useEffect hooks
useEffect(() => { useEffect(() => {
!truncated && calculateString(); calculateString();
}, [ calculateString, isFontFaceLoaded, truncated ]); }, [ calculateString, isFontFaceLoaded ]);
useEffect(() => { useEffect(() => {
if (!truncated) {
const resizeHandler = _debounce(calculateString, 100); const resizeHandler = _debounce(calculateString, 100);
const resizeObserver = new ResizeObserver(resizeHandler); const resizeObserver = new ResizeObserver(resizeHandler);
...@@ -85,15 +76,14 @@ const AddressWithDots = ({ address, fontWeight, truncated }: Props) => { ...@@ -85,15 +76,14 @@ const AddressWithDots = ({ address, fontWeight, truncated }: Props) => {
return function cleanup() { return function cleanup() {
resizeObserver.unobserve(document.body); resizeObserver.unobserve(document.body);
}; };
} }, [ calculateString ]);
}, [ calculateString, truncated ]);
const content = <span ref={ addressRef }>{ displayedAddress }</span>; const content = <span ref={ elementRef }>{ displayedString }</span>;
const isTruncated = truncated || address.length !== displayedAddress.length; const isTruncated = hash.length !== displayedString.length;
if (isTruncated) { if (isTruncated) {
return ( return (
<Tooltip label={ address }>{ content }</Tooltip> <Tooltip label={ hash }>{ content }</Tooltip>
); );
} }
...@@ -104,4 +94,4 @@ function getWidth(el: HTMLElement) { ...@@ -104,4 +94,4 @@ function getWidth(el: HTMLElement) {
return el.getBoundingClientRect().width; return el.getBoundingClientRect().width;
} }
export default React.memo(AddressWithDots); export default React.memo(HashStringShortenDynamic);
...@@ -43,9 +43,7 @@ const Page = ({ children }: Props) => { ...@@ -43,9 +43,7 @@ const Page = ({ children }: Props) => {
<Header/> <Header/>
<Box <Box
as="main" as="main"
borderRadius="base"
w="100%" w="100%"
overflow="hidden"
paddingTop={ isMobile ? '138px' : '52px' } paddingTop={ isMobile ? '138px' : '52px' }
> >
{ children } { children }
......
import { Box, HStack, Icon, useColorModeValue } from '@chakra-ui/react'; import { Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface Props { interface Props {
hash: string; hash: string;
...@@ -10,12 +12,11 @@ interface Props { ...@@ -10,12 +12,11 @@ interface Props {
const TransactionSnippet = ({ hash }: Props) => { const TransactionSnippet = ({ hash }: Props) => {
return ( return (
<HStack spacing={ 2 } overflow="hidden" alignItems="start" maxW="100%"> <Address maxW="100%">
<Icon as={ transactionIcon } boxSize={ 6 } color={ useColorModeValue('gray.500', 'gray.400') }/> <Icon as={ transactionIcon } boxSize={ 6 } color={ useColorModeValue('gray.500', 'gray.400') }/>
<Box overflow="hidden"> <AddressLink hash={ hash } fontWeight="600" type="transaction" ml={ 2 }/>
<AddressLinkWithTooltip address={ hash } type="transaction"/> <CopyToClipboard text={ hash } ml={ 1 }/>
</Box> </Address>
</HStack>
); );
}; };
......
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
className?: string;
children: React.ReactNode;
}
const Address = ({ children, className }: Props) => {
return <Flex alignItems="center" overflow="hidden" className={ className }>{ children }</Flex>;
};
const AddressChakra = chakra(Address);
export default React.memo(AddressChakra);
import { Box, chakra } from '@chakra-ui/react';
import React from 'react';
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
const AddressIcon = ({ hash, className }: {hash: string; className?: string}) => {
return (
<Box className={ className } width="24px" display="inline-flex">
<Jazzicon diameter={ 24 } seed={ jsNumberForAddress(hash) }/>
</Box>
);
};
export default chakra(AddressIcon);
import { Link, chakra, shouldForwardProp } from '@chakra-ui/react';
import React from 'react';
import useLink from 'lib/link/useLink';
import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
type?: 'address' | 'transaction' | 'token';
className?: string;
hash: string;
truncation?: 'constant' | 'dynamic'| 'none';
fontWeight?: string;
}
const AddressLink = ({ type, className, truncation = 'dynamic', hash, fontWeight }: Props) => {
const link = useLink();
let url;
if (type === 'transaction') {
url = link('tx_index', { id: hash });
} else if (type === 'token') {
url = link('token_index', { id: hash });
} else {
url = link('address_index', { id: hash });
}
const content = (() => {
switch (truncation) {
case 'constant':
return <HashStringShorten hash={ hash }/>;
case 'dynamic':
return <HashStringShortenDynamic hash={ hash } fontWeight={ fontWeight }/>;
case 'none':
return <span>{ hash }</span>;
}
})();
return (
<Link
className={ className }
href={ url }
target="_blank"
overflow="hidden"
whiteSpace="nowrap"
>
{ content }
</Link>
);
};
const AddressLinkChakra = chakra(AddressLink, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
// forward fontWeight to the AddressLink since it's needed for underlying HashStringShortenDynamic component
if (isChakraProp && prop !== 'fontWeight') {
return false;
}
return true;
},
});
export default React.memo(AddressLinkChakra);
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import rightArrowIcon from 'icons/arrows/right.svg'; import rightArrowIcon from 'icons/arrows/right.svg';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressLink from 'ui/shared/address/AddressLink';
import Token from 'ui/shared/Token'; import Token from 'ui/shared/Token';
interface Props { interface Props {
...@@ -17,9 +17,9 @@ interface Props { ...@@ -17,9 +17,9 @@ interface Props {
const TokenTransfer = ({ from, to, amount, usd, token }: Props) => { const TokenTransfer = ({ from, to, amount, usd, token }: Props) => {
return ( return (
<Center> <Center>
<AddressLinkWithTooltip address={ from } fontWeight="500" truncated withCopy={ false }/> <AddressLink fontWeight="500" hash={ from } truncation="constant"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/> <Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/>
<AddressLinkWithTooltip address={ to } fontWeight="500" truncated withCopy={ false }/> <AddressLink fontWeight="500" hash={ to } truncation="constant"/>
<Text fontWeight={ 500 } as="span" ml={ 4 }>For:{ space } <Text fontWeight={ 500 } as="span" ml={ 4 }>For:{ space }
<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>
......
import { Flex, Text, Grid, GridItem, useColorModeValue } from '@chakra-ui/react'; import { Flex, Text, Grid, GridItem, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface RowProps { interface RowProps {
...@@ -109,10 +110,16 @@ const TxDecodedInputData = () => { ...@@ -109,10 +110,16 @@ const TxDecodedInputData = () => {
Data Data
</GridItem> </GridItem>
<TableRow name="from" type="address"> <TableRow name="from" type="address">
<AddressLinkWithTooltip address="0x0000000000000000000000000000000000000000" columnGap={ 0 } justifyContent="space-between" fontWeight="400"/> <Address justifyContent="space-between">
<AddressLink hash="0x0000000000000000000000000000000000000000"/>
<CopyToClipboard text="0x0000000000000000000000000000000000000000"/>
</Address>
</TableRow> </TableRow>
<TableRow name="from" type="address"> <TableRow name="from" type="address">
<AddressLinkWithTooltip address="0xcf0c50b7ea8af37d57380a0ac199d55b0782c718" columnGap={ 0 } justifyContent="space-between" fontWeight="400"/> <Address justifyContent="space-between">
<AddressLink hash="0xcf0c50b7ea8af37d57380a0ac199d55b0782c718"/>
<CopyToClipboard text="0xcf0c50b7ea8af37d57380a0ac199d55b0782c718"/>
</Address>
</TableRow> </TableRow>
<TableRow name="tokenId" type="uint256" isLast> <TableRow name="tokenId" type="uint256" isLast>
<Flex alignItems="center" justifyContent="space-between"> <Flex alignItems="center" justifyContent="space-between">
......
...@@ -7,8 +7,9 @@ import clockIcon from 'icons/clock.svg'; ...@@ -7,8 +7,9 @@ import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.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 AddressIcon from 'ui/shared/AddressIcon'; import Address from 'ui/shared/address/Address';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import RawInputData from 'ui/shared/RawInputData'; import RawInputData from 'ui/shared/RawInputData';
...@@ -77,15 +78,21 @@ const TxDetails = () => { ...@@ -77,15 +78,21 @@ const TxDetails = () => {
hint="Address (external or contract) sending the transaction." hint="Address (external or contract) sending the transaction."
mt={ 8 } mt={ 8 }
> >
<AddressIcon address={ tx.address_from }/> <Address>
<AddressLinkWithTooltip address={ tx.address_from } columnGap={ 0 } ml={ 2 } fontWeight="400"/> <AddressIcon hash={ tx.address_from }/>
<AddressLink ml={ 2 } hash={ tx.address_from }/>
<CopyToClipboard text={ tx.address_from }/>
</Address>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Interacted with contract" title="Interacted with contract"
hint="Address (external or contract) receiving the transaction." hint="Address (external or contract) receiving the transaction."
> >
<AddressIcon address={ tx.address_to }/> <Address>
<AddressLinkWithTooltip address={ tx.address_to } columnGap={ 0 } ml={ 2 } fontWeight="400"/> <AddressIcon hash={ tx.address_to }/>
<AddressLink ml={ 2 } hash={ tx.address_to }/>
<CopyToClipboard text={ tx.address_to }/>
</Address>
<Tag colorScheme="orange" variant="solid" ml={ 3 }>SANA</Tag> <Tag colorScheme="orange" variant="solid" ml={ 3 }>SANA</Tag>
<Icon as={ successIcon } boxSize={ 4 } ml={ 2 } color="green.500"/> <Icon as={ successIcon } boxSize={ 4 } ml={ 2 } color="green.500"/>
<Token symbol="USDT" ml={ 3 }/> <Token symbol="USDT" ml={ 3 }/>
......
...@@ -14,7 +14,7 @@ const TxStatus = ({ status }: Props) => { ...@@ -14,7 +14,7 @@ const TxStatus = ({ status }: Props) => {
const colorScheme = status === 'success' ? 'green' : 'red'; const colorScheme = status === 'success' ? 'green' : 'red';
return ( return (
<Tag colorScheme={ colorScheme }> <Tag colorScheme={ colorScheme } display="inline-flex">
<TagLeftIcon boxSize={ 2.5 } as={ icon }/> <TagLeftIcon boxSize={ 2.5 } as={ icon }/>
<TagLabel>{ label }</TagLabel> <TagLabel>{ label }</TagLabel>
</Tag> </Tag>
......
import { Tr, Td, Tag, Flex, Icon } from '@chakra-ui/react'; import { Tr, Td, Tag, Icon } from '@chakra-ui/react';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import React from 'react'; import React from 'react';
import rightArrowIcon from 'icons/arrows/right.svg'; import rightArrowIcon from 'icons/arrows/right.svg';
import AddressIcon from 'ui/shared/AddressIcon'; import Address from 'ui/shared/address/Address';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxStatus from 'ui/tx/TxStatus'; import TxStatus from 'ui/tx/TxStatus';
interface Props { interface Props {
...@@ -24,17 +25,17 @@ const TxInternalTableItem = ({ type, status, from, to, value, gasLimit }: Props) ...@@ -24,17 +25,17 @@ const TxInternalTableItem = ({ type, status, from, to, value, gasLimit }: Props)
<TxStatus status={ status }/> <TxStatus status={ status }/>
</Td> </Td>
<Td pr="0"> <Td pr="0">
<Flex alignItems="center"> <Address>
<AddressIcon address={ from }/> <AddressIcon hash={ from }/>
<AddressLinkWithTooltip address={ from } fontWeight="500" withCopy={ false } ml={ 2 }/> <AddressLink ml={ 2 } fontWeight="500" hash={ from }/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/> <Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } flexShrink={ 0 } color="gray.500"/>
</Flex> </Address>
</Td> </Td>
<Td pl="0"> <Td pl="0">
<Flex alignItems="center"> <Address>
<AddressIcon address={ to }/> <AddressIcon hash={ to }/>
<AddressLinkWithTooltip address={ to } fontWeight="500" withCopy={ false } ml={ 2 }/> <AddressLink ml={ 2 } fontWeight="500" hash={ to }/>
</Flex> </Address>
</Td> </Td>
<Td isNumeric> <Td isNumeric>
{ value } { value }
......
...@@ -2,8 +2,9 @@ import { SearchIcon } from '@chakra-ui/icons'; ...@@ -2,8 +2,9 @@ import { SearchIcon } from '@chakra-ui/icons';
import { Text, Grid, GridItem, Link, Tooltip, Button, useColorModeValue } from '@chakra-ui/react'; import { Text, Grid, GridItem, Link, Tooltip, Button, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import AddressIcon from 'ui/shared/AddressIcon'; import Address from 'ui/shared/address/Address';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxLogTopic from 'ui/tx/logs/TxLogTopic'; import TxLogTopic from 'ui/tx/logs/TxLogTopic';
import DecodedInputData from 'ui/tx/TxDecodedInputData'; import DecodedInputData from 'ui/tx/TxDecodedInputData';
...@@ -24,8 +25,10 @@ const TxLogItem = ({ address, index, topics, data }: Props) => { ...@@ -24,8 +25,10 @@ const TxLogItem = ({ address, index, topics, data }: Props) => {
<Grid gridTemplateColumns="200px 1fr" gap={ 8 } py={ 8 } _notFirst={{ borderTopWidth: '1px', borderTopColor: borderColor }}> <Grid gridTemplateColumns="200px 1fr" gap={ 8 } py={ 8 } _notFirst={{ borderTopWidth: '1px', borderTopColor: borderColor }}>
<RowHeader>Address</RowHeader> <RowHeader>Address</RowHeader>
<GridItem display="flex" alignItems="center"> <GridItem display="flex" alignItems="center">
<AddressIcon address={ address }/> <Address>
<AddressLinkWithTooltip address={ address } columnGap={ 0 } ml={ 2 } fontWeight="400" withCopy={ false }/> <AddressIcon hash={ address }/>
<AddressLink hash={ address } ml={ 2 }/>
</Address>
<Tooltip label="Find matches topic"> <Tooltip label="Find matches topic">
<Link ml={ 2 }> <Link ml={ 2 }>
<SearchIcon w={ 5 } h={ 5 }/> <SearchIcon w={ 5 } h={ 5 }/>
......
...@@ -7,7 +7,6 @@ import { ...@@ -7,7 +7,6 @@ import {
Box, Box,
Tr, Tr,
Td, Td,
Flex,
Stat, Stat,
StatArrow, StatArrow,
Portal, Portal,
...@@ -18,8 +17,9 @@ import React, { useRef } from 'react'; ...@@ -18,8 +17,9 @@ import React, { useRef } from 'react';
import type { TTxStateItem } from 'data/txState'; import type { TTxStateItem } from 'data/txState';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import AddressIcon from 'ui/shared/AddressIcon'; import Address from 'ui/shared/address/Address';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxStateStorageItem from './TxStateStorageItem'; import TxStateStorageItem from './TxStateStorageItem';
...@@ -57,10 +57,10 @@ const TxStateTableItem = ({ txStateItem }: { txStateItem: TTxStateItem }) => { ...@@ -57,10 +57,10 @@ const TxStateTableItem = ({ txStateItem }: { txStateItem: TTxStateItem }) => {
</AccordionButton> </AccordionButton>
</Td> </Td>
<Td border={ 0 }> <Td border={ 0 }>
<Flex height="30px" alignItems="center"> <Address height="30px">
<AddressIcon address={ txStateItem.address }/> <AddressIcon hash={ txStateItem.address }/>
<AddressLinkWithTooltip address={ txStateItem.address } fontWeight="500" truncated withCopy={ false } ml={ 2 }/> <AddressLink hash={ txStateItem.address } fontWeight="500" truncation="constant" ml={ 2 }/>
</Flex> </Address>
</Td> </Td>
<Td border={ 0 } lineHeight="30px"><Link>{ txStateItem.miner }</Link></Td> <Td border={ 0 } lineHeight="30px"><Link>{ txStateItem.miner }</Link></Td>
<Td border={ 0 } isNumeric lineHeight="30px"> <Td border={ 0 } isNumeric lineHeight="30px">
......
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