feat(nft-bag): wrong-network-cta (#5056)

* pull warning out into its own component

* add wrong network cta

* simplify sufficient balance logic

* consolidate warning and button text logic
This commit is contained in:
Jordan Frankfurt 2022-11-02 16:56:37 -05:00 committed by GitHub
parent d400b9094d
commit d575c72127
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 56 deletions

@ -1,7 +1,6 @@
import { BigNumber } from '@ethersproject/bignumber'
import { formatEther } from '@ethersproject/units'
import { useWeb3React } from '@web3-react/core'
import { parseEther } from 'ethers/lib/utils'
import { BagFooter } from 'nft/components/bag/BagFooter'
import ListingModal from 'nft/components/bag/profile/ListingModal'
import { Box } from 'nft/components/Box'
@ -16,7 +15,6 @@ import {
useSellAsset,
useSendTransaction,
useTransactionResponse,
useWalletBalance,
} from 'nft/hooks'
import { fetchRoute } from 'nft/queries'
import { BagItemStatus, BagStatus, ProfilePageStateType, RouteResponse, TxStateType } from 'nft/types'
@ -57,7 +55,8 @@ const ScrollingIndicator = ({ top, show }: SeparatorProps) => (
)
const Bag = () => {
const { account } = useWeb3React()
const { account, provider } = useWeb3React()
const bagStatus = useBag((s) => s.bagStatus)
const setBagStatus = useBag((s) => s.setBagStatus)
const didOpenUnavailableAssets = useBag((s) => s.didOpenUnavailableAssets)
@ -76,9 +75,6 @@ const Bag = () => {
const setTotalEthPrice = useBag((s) => s.setTotalEthPrice)
const setTotalUsdPrice = useBag((s) => s.setTotalUsdPrice)
const { address, balance: balanceInEth, provider } = useWalletBalance()
const isConnected = !!provider && !!address
const { pathname } = useLocation()
const isProfilePage = pathname.startsWith('/profile')
const isNFTPage = pathname.startsWith('/nfts')
@ -126,11 +122,6 @@ const Bag = () => {
return { totalEthPrice, totalUsdPrice }
}, [itemsInBag, fetchedPriceData])
const sufficientBalance = useMemo(() => {
const balance = parseEther(balanceInEth.toString())
return isConnected ? BigNumber.from(balance).gte(totalEthPrice) : true
}, [balanceInEth, totalEthPrice, isConnected])
const purchaseAssets = async (routingData: RouteResponse) => {
if (!provider || !routingData) return
const purchaseResponse = await sendTransaction(
@ -279,13 +270,10 @@ const Bag = () => {
<ScrollingIndicator show={userCanScroll && scrollProgress < 100} />
{hasAssetsToShow && !isProfilePage && (
<BagFooter
sufficientBalance={sufficientBalance}
isConnected={isConnected}
totalEthPrice={totalEthPrice}
totalUsdPrice={totalUsdPrice}
bagStatus={bagStatus}
fetchAssets={fetchAssets}
assetsAreInReview={itemsInBag.some((item) => item.status === BagItemStatus.REVIEWING_PRICE_CHANGE)}
eventProperties={{
usd_value: totalUsdPrice,
...formatAssetEventProperties(itemsInBag.map((item) => item.asset)),

@ -1,18 +1,24 @@
import { BigNumber } from '@ethersproject/bignumber'
import { parseEther } from '@ethersproject/units'
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { ElementName, Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import Loader from 'components/Loader'
import { SupportedChainId } from 'constants/chains'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { bodySmall } from 'nft/css/common.css'
import { useWalletBalance } from 'nft/hooks/useWalletBalance'
import { BagStatus } from 'nft/types'
import { ethNumberStandardFormatter, formatWeiToDecimal } from 'nft/utils'
import { PropsWithChildren, useCallback, useMemo } from 'react'
import { AlertTriangle } from 'react-feather'
import { useModalIsOpen, useToggleWalletModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { switchChain } from 'utils/switchChain'
import * as styles from './BagFooter.css'
@ -42,14 +48,36 @@ const WarningText = styled(ThemedText.BodyPrimary)`
text-align: center;
`
interface ActionButtonProps {
disabled?: boolean
onClick: () => void
}
const ActionButton = ({ disabled, children, onClick }: PropsWithChildren<ActionButtonProps>) => {
return (
<Row as="button" color="explicitWhite" className={styles.payButton} disabled={disabled} onClick={onClick}>
{children}
</Row>
)
}
const Warning = ({ children }: PropsWithChildren<unknown>) => {
if (!children) {
return null
}
return (
<WarningText fontSize="14px" lineHeight="20px">
<WarningIcon />
{children}
</WarningText>
)
}
interface BagFooterProps {
isConnected: boolean
sufficientBalance: boolean
totalEthPrice: BigNumber
totalUsdPrice: number | undefined
bagStatus: BagStatus
fetchAssets: () => void
assetsAreInReview: boolean
eventProperties: Record<string, unknown>
}
@ -61,26 +89,72 @@ const PENDING_BAG_STATUSES = [
]
export const BagFooter = ({
isConnected,
sufficientBalance,
totalEthPrice,
totalUsdPrice,
bagStatus,
fetchAssets,
assetsAreInReview,
eventProperties,
}: BagFooterProps) => {
const toggleWalletModal = useToggleWalletModal()
const walletModalIsOpen = useModalIsOpen(ApplicationModal.WALLET)
const { account, chainId, connector } = useWeb3React()
const connected = Boolean(account && chainId)
const handleClick = useCallback(() => {
if (!connected) {
toggleWalletModal()
} else if (connected && chainId !== SupportedChainId.MAINNET) {
switchChain(connector, SupportedChainId.MAINNET)
} else {
fetchAssets()
}
}, [connected, chainId, toggleWalletModal, connector, fetchAssets])
const { balance: balanceInEth } = useWalletBalance()
const sufficientBalance = useMemo(() => {
if (!connected || chainId !== SupportedChainId.MAINNET) {
return undefined
}
return parseEther(balanceInEth).gte(totalEthPrice)
}, [connected, chainId, balanceInEth, totalEthPrice])
const { buttonText, disabled, warningText } = useMemo(() => {
let buttonText = <Trans>Something went wrong</Trans>
let disabled = true
let warningText = null
if (connected && chainId !== SupportedChainId.MAINNET) {
buttonText = <Trans>Switch networks</Trans>
disabled = false
warningText = <Trans>Wrong network</Trans>
} else if (sufficientBalance === false) {
buttonText = <Trans>Pay</Trans>
disabled = true
warningText = <Trans>Insufficient funds</Trans>
} else if (bagStatus === BagStatus.WARNING) {
warningText = <Trans>Something went wrong. Please try again.</Trans>
} else if (!connected || walletModalIsOpen) {
disabled = false
buttonText = <Trans>Connect wallet</Trans>
} else if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
disabled = true
buttonText = <Trans>Proceed in wallet</Trans>
} else if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
disabled = true
buttonText = <Trans>Transaction pending</Trans>
} else if (sufficientBalance === true) {
disabled = false
buttonText = <Trans>Pay</Trans>
}
return { buttonText, disabled, warningText }
}, [bagStatus, chainId, connected, sufficientBalance, walletModalIsOpen])
const isPending = PENDING_BAG_STATUSES.includes(bagStatus) || walletModalIsOpen
const isDisabled = isConnected && (isPending || !sufficientBalance || assetsAreInReview)
const showWarning = isConnected && (!sufficientBalance || bagStatus === BagStatus.WARNING)
return (
<Column className={styles.footerContainer}>
<Footer $showWarning={showWarning}>
<Footer $showWarning={bagStatus === BagStatus.WARNING}>
<Column gap="4" paddingTop="8" paddingBottom="20">
<Row justifyContent="space-between">
<Box>
@ -96,45 +170,18 @@ export const BagFooter = ({
{`${ethNumberStandardFormatter(totalUsdPrice, true)}`}
</Row>
</Column>
{showWarning && (
<WarningText fontSize="14px" lineHeight="20px">
<WarningIcon />
{!sufficientBalance ? (
<Trans>Insufficient funds</Trans>
) : (
<Trans>Something went wrong. Please try again.</Trans>
)}
</WarningText>
)}
<TraceEvent
events={[Event.onClick]}
name={EventName.NFT_BUY_BAG_PAY}
element={ElementName.NFT_BUY_BAG_PAY_BUTTON}
properties={{ ...eventProperties }}
shouldLogImpression={isConnected && !isDisabled}
shouldLogImpression={connected && !disabled}
>
<Row
as="button"
color="explicitWhite"
className={styles.payButton}
disabled={isDisabled}
onClick={() => {
if (!isConnected) {
toggleWalletModal()
} else {
fetchAssets()
}
}}
>
<Warning>{warningText}</Warning>
<ActionButton onClick={handleClick} disabled={disabled}>
{isPending && <Loader size="20px" stroke="white" />}
{!isConnected || walletModalIsOpen
? 'Connect wallet'
: bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET
? 'Proceed in wallet'
: bagStatus === BagStatus.PROCESSING_TRANSACTION
? 'Transaction pending'
: 'Pay'}
</Row>
{buttonText}
</ActionButton>
</TraceEvent>
</Footer>
</Column>