Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
"defi",
"deletable",
"devnet",
"dodoex",
"drawed",
"energi",
"enjin",
Expand Down Expand Up @@ -376,7 +377,9 @@
"testweb",
"tinycolor",
"tipable",
"traderjoexyz",
"treeshake",
"txes",
"txreceipt",
"txts",
"uaddr",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { memo, useMemo } from 'react'
import formatDateTime from 'date-fns/format'
import type { Transaction } from '@masknet/web3-shared-evm'
import { Box, TableCell, TableRow, Typography, Link, Stack } from '@mui/material'
import { makeStyles, MaskColorVar } from '@masknet/theme'
import {
ChainId,
DebankTransactionDirection,
TransactionType,
ZerionTransactionDirection,
} from '@masknet/web3-shared-evm'
import { TransactionIcon } from '../TransactionIcon'
import { LinkOutIcon } from '@masknet/icons'
import classNames from 'classnames'
import { useReverseAddress, useWeb3State } from '@masknet/plugin-infra/web3'
import { makeStyles, MaskColorVar } from '@masknet/theme'
import type { Transaction } from '@masknet/web3-shared-evm'
import { ChainId, DebankTransactionDirection, ZerionTransactionDirection } from '@masknet/web3-shared-evm'
import { Box, Link, Stack, TableCell, TableRow, Typography } from '@mui/material'
import classNames from 'classnames'
import formatDateTime from 'date-fns/format'
import { memo } from 'react'
import { TransactionIcon } from '../TransactionIcon'

const useStyles = makeStyles()((theme) => ({
type: {
Expand Down Expand Up @@ -60,12 +55,7 @@ export interface HistoryTableRowProps {
export const HistoryTableRow = memo<HistoryTableRowProps>(({ transaction, selectedChainId }) => {
const { value: domain } = useReverseAddress(transaction.toAddress)

const transactionType = useMemo(() => {
if (transaction.type === TransactionType.CREATE_RED_PACKET) {
return 'Create Lucky Drop'
}
return (transaction.type ?? '').replace(/_/g, ' ')
}, [transaction.type])
const transactionType = (transaction.type ?? '').replace(/_/g, ' ')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe extract format function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe we should add a uiName field to Transaction


return (
<HistoryTableRowUI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const TransactionIconUI = memo<TransactionIconUIProps>(({ isFailed, isRed
return <UploadIcon style={{ stroke: MaskColorVar.warning }} className={classes.icon} />
case TransactionType.RECEIVE:
return <DownloadIcon style={{ stroke: MaskColorVar.greenMain }} className={classes.icon} />
case TransactionType.CREATE_RED_PACKET:
case TransactionType.CREATE_LUCKY_DROP:
return <RedPacketIcon className={classes.icon} />
case TransactionType.FILL_POOL:
return <ITOIcon className={classes.icon} />
Expand Down
1 change: 1 addition & 0 deletions packages/mask/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@masknet/theme": "workspace:*",
"@masknet/typed-message": "workspace:*",
"@masknet/web3-contracts": "workspace:*",
"@masknet/web3-constants": "workspace:*",
"@masknet/web3-providers": "workspace:*",
"@masknet/web3-shared-base": "workspace:*",
"@masknet/web3-shared-evm": "workspace:*",
Expand Down
7 changes: 7 additions & 0 deletions packages/mask/shared-ui/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,17 @@
"plugin_airdrop_nft_claim_all": "Claim Token",
"plugin_airdrop_nft_claim_successful": "Token claimed successfully",
"plugin_airdrop_nft_claim_failed": "Token claimed failed",
"recent_transaction_pending": "Pending",
"recent_transaction_success": "Success",
"recent_transaction_failed": "Failed",
"recent_transaction_cancelled": "Cancelled",
"wallet_balance": "Balance",
"wallet_balance_eth": "Balance(ETH)",
"wallet_new": "New Wallet",
"wallets": "Wallets",
"wallet_status_pending": "Pending",
"wallet_status_pending_clear_all": "Clear all",
"wallet_status_pending_clear": "Clear",
"wallet_status_button_change": "Change",
"wallet_status_button_copy_address": "Copy Address",
"wallet_transfer_account": "Transfer Account",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { LinkOutIcon } from '@masknet/icons'
import { useWeb3State } from '@masknet/plugin-infra/web3'
import { WalletMessages } from '@masknet/plugin-wallet'
import { makeStyles } from '@masknet/theme'
import {
ChainId,
getContractOwnerDomain,
isSameAddress,
TransactionStateType,
TransactionStatusType,
useChainId,
useWeb3,
} from '@masknet/web3-shared-evm'
import { Button, Grid, GridProps, Link, List, ListItem, ListProps, Stack, Typography } from '@mui/material'
import classnames from 'classnames'
import format from 'date-fns/format'
import { noop } from 'lodash-unified'
import { FC, forwardRef, useCallback, useEffect, useMemo, useState } from 'react'
import { useAsync } from 'react-use'
import Services from '../../../extension/service'
import type { RecentTransaction } from '../../../plugins/Wallet/services/transaction'
import { useI18N } from '../../../utils'

const useStyles = makeStyles()((theme) => ({
list: {
borderRadius: 8,
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
padding: 0,
},
listItem: {
height: 54,
backgroundColor: theme.palette.background.paper,
'&:nth-child(even)': {
backgroundColor: theme.palette.background.default,
},
},
transaction: {
width: '100%',
},
methodName: {
fontWeight: 'bold',
fontSize: 14,
textTransform: 'capitalize',
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
},
cell: {
fontSize: 14,
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 0.5),
color: theme.palette.text.primary,
boxSizing: 'border-box',
},
link: {
fill: 'none',
},
linkIcon: {
fill: 'none',
width: 12,
height: 12,
marginLeft: theme.spacing(0.5),
},
}))

const statusTextColorMap: Record<TransactionStatusType, string> = {
[TransactionStatusType.NOT_DEPEND]: '#FFB915',
[TransactionStatusType.SUCCEED]: '#60DFAB',
[TransactionStatusType.FAILED]: '#FF5F5F',
[TransactionStatusType.CANCELLED]: '#FF5F5F',
}
const getContractFunctionName = async (data: string | undefined) => {
if (!data) return null
const sig = data.slice(0, 10)
const name = await Services.Ethereum.getContractFunctionName(sig)
return name ? name.replace(/_/g, ' ') : 'Contract Interaction'
}

interface TransactionProps extends GridProps {
chainId: ChainId
transaction: RecentTransaction
onClear?(tx: RecentTransaction): void
}
const Transaction: FC<TransactionProps> = ({ chainId, transaction: tx, onClear = noop, ...rest }) => {
const { t } = useI18N()
const { Utils } = useWeb3State()
const { classes, theme } = useStyles()

const statusTextMap: Record<TransactionStatusType, string> = {
[TransactionStatusType.NOT_DEPEND]: t('recent_transaction_pending'),
[TransactionStatusType.SUCCEED]: t('recent_transaction_success'),
[TransactionStatusType.FAILED]: t('recent_transaction_failed'),
[TransactionStatusType.CANCELLED]: t('recent_transaction_cancelled'),
}

const web3 = useWeb3()
const { value: targetAddress } = useAsync(async () => {
if (tx.receipt?.contractAddress) return tx.receipt.contractAddress
if (tx.payload?.params?.[0].to) return tx.payload.params[0].to as string

@nuanyang233 nuanyang233 Apr 20, 2022 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the lodash head or first

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Access tx.payload.params twice, don't wanna use head twice.

const transaction = await web3.eth.getTransaction(tx.hash)
return transaction.to
}, [web3, tx])
const address = (targetAddress || '').toLowerCase()
const txData = tx.payload?.params?.[0].data as string | undefined
const { value: functionName } = useAsync(async () => {
return getContractFunctionName(txData)
}, [txData])

const handleClear = useCallback(() => {
onClear(tx)
}, [onClear, tx])

const domainOrAddress = useMemo(() => getContractOwnerDomain(address), [address])

const [txStatus, setTxStatus] = useState(tx.status)

useEffect(() => {
return WalletMessages.events.transactionStateUpdated.on((data) => {
if ('receipt' in data && tx.hash === data.receipt?.transactionHash) {
switch (data.type) {
case TransactionStateType.CONFIRMED:
setTxStatus(TransactionStatusType.SUCCEED)
break
case TransactionStateType.FAILED:
setTxStatus(TransactionStatusType.FAILED)
}
}
})
}, [tx.hash])

return (
<Grid container {...rest}>
<Grid item className={classes.cell} textAlign="left" md={4}>
<Stack overflow="hidden">
<Typography
className={classes.methodName}
title={functionName || ''}
variant="body1"
component="strong">
{functionName}
</Typography>
<Typography variant="body1" color={theme.palette.text.secondary}>
{format(tx.at, 'yyyy.MM.dd hh:mm')}
</Typography>
</Stack>
</Grid>
<Grid item className={classes.cell} flexGrow={1} md={4} justifyContent="right">
<Typography variant="body1">
{address && isSameAddress(domainOrAddress, address)
? Utils?.formatAddress?.(address, 4)
: domainOrAddress || address}
</Typography>
<Link
className={classes.link}
href={Utils?.resolveTransactionLink?.(chainId, tx.hash)}
target="_blank"
rel="noopener noreferrer">
<LinkOutIcon className={classes.linkIcon} />
</Link>
</Grid>
<Grid item className={classes.cell} md={2} justifyContent="center">
<Typography justifyContent="center" color={statusTextColorMap[txStatus]}>
{statusTextMap[txStatus]}
</Typography>
</Grid>
<Grid item className={classes.cell} md={2} justifyContent="center">
{txStatus === TransactionStatusType.NOT_DEPEND ? (
<Button variant="text" size="small" onClick={handleClear}>
{t('wallet_status_pending_clear')}
</Button>
) : null}
</Grid>
</Grid>
)
}

interface Props extends ListProps {
transactions: RecentTransaction[]
onClear?(tx: RecentTransaction): void
}

export const TransactionList: FC<Props> = forwardRef(({ className, transactions, onClear = noop, ...rest }, ref) => {
const { classes } = useStyles()
const chainId = useChainId()
if (!transactions.length) return null
return (
<List className={classnames(classes.list, className)} {...rest} ref={ref}>
{transactions.map((tx) => (
<ListItem key={tx.hash} className={classes.listItem}>
<Transaction className={classes.transaction} transaction={tx} chainId={chainId} onClear={onClear} />
</ListItem>
))}
</List>
)
})
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { useCopyToClipboard } from 'react-use'
import { Copy, ExternalLink } from 'react-feather'
import classNames from 'classnames'
import { ProviderType } from '@masknet/web3-shared-evm'
import { Button, Link, Typography } from '@mui/material'
import { getMaskColor, makeStyles } from '@masknet/theme'
import { isDashboardPage } from '@masknet/shared-base'
import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
import {
useAccount,
useWeb3State,
useChainId,
useNetworkDescriptor,
useProviderDescriptor,
useProviderType,
useReverseAddress,
useWallet,
useWeb3State,
} from '@masknet/plugin-infra/web3'
import { FormattedAddress, useSnackbarCallback, WalletIcon } from '@masknet/shared'
import { WalletMessages } from '../../plugins/Wallet/messages'
import { useI18N } from '../../utils'
import { isDashboardPage } from '@masknet/shared-base'
import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
import { getMaskColor, makeStyles } from '@masknet/theme'
import { ProviderType } from '@masknet/web3-shared-evm'
import { Button, Link, Typography } from '@mui/material'
import classNames from 'classnames'
import { Copy, ExternalLink } from 'react-feather'
import { useCopyToClipboard } from 'react-use'
import { WalletMessages } from '../../../plugins/Wallet/messages'
import { useI18N } from '../../../utils'
import { usePendingTransactions } from './usePendingTransactions'

const useStyles = makeStyles<{ isDashboard: boolean }>()((theme, { isDashboard }) => ({
content: {
Expand Down Expand Up @@ -86,6 +87,17 @@ const useStyles = makeStyles<{ isDashboard: boolean }>()((theme, { isDashboard }
backgroundColor: '#ffffff',
color: theme.palette.common.black,
},
statusBox: {
position: 'relative',
},
transactionList: {
zIndex: 999,
position: 'absolute',
left: 0,
right: 0,
top: 88,
padding: 0,
},
}))
interface WalletStatusBox {
isDashboard?: boolean
Expand Down Expand Up @@ -127,8 +139,15 @@ export function WalletStatusBox(props: WalletStatusBox) {
)
// #endregion

const { summary: pendingSummary, transactionList } = usePendingTransactions()

return account ? (
<section className={classNames(classes.currentAccount, props.isDashboard ? classes.dashboardBackground : '')}>
<section
className={classNames(
classes.statusBox,
classes.currentAccount,
props.isDashboard ? classes.dashboardBackground : '',
)}>
<WalletIcon
size={48}
badgeSize={16}
Expand Down Expand Up @@ -174,8 +193,10 @@ export function WalletStatusBox(props: WalletStatusBox) {
rel="noopener noreferrer">
<ExternalLink className={classes.linkIcon} size={14} />
</Link>
{pendingSummary}
</div>
</div>
<div className={classes.transactionList}>{transactionList}</div>
{!props.disableChange && (
<section>
<Button
Expand Down
Loading