Tweak deposit dialog layout

This commit is contained in:
Gary Wang 2020-09-10 00:25:53 -07:00
parent 25b6604487
commit 21e8770ef0
7 changed files with 195 additions and 134 deletions

View File

@ -82,7 +82,7 @@ export default function AddTokenDialog({ open, onClose }) {
{tokenAccountCost ? (
<DialogContentText>
Add a token to your wallet. This will cost{' '}
{feeFormat.format(tokenAccountCost / LAMPORTS_PER_SOL)} Solana.
{feeFormat.format(tokenAccountCost / LAMPORTS_PER_SOL)} SOL.
</DialogContentText>
) : (
<LoadingIndicator />

View File

@ -13,6 +13,7 @@ import LoadingIndicator from './LoadingIndicator';
import Collapse from '@material-ui/core/Collapse';
import { Typography } from '@material-ui/core';
import TokenInfoDialog from './TokenInfoDialog';
import Link from '@material-ui/core/Link';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import { makeStyles } from '@material-ui/core/styles';
@ -25,12 +26,16 @@ import Toolbar from '@material-ui/core/Toolbar';
import AddIcon from '@material-ui/icons/Add';
import RefreshIcon from '@material-ui/icons/Refresh';
import IconButton from '@material-ui/core/IconButton';
import InfoIcon from '@material-ui/icons/Info';
import InfoIcon from '@material-ui/icons/InfoOutlined';
import Tooltip from '@material-ui/core/Tooltip';
import AddTokenDialog from './AddTokenDialog';
import SendDialog from './SendDialog';
import DepositDialog from './DepositDialog';
import { refreshAccountInfo } from '../utils/connection';
import {
refreshAccountInfo,
useSolanaExplorerUrlSuffix,
} from '../utils/connection';
import { showTokenInfoDialog } from '../utils/config';
const balanceFormat = new Intl.NumberFormat(undefined, {
minimumFractionDigits: 4,
@ -106,15 +111,12 @@ function BalanceListItem({ publicKey }) {
const balanceInfo = useBalanceInfo(publicKey);
const classes = useStyles();
const [open, setOpen] = useState(false);
const [sendDialogOpen, setSendDialogOpen] = useState(false);
const [depositDialogOpen, setDepositDialogOpen] = useState(false);
const [tokenInfoDialogOpen, setTokenInfoDialogOpen] = useState(false);
if (!balanceInfo) {
return <LoadingIndicator delay={0} />;
}
let { amount, decimals, mint, tokenName, tokenSymbol, owner } = balanceInfo;
let { amount, decimals, mint, tokenName, tokenSymbol } = balanceInfo;
return (
<>
@ -133,37 +135,86 @@ function BalanceListItem({ publicKey }) {
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<div className={classes.itemDetails}>
<div className={classes.buttonContainer}>
{!publicKey.equals(owner) && (
<Button
variant="outlined"
color="inherit"
startIcon={<InfoIcon />}
onClick={() => setTokenInfoDialogOpen(true)}
>
Token Info
</Button>
)}
<Button
variant="outlined"
color="primary"
startIcon={<ReceiveIcon />}
onClick={() => setDepositDialogOpen(true)}
>
Receive
</Button>
<Button
variant="outlined"
color="primary"
startIcon={<SendIcon />}
onClick={() => setSendDialogOpen(true)}
>
Send
</Button>
</div>
</div>
<BalanceListItemDetails
publicKey={publicKey}
balanceInfo={balanceInfo}
/>
</Collapse>
</>
);
}
function BalanceListItemDetails({ publicKey, balanceInfo }) {
const urlSuffix = useSolanaExplorerUrlSuffix();
const classes = useStyles();
const [sendDialogOpen, setSendDialogOpen] = useState(false);
const [depositDialogOpen, setDepositDialogOpen] = useState(false);
const [tokenInfoDialogOpen, setTokenInfoDialogOpen] = useState(false);
if (!balanceInfo) {
return <LoadingIndicator delay={0} />;
}
let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
return (
<>
<div className={classes.itemDetails}>
<div className={classes.buttonContainer}>
{!publicKey.equals(owner) && showTokenInfoDialog ? (
<Button
variant="outlined"
color="default"
startIcon={<InfoIcon />}
onClick={() => setTokenInfoDialogOpen(true)}
>
Token Info
</Button>
) : null}
<Button
variant="outlined"
color="primary"
startIcon={<ReceiveIcon />}
onClick={() => setDepositDialogOpen(true)}
>
Receive
</Button>
<Button
variant="outlined"
color="primary"
startIcon={<SendIcon />}
onClick={() => setSendDialogOpen(true)}
>
Send
</Button>
</div>
<Typography variant="body2" className={classes.address}>
Deposit Address: {publicKey.toBase58()}
</Typography>
<Typography variant="body2">
Token Name: {tokenName ?? 'Unknown'}
</Typography>
<Typography variant="body2">
Token Symbol: {tokenSymbol ?? 'Unknown'}
</Typography>
{mint ? (
<Typography variant="body2" className={classes.address}>
Token Address: {mint.toBase58()}
</Typography>
) : null}
<Typography variant="body2">
<Link
href={
`https://explorer.solana.com/account/${publicKey.toBase58()}` +
urlSuffix
}
target="_blank"
rel="noopener"
>
View on Solana Explorer
</Link>
</Typography>
</div>
<SendDialog
open={sendDialogOpen}
onClose={() => setSendDialogOpen(false)}

View File

@ -1,8 +1,13 @@
import React, { useRef } from 'react';
import { Button, TextField } from '@material-ui/core';
import { TextField } from '@material-ui/core';
import CopyIcon from 'mdi-material-ui/ContentCopy';
import { makeStyles } from '@material-ui/core/styles';
import { useSnackbar } from 'notistack';
import QrcodeIcon from 'mdi-material-ui/Qrcode';
import QRCode from 'qrcode.react';
import DialogForm from './DialogForm';
import DialogContent from '@material-ui/core/DialogContent';
import IconButton from '@material-ui/core/IconButton';
const useStyles = makeStyles((theme) => ({
root: {
@ -10,21 +15,13 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
button: {
marginLeft: theme.spacing(3),
whiteSpace: 'nowrap',
marginTop: theme.spacing(2),
},
buttonIcon: {
marginRight: theme.spacing(1),
},
}));
export default function CopyableDisplay({
value,
label,
autoFocus,
buttonProps,
qrCode,
helperText,
}) {
const { enqueueSnackbar } = useSnackbar();
@ -57,14 +54,37 @@ export default function CopyableDisplay({
label={label}
spellCheck={false}
/>
<Button
variant="contained"
onClick={copyLink}
className={classes.button}
{...buttonProps}
>
<CopyIcon className={classes.buttonIcon} /> {'Copy'}
</Button>
<IconButton onClick={copyLink}>
<CopyIcon />
</IconButton>
{qrCode ? <Qrcode value={value} /> : null}
</div>
);
}
const useQrCodeStyles = makeStyles((theme) => ({
qrcodeContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginBottom: theme.spacing(2),
},
}));
function Qrcode({ value }) {
const [showQrcode, setShowQrcode] = React.useState(false);
const classes = useQrCodeStyles();
return (
<>
<IconButton onClick={() => setShowQrcode(true)}>
<QrcodeIcon />
</IconButton>
<DialogForm open={showQrcode} onClose={() => setShowQrcode(false)}>
<DialogContent className={classes.qrcodeContainer}>
<QRCode value={value} size={256} includeMargin />
</DialogContent>
</DialogForm>
</>
);
}

View File

@ -3,11 +3,12 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DialogForm from './DialogForm';
import { abbreviateAddress } from '../utils/utils';
import { Button, Typography } from '@material-ui/core';
import CopyableDisplay from './CopyableDisplay';
import { makeStyles } from '@material-ui/core/styles';
import QrcodeIcon from 'mdi-material-ui/Qrcode';
import QRCode from 'qrcode.react';
import Link from '@material-ui/core/Link';
import { useSolanaExplorerUrlSuffix } from '../utils/connection';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
export default function DepositDialog({
open,
@ -15,72 +16,50 @@ export default function DepositDialog({
publicKey,
balanceInfo,
}) {
const urlSuffix = useSolanaExplorerUrlSuffix();
let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
return (
<DialogForm open={open} onClose={onClose}>
<DialogTitle>
Deposit {tokenName ?? abbreviateAddress(mint)}
Deposit {tokenName ?? mint.toBase58()}
{tokenSymbol ? ` (${tokenSymbol})` : null}
</DialogTitle>
<DialogContent>
{publicKey.equals(owner) ? (
<Typography>
<DialogContentText>
This address can only be used to receive SOL. Do not send other
tokens to this address.
</Typography>
</DialogContentText>
) : (
<Typography>
This address can only be used to receive {tokenSymbol}. Do not send
SOL to this address.
</Typography>
<DialogContentText>
This address can only be used to receive{' '}
{tokenSymbol ?? abbreviateAddress(mint)}. Do not send SOL to this
address.
</DialogContentText>
)}
<CopyableDisplay
value={publicKey.toBase58()}
label={'Deposit Address'}
autoFocus
qrCode
/>
<Qrcode value={publicKey.toBase58()} />
<DialogContentText variant="body2">
<Link
href={
`https://explorer.solana.com/account/${publicKey.toBase58()}` +
urlSuffix
}
target="_blank"
rel="noopener"
>
View on Solana Explorer
</Link>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Close</Button>
</DialogActions>
</DialogForm>
);
}
const useQrCodeStyles = makeStyles((theme) => ({
qrcodeButton: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(2),
},
qrcodeIcon: {
marginRight: theme.spacing(1),
},
qrcodeContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginBottom: theme.spacing(2),
},
}));
function Qrcode({ value }) {
const [showQrcode, setShowQrcode] = React.useState(false);
const classes = useQrCodeStyles();
return (
<>
<Button
variant="contained"
onClick={() => setShowQrcode(!showQrcode)}
className={classes.qrcodeButton}
>
<QrcodeIcon className={classes.qrcodeIcon} />
{showQrcode ? 'Hide QR Code' : 'Show QR Code'}
</Button>
{showQrcode && (
<div className={classes.qrcodeContainer}>
<QRCode value={value} size={256} includeMargin />
</div>
)}
</>
);
}

View File

@ -22,6 +22,7 @@ import Tooltip from '@material-ui/core/Tooltip';
const useStyles = makeStyles((theme) => ({
content: {
flexGrow: 1,
paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(3),
paddingLeft: theme.spacing(1),
@ -49,34 +50,10 @@ export default function NavigationFrame({ children }) {
</Typography>
<WalletSelector />
<NetworkSelector />
<Hidden xsDown>
<Button
component="a"
color="inherit"
target="_blank"
rel="noopener"
href="https://github.com/serum-foundation/spl-token-wallet"
className={classes.button}
>
Source
</Button>
</Hidden>
<Hidden smUp>
<Tooltip title="View Source" arrow>
<IconButton
component="a"
color="inherit"
target="_blank"
rel="noopener"
href="https://github.com/serum-foundation/spl-token-wallet"
>
<CodeIcon />
</IconButton>
</Tooltip>
</Hidden>
</Toolbar>
</AppBar>
<main className={classes.content}>{children}</main>
<Footer />
</>
);
}
@ -215,3 +192,30 @@ function WalletSelector() {
</>
);
}
const useFooterStyles = makeStyles((theme) => ({
footer: {
display: 'flex',
justifyContent: 'flex-end',
margin: theme.spacing(2),
},
}));
function Footer() {
const classes = useFooterStyles();
return (
<footer className={classes.footer}>
<Button
variant="outlined"
color="primary"
component="a"
target="_blank"
rel="noopener"
href="https://github.com/serum-foundation/spl-token-wallet"
startIcon={<CodeIcon />}
>
View Source
</Button>
</footer>
);
}

View File

@ -7,7 +7,11 @@ body {
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
html, body, #root {
height: 100%;
}
#root {
display: flex;
flex-direction: column;
}

3
src/utils/config.js Normal file
View File

@ -0,0 +1,3 @@
export const showTokenInfoDialog = !!localStorage.getItem(
'showTokenInfoDialog',
);