bridge_ui: chain dropdown with icon

Change-Id: I3d60fb2befdbd031ca1a0d7a0fad7e84973ca98b
This commit is contained in:
Evan Gray 2021-10-16 00:16:08 -04:00
parent 60a33353af
commit efa1b9d993
9 changed files with 110 additions and 105 deletions

View File

@ -1,7 +1,6 @@
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
import { makeStyles, TextField } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useBetaContext } from "../../contexts/BetaContext";
import {
incrementStep,
setSourceAsset,
@ -13,8 +12,9 @@ import {
selectAttestSourceAsset,
selectAttestSourceChain,
} from "../../store/selectors";
import { BETA_CHAINS, CHAINS } from "../../utils/consts";
import { CHAINS } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
@ -27,7 +27,6 @@ const useStyles = makeStyles((theme) => ({
function Source() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const sourceChain = useSelector(selectAttestSourceChain);
const sourceAsset = useSelector(selectAttestSourceAsset);
const isSourceComplete = useSelector(selectAttestIsSourceComplete);
@ -49,22 +48,15 @@ function Source() {
}, [dispatch]);
return (
<>
<TextField
<ChainSelect
select
variant="outlined"
fullWidth
value={sourceChain}
onChange={handleSourceChange}
disabled={shouldLockFields}
>
{CHAINS.filter(({ id }) =>
isBeta ? true : !BETA_CHAINS.includes(id)
).map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={CHAINS}
/>
<KeyAndBalance chainId={sourceChain} />
<TextField
label="Asset"

View File

@ -1,8 +1,7 @@
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
import { makeStyles, Typography } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useBetaContext } from "../../contexts/BetaContext";
import { EthGasEstimateSummary } from "../../hooks/useTransactionFees";
import { incrementStep, setTargetChain } from "../../store/attestSlice";
import {
@ -11,9 +10,10 @@ import {
selectAttestSourceChain,
selectAttestTargetChain,
} from "../../store/selectors";
import { BETA_CHAINS, CHAINS, CHAINS_BY_ID } from "../../utils/consts";
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
import { isEVMChain } from "../../utils/ethereum";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
@ -27,7 +27,6 @@ const useStyles = makeStyles((theme) => ({
function Target() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const sourceChain = useSelector(selectAttestSourceChain);
const chains = useMemo(
() => CHAINS.filter((c) => c.id !== sourceChain),
@ -47,22 +46,15 @@ function Target() {
}, [dispatch]);
return (
<>
<TextField
<ChainSelect
select
variant="outlined"
fullWidth
value={targetChain}
onChange={handleTargetChange}
disabled={shouldLockFields}
>
{chains
.filter(({ id }) => (isBeta ? true : !BETA_CHAINS.includes(id)))
.map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={chains}
/>
<KeyAndBalance chainId={targetChain} />
<Alert severity="info" variant="outlined" className={classes.alert}>
<Typography>

View File

@ -0,0 +1,56 @@
import {
ListItemIcon,
ListItemText,
makeStyles,
MenuItem,
OutlinedTextFieldProps,
TextField,
} from "@material-ui/core";
import clsx from "clsx";
import { useMemo } from "react";
import { useBetaContext } from "../contexts/BetaContext";
import { BETA_CHAINS, ChainInfo } from "../utils/consts";
const useStyles = makeStyles((theme) => ({
select: {
"& .MuiSelect-root": {
display: "flex",
alignItems: "center",
},
},
listItemIcon: {
minWidth: 40,
},
icon: {
height: 24,
maxWidth: 24,
},
}));
const createChainMenuItem = ({ id, name, logo }: ChainInfo, classes: any) => (
<MenuItem key={id} value={id}>
<ListItemIcon className={classes.listItemIcon}>
<img src={logo} alt={name} className={classes.icon} />
</ListItemIcon>
<ListItemText>{name}</ListItemText>
</MenuItem>
);
interface ChainSelectProps extends OutlinedTextFieldProps {
chains: ChainInfo[];
}
export default function ChainSelect({ chains, ...rest }: ChainSelectProps) {
const classes = useStyles();
const isBeta = useBetaContext();
const filteredChains = useMemo(
() =>
chains.filter(({ id }) => (isBeta ? true : !BETA_CHAINS.includes(id))),
[chains, isBeta]
);
return (
<TextField {...rest} className={clsx(classes.select, rest.className)}>
{filteredChains.map((chain) => createChainMenuItem(chain, classes))}
</TextField>
);
}

View File

@ -1,10 +1,9 @@
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { Button, makeStyles } from "@material-ui/core";
import { VerifiedUser } from "@material-ui/icons";
import { Alert } from "@material-ui/lab";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { useBetaContext } from "../../contexts/BetaContext";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import { incrementStep, setSourceChain } from "../../store/nftSlice";
import {
@ -14,9 +13,10 @@ import {
selectNFTSourceChain,
selectNFTSourceError,
} from "../../store/selectors";
import { BETA_CHAINS, CHAINS_WITH_NFT_SUPPORT } from "../../utils/consts";
import { CHAINS_WITH_NFT_SUPPORT } from "../../utils/consts";
import { isEVMChain } from "../../utils/ethereum";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
import StepDescription from "../StepDescription";
@ -31,7 +31,6 @@ const useStyles = makeStyles((theme) => ({
function Source() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const sourceChain = useSelector(selectNFTSourceChain);
const uiAmountString = useSelector(selectNFTSourceBalanceString);
const error = useSelector(selectNFTSourceError);
@ -66,22 +65,15 @@ function Source() {
</div>
</div>
</StepDescription>
<TextField
<ChainSelect
variant="outlined"
select
fullWidth
value={sourceChain}
onChange={handleSourceChange}
disabled={shouldLockFields}
>
{CHAINS_WITH_NFT_SUPPORT.filter(({ id }) =>
isBeta ? true : !BETA_CHAINS.includes(id)
).map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={CHAINS_WITH_NFT_SUPPORT}
/>
{isEVMChain(sourceChain) ? (
<Alert severity="info" variant="outlined">
Only NFTs which implement ERC-721 are supported.

View File

@ -3,13 +3,12 @@ import {
hexToNativeString,
hexToUint8Array,
} from "@certusone/wormhole-sdk";
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
import { makeStyles, TextField, Typography } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { PublicKey } from "@solana/web3.js";
import { BigNumber, ethers } from "ethers";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useBetaContext } from "../../contexts/BetaContext";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
import { EthGasEstimateSummary } from "../../hooks/useTransactionFees";
@ -27,13 +26,10 @@ import {
selectNFTTargetChain,
selectNFTTargetError,
} from "../../store/selectors";
import {
BETA_CHAINS,
CHAINS_BY_ID,
CHAINS_WITH_NFT_SUPPORT,
} from "../../utils/consts";
import { CHAINS_BY_ID, CHAINS_WITH_NFT_SUPPORT } from "../../utils/consts";
import { isEVMChain } from "../../utils/ethereum";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
import StepDescription from "../StepDescription";
@ -51,7 +47,6 @@ const useStyles = makeStyles((theme) => ({
function Target() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const sourceChain = useSelector(selectNFTSourceChain);
const chains = useMemo(
() => CHAINS_WITH_NFT_SUPPORT.filter((c) => c.id !== sourceChain),
@ -94,21 +89,14 @@ function Target() {
return (
<>
<StepDescription>Select a recipient chain and address.</StepDescription>
<TextField
<ChainSelect
select
fullWidth
variant="outlined"
value={targetChain}
onChange={handleTargetChange}
>
{chains
.filter(({ id }) => (isBeta ? true : !BETA_CHAINS.includes(id)))
.map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={chains}
/>
<KeyAndBalance chainId={targetChain} balance={uiAmountString} />
<TextField
label="Recipient Address"

View File

@ -36,7 +36,6 @@ import { useSnackbar } from "notistack";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router";
import { useBetaContext } from "../contexts/BetaContext";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { COLORS } from "../muiTheme";
import {
@ -50,7 +49,6 @@ import {
setTargetChain,
} from "../store/transferSlice";
import {
BETA_CHAINS,
CHAINS,
CHAINS_WITH_NFT_SUPPORT,
getBridgeAddressForChain,
@ -67,6 +65,7 @@ import { isEVMChain } from "../utils/ethereum";
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
import parseError from "../utils/parseError";
import ButtonWithLoader from "./ButtonWithLoader";
import ChainSelect from "./ChainSelect";
import KeyAndBalance from "./KeyAndBalance";
const useStyles = makeStyles((theme) => ({
@ -169,7 +168,6 @@ async function terra(tx: string, enqueueSnackbar: any) {
export default function Recovery() {
const classes = useStyles();
const isBeta = useBetaContext();
const { push } = useHistory();
const { enqueueSnackbar } = useSnackbar();
const dispatch = useDispatch();
@ -352,7 +350,7 @@ export default function Recovery() {
<MenuItem value="Token">Token</MenuItem>
<MenuItem value="NFT">NFT</MenuItem>
</TextField>
<TextField
<ChainSelect
select
variant="outlined"
label="Source Chain"
@ -361,15 +359,8 @@ export default function Recovery() {
onChange={handleSourceChainChange}
fullWidth
margin="normal"
>
{(isNFT ? CHAINS_WITH_NFT_SUPPORT : CHAINS)
.filter(({ id }) => (isBeta ? true : !BETA_CHAINS.includes(id)))
.map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={isNFT ? CHAINS_WITH_NFT_SUPPORT : CHAINS}
/>
{isEVMChain(recoverySourceChain) ? (
<KeyAndBalance chainId={recoverySourceChain} />
) : null}

View File

@ -4,11 +4,10 @@ import {
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { getAddress } from "@ethersproject/address";
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { Button, makeStyles, TextField } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router";
import { useBetaContext } from "../../contexts/BetaContext";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import {
selectTransferAmount,
@ -25,13 +24,13 @@ import {
setSourceChain,
} from "../../store/transferSlice";
import {
BETA_CHAINS,
BSC_MIGRATION_ASSET_MAP,
CHAINS,
ETH_MIGRATION_ASSET_MAP,
MIGRATION_ASSET_MAP,
} from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
import StepDescription from "../StepDescription";
@ -47,7 +46,6 @@ const useStyles = makeStyles((theme) => ({
function Source() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const history = useHistory();
const sourceChain = useSelector(selectTransferSourceChain);
const parsedTokenAccount = useSelector(
@ -105,22 +103,15 @@ function Source() {
<StepDescription>
Select tokens to send through the Wormhole Token Bridge.
</StepDescription>
<TextField
<ChainSelect
select
variant="outlined"
fullWidth
value={sourceChain}
onChange={handleSourceChange}
disabled={shouldLockFields}
>
{CHAINS.filter(({ id }) =>
isBeta ? true : !BETA_CHAINS.includes(id)
).map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={CHAINS}
/>
<KeyAndBalance chainId={sourceChain} balance={uiAmountString} />
{isReady || uiAmountString ? (
<div className={classes.transferField}>

View File

@ -1,9 +1,8 @@
import { CHAIN_ID_SOLANA, hexToNativeString } from "@certusone/wormhole-sdk";
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
import { makeStyles, Typography } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useBetaContext } from "../../contexts/BetaContext";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import useMetadata from "../../hooks/useMetadata";
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
@ -21,9 +20,10 @@ import {
UNREGISTERED_ERROR_MESSAGE,
} from "../../store/selectors";
import { incrementStep, setTargetChain } from "../../store/transferSlice";
import { BETA_CHAINS, CHAINS, CHAINS_BY_ID } from "../../utils/consts";
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
import { isEVMChain } from "../../utils/ethereum";
import ButtonWithLoader from "../ButtonWithLoader";
import ChainSelect from "../ChainSelect";
import KeyAndBalance from "../KeyAndBalance";
import LowBalanceWarning from "../LowBalanceWarning";
import SmartAddress from "../SmartAddress";
@ -76,7 +76,6 @@ export const useTargetInfo = () => {
function Target() {
const classes = useStyles();
const dispatch = useDispatch();
const isBeta = useBetaContext();
const sourceChain = useSelector(selectTransferSourceChain);
const chains = useMemo(
() => CHAINS.filter((c) => c.id !== sourceChain),
@ -115,22 +114,15 @@ function Target() {
return (
<>
<StepDescription>Select a recipient chain and address.</StepDescription>
<TextField
<ChainSelect
variant="outlined"
select
fullWidth
value={targetChain}
onChange={handleTargetChange}
disabled={shouldLockFields}
>
{chains
.filter(({ id }) => (isBeta ? true : !BETA_CHAINS.includes(id)))
.map(({ id, name }) => (
<MenuItem key={id} value={id}>
{name}
</MenuItem>
))}
</TextField>
chains={chains}
/>
<KeyAndBalance chainId={targetChain} balance={uiAmountString} />
{readableTargetAddress ? (
<>

View File

@ -7,6 +7,10 @@ import {
} from "@certusone/wormhole-sdk";
import { clusterApiUrl } from "@solana/web3.js";
import { getAddress } from "ethers/lib/utils";
import bscIcon from "../icons/bsc.svg";
import ethIcon from "../icons/eth.svg";
import solanaIcon from "../icons/solana.svg";
import terraIcon from "../icons/terra.svg";
export type Cluster = "devnet" | "testnet" | "mainnet";
export const CLUSTER: Cluster =
@ -18,6 +22,7 @@ export const CLUSTER: Cluster =
export interface ChainInfo {
id: ChainId;
name: string;
logo: string;
}
export const CHAINS =
CLUSTER === "mainnet"
@ -25,18 +30,22 @@ export const CHAINS =
{
id: CHAIN_ID_BSC,
name: "Binance Smart Chain",
logo: bscIcon,
},
{
id: CHAIN_ID_ETH,
name: "Ethereum",
logo: ethIcon,
},
{
id: CHAIN_ID_SOLANA,
name: "Solana",
logo: solanaIcon,
},
{
id: CHAIN_ID_TERRA,
name: "Terra",
logo: terraIcon,
},
]
: CLUSTER === "testnet"
@ -44,32 +53,34 @@ export const CHAINS =
{
id: CHAIN_ID_ETH,
name: "Ethereum",
logo: ethIcon,
},
{
id: CHAIN_ID_SOLANA,
name: "Solana",
logo: solanaIcon,
},
// {
// id: CHAIN_ID_TERRA,
// name: "Terra",
// },
]
: [
{
id: CHAIN_ID_BSC,
name: "Binance Smart Chain",
logo: bscIcon,
},
{
id: CHAIN_ID_ETH,
name: "Ethereum",
logo: ethIcon,
},
{
id: CHAIN_ID_SOLANA,
name: "Solana",
logo: solanaIcon,
},
{
id: CHAIN_ID_TERRA,
name: "Terra",
logo: terraIcon,
},
];
export const BETA_CHAINS = CLUSTER === "mainnet" ? [CHAIN_ID_TERRA] : [];