Commit 3f169adc authored by Jack Short's avatar Jack Short Committed by GitHub

chore: cleaning up buy button states (#6618)

parent 774368f3
This diff is collapsed.
...@@ -40,6 +40,8 @@ import { ThemedText } from 'theme' ...@@ -40,6 +40,8 @@ import { ThemedText } from 'theme'
import { switchChain } from 'utils/switchChain' import { switchChain } from 'utils/switchChain'
import { shallow } from 'zustand/shallow' import { shallow } from 'zustand/shallow'
import { BuyButtonStateData, BuyButtonStates, getBuyButtonStateData } from './ButtonStates'
const FooterContainer = styled.div` const FooterContainer = styled.div`
padding: 0px 12px; padding: 0px 12px;
` `
...@@ -98,7 +100,7 @@ const CurrencyInput = styled(Row)` ...@@ -98,7 +100,7 @@ const CurrencyInput = styled(Row)`
cursor: pointer; cursor: pointer;
` `
const PayButton = styled.button<{ $backgroundColor: string; $color: string }>` const ActionButton = styled.button<{ $backgroundColor: string; $color: string }>`
display: flex; display: flex;
background: ${({ $backgroundColor }) => $backgroundColor}; background: ${({ $backgroundColor }) => $backgroundColor};
color: ${({ $color }) => $color}; color: ${({ $color }) => $color};
...@@ -149,27 +151,6 @@ const ValueText = styled(ThemedText.BodyPrimary)` ...@@ -149,27 +151,6 @@ const ValueText = styled(ThemedText.BodyPrimary)`
} }
` `
interface ActionButtonProps {
disabled?: boolean
onClick: () => void
backgroundColor: string
textColor: string
}
const ActionButton = ({
disabled,
children,
onClick,
backgroundColor,
textColor,
}: PropsWithChildren<ActionButtonProps>) => {
return (
<PayButton disabled={disabled} onClick={onClick} $backgroundColor={backgroundColor} $color={textColor}>
{children}
</PayButton>
)
}
interface HelperTextProps { interface HelperTextProps {
color: string color: string
} }
...@@ -179,7 +160,7 @@ const Warning = ({ color, children }: PropsWithChildren<HelperTextProps>) => { ...@@ -179,7 +160,7 @@ const Warning = ({ color, children }: PropsWithChildren<HelperTextProps>) => {
return null return null
} }
return ( return (
<WarningText fontSize="14px" lineHeight="20px" $color={color}> <WarningText data-testid="nft-buy-button-warning" fontSize="14px" lineHeight="20px" $color={color}>
<WarningIcon /> <WarningIcon />
{children} {children}
</WarningText> </WarningText>
...@@ -274,11 +255,6 @@ const FiatValue = ({ ...@@ -274,11 +255,6 @@ const FiatValue = ({
) )
} }
interface BagFooterProps {
setModalIsOpen: (open: boolean) => void
eventProperties: Record<string, unknown>
}
const PENDING_BAG_STATUSES = [ const PENDING_BAG_STATUSES = [
BagStatus.FETCHING_ROUTE, BagStatus.FETCHING_ROUTE,
BagStatus.CONFIRMING_IN_WALLET, BagStatus.CONFIRMING_IN_WALLET,
...@@ -286,13 +262,18 @@ const PENDING_BAG_STATUSES = [ ...@@ -286,13 +262,18 @@ const PENDING_BAG_STATUSES = [
BagStatus.PROCESSING_TRANSACTION, BagStatus.PROCESSING_TRANSACTION,
] ]
interface BagFooterProps {
setModalIsOpen: (open: boolean) => void
eventProperties: Record<string, unknown>
}
export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) => { export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) => {
const toggleWalletDrawer = useToggleAccountDrawer() const toggleWalletDrawer = useToggleAccountDrawer()
const theme = useTheme() const theme = useTheme()
const { account, chainId, connector } = useWeb3React() const { account, chainId, connector } = useWeb3React()
const connected = Boolean(account && chainId) const connected = Boolean(account && chainId)
const totalEthPrice = useBagTotalEthPrice() const totalEthPrice = useBagTotalEthPrice()
const inputCurrency = useTokenInput((state) => state.inputCurrency) const { inputCurrency } = useTokenInput(({ inputCurrency }) => ({ inputCurrency }), shallow)
const setInputCurrency = useTokenInput((state) => state.setInputCurrency) const setInputCurrency = useTokenInput((state) => state.setInputCurrency)
const defaultCurrency = useCurrency('ETH') const defaultCurrency = useCurrency('ETH')
const inputCurrencyBalance = useTokenBalance( const inputCurrencyBalance = useTokenBalance(
...@@ -377,105 +358,78 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = ...@@ -377,105 +358,78 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
helperTextColor, helperTextColor,
handleClick, handleClick,
buttonColor, buttonColor,
} = useMemo(() => { } = useMemo((): BuyButtonStateData => {
let handleClick: (() => void) | (() => Promise<void>) = fetchAssets
let buttonText = <Trans>Something went wrong</Trans>
let disabled = true
let warningText = undefined
let warningTextColor = theme.accentWarning
let helperText = undefined
let helperTextColor = theme.textSecondary
let buttonColor = theme.accentAction
let buttonTextColor = theme.accentTextLightPrimary
if (connected && chainId !== SupportedChainId.MAINNET) { if (connected && chainId !== SupportedChainId.MAINNET) {
handleClick = () => switchChain(connector, SupportedChainId.MAINNET) const handleClick = () => switchChain(connector, SupportedChainId.MAINNET)
buttonText = <Trans>Switch networks</Trans> return getBuyButtonStateData(BuyButtonStates.NOT_SUPPORTED_CHAIN, theme, handleClick)
disabled = false }
warningText = <Trans>Wrong network</Trans>
} else if (sufficientBalance === false) { if (sufficientBalance === false) {
buttonText = <Trans>Pay</Trans> return getBuyButtonStateData(BuyButtonStates.INSUFFICIENT_BALANCE, theme)
disabled = true }
warningText = <Trans>Insufficient funds</Trans>
} else if (bagStatus === BagStatus.WARNING) { if (bagStatus === BagStatus.WARNING) {
warningText = <Trans>Something went wrong. Please try again.</Trans> return getBuyButtonStateData(BuyButtonStates.ERROR, theme)
} else if (!connected) { }
handleClick = () => {
if (!connected) {
const handleClick = () => {
toggleWalletDrawer() toggleWalletDrawer()
setBagExpanded({ bagExpanded: false }) setBagExpanded({ bagExpanded: false })
} }
disabled = false return getBuyButtonStateData(BuyButtonStates.WALLET_NOT_CONNECTED, theme, handleClick)
buttonText = <Trans>Connect wallet</Trans> }
} else if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
disabled = true if (bagStatus === BagStatus.FETCHING_FINAL_ROUTE || bagStatus === BagStatus.CONFIRMING_IN_WALLET) {
buttonText = <Trans>Proceed in wallet</Trans> return getBuyButtonStateData(BuyButtonStates.IN_WALLET_CONFIRMATION, theme)
} else if (bagStatus === BagStatus.PROCESSING_TRANSACTION) { }
disabled = true
buttonText = <Trans>Transaction pending</Trans> if (bagStatus === BagStatus.PROCESSING_TRANSACTION) {
} else if (usingPayWithAnyToken && tradeState !== TradeState.VALID) { return getBuyButtonStateData(BuyButtonStates.PROCESSING_TRANSACTION, theme)
disabled = true }
buttonText = <Trans>Fetching Route</Trans>
if (usingPayWithAnyToken && tradeState !== TradeState.VALID) {
if (tradeState === TradeState.INVALID) { if (tradeState === TradeState.INVALID) {
buttonText = <Trans>Pay</Trans> return getBuyButtonStateData(BuyButtonStates.INVALID_TOKEN_ROUTE, theme)
} }
if (tradeState === TradeState.NO_ROUTE_FOUND) { if (tradeState === TradeState.NO_ROUTE_FOUND) {
buttonText = <Trans>Insufficient liquidity</Trans> return getBuyButtonStateData(BuyButtonStates.NO_TOKEN_ROUTE_FOUND, theme)
buttonColor = theme.backgroundInteractive
buttonTextColor = theme.textPrimary
helperText = <Trans>Insufficient pool liquidity to complete transaction</Trans>
} }
} else if (allowance.state === AllowanceState.REQUIRED || loadingAllowance) {
handleClick = () => updateAllowance() return getBuyButtonStateData(BuyButtonStates.FETCHING_TOKEN_ROUTE, theme)
disabled = isAllowancePending || isApprovalLoading || loadingAllowance }
if (allowance.state === AllowanceState.REQUIRED || loadingAllowance) {
const handleClick = () => updateAllowance()
if (loadingAllowance) { if (loadingAllowance) {
buttonText = <Trans>Loading Allowance</Trans> return getBuyButtonStateData(BuyButtonStates.LOADING_ALLOWANCE, theme, handleClick)
} else if (isAllowancePending) { } else if (isAllowancePending) {
buttonText = <Trans>Approve in your wallet</Trans> return getBuyButtonStateData(BuyButtonStates.IN_WALLET_ALLOWANCE_APPROVAL, theme, handleClick)
} else if (isApprovalLoading) { } else if (isApprovalLoading) {
buttonText = <Trans>Approval pending</Trans> return getBuyButtonStateData(BuyButtonStates.PROCESSING_APPROVAL, theme, handleClick)
} else {
helperText = <Trans>An approval is needed to use this token. </Trans>
buttonText = <Trans>Approve</Trans>
} }
} else if (bagStatus === BagStatus.CONFIRM_QUOTE) {
disabled = false return getBuyButtonStateData(BuyButtonStates.REQUIRE_APPROVAL, theme, handleClick)
warningTextColor = theme.accentAction
warningText = <Trans>Price updated</Trans>
buttonText = <Trans>Pay</Trans>
} else if (priceImpact && priceImpact.priceImpactSeverity.type === 'error') {
disabled = false
buttonColor = priceImpact.priceImpactSeverity.color
helperText = <Trans>Price impact warning</Trans>
helperTextColor = priceImpact.priceImpactSeverity.color
buttonText = <Trans>Pay Anyway</Trans>
} else if (sufficientBalance === true) {
disabled = false
buttonText = <Trans>Pay</Trans>
helperText = usingPayWithAnyToken ? <Trans>Refunds for unavailable items will be given in ETH</Trans> : undefined
} }
return { if (bagStatus === BagStatus.CONFIRM_QUOTE) {
buttonText, return getBuyButtonStateData(BuyButtonStates.CONFIRM_UPDATED_PRICE, theme, fetchAssets)
buttonTextColor,
disabled,
warningText,
warningTextColor,
helperText,
helperTextColor,
handleClick,
buttonColor,
} }
if (priceImpact && priceImpact.priceImpactSeverity.type === 'error') {
return getBuyButtonStateData(
BuyButtonStates.PRICE_IMPACT_HIGH,
theme,
fetchAssets,
usingPayWithAnyToken,
priceImpact
)
}
return getBuyButtonStateData(BuyButtonStates.PAY, theme, fetchAssets, usingPayWithAnyToken)
}, [ }, [
fetchAssets,
theme.accentWarning,
theme.textSecondary,
theme.accentAction,
theme.accentTextLightPrimary,
theme.backgroundInteractive,
theme.textPrimary,
connected, connected,
chainId, chainId,
sufficientBalance, sufficientBalance,
...@@ -485,6 +439,8 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = ...@@ -485,6 +439,8 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
allowance.state, allowance.state,
loadingAllowance, loadingAllowance,
priceImpact, priceImpact,
theme,
fetchAssets,
connector, connector,
toggleWalletDrawer, toggleWalletDrawer,
setBagExpanded, setBagExpanded,
...@@ -553,10 +509,11 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = ...@@ -553,10 +509,11 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) =
<Warning color={warningTextColor}>{warningText}</Warning> <Warning color={warningTextColor}>{warningText}</Warning>
<Helper color={helperTextColor}>{helperText}</Helper> <Helper color={helperTextColor}>{helperText}</Helper>
<ActionButton <ActionButton
data-testid="nft-buy-button"
onClick={handleClick} onClick={handleClick}
disabled={disabled || isPending} disabled={disabled || isPending}
backgroundColor={buttonColor} $backgroundColor={buttonColor}
textColor={buttonTextColor} $color={buttonTextColor}
> >
{isPending && <Loader size="20px" stroke="white" />} {isPending && <Loader size="20px" stroke="white" />}
{buttonText} {buttonText}
......
import { Trans } from '@lingui/macro'
import { PriceImpact } from 'nft/hooks/usePriceImpact'
import { ReactNode } from 'react'
import { DefaultTheme } from 'styled-components/macro'
export enum BuyButtonStates {
WALLET_NOT_CONNECTED,
NOT_SUPPORTED_CHAIN,
INSUFFICIENT_BALANCE,
ERROR,
IN_WALLET_CONFIRMATION,
PROCESSING_TRANSACTION,
FETCHING_TOKEN_ROUTE,
INVALID_TOKEN_ROUTE,
NO_TOKEN_ROUTE_FOUND,
LOADING_ALLOWANCE,
IN_WALLET_ALLOWANCE_APPROVAL,
PROCESSING_APPROVAL,
REQUIRE_APPROVAL,
CONFIRM_UPDATED_PRICE,
PRICE_IMPACT_HIGH,
PAY,
}
export interface BuyButtonStateData {
handleClick: (() => void) | (() => Promise<void>)
buttonText: ReactNode
disabled: boolean
warningText?: ReactNode
warningTextColor: string
helperText?: ReactNode
helperTextColor: string
buttonColor: string
buttonTextColor: string
}
export function getBuyButtonStateData(
buyButtonState: BuyButtonStates,
theme: DefaultTheme,
handleClickOverride?: (() => void) | (() => Promise<void>),
usingPayWithAnyToken?: boolean,
priceImpact?: PriceImpact
): BuyButtonStateData {
const defaultBuyButtonState: BuyButtonStateData = {
handleClick: () => undefined,
buttonText: <Trans>Something went wrong</Trans>,
disabled: true,
warningText: undefined,
warningTextColor: theme.accentWarning,
helperText: undefined,
helperTextColor: theme.textSecondary,
buttonColor: theme.accentAction,
buttonTextColor: theme.accentTextLightPrimary,
}
const buyButtonStateData: Record<BuyButtonStates, BuyButtonStateData> = {
[BuyButtonStates.WALLET_NOT_CONNECTED]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonText: <Trans>Connect wallet</Trans>,
},
[BuyButtonStates.NOT_SUPPORTED_CHAIN]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
buttonText: <Trans>Switch networks</Trans>,
disabled: false,
warningText: <Trans>Wrong network</Trans>,
},
[BuyButtonStates.INSUFFICIENT_BALANCE]: {
...defaultBuyButtonState,
buttonText: <Trans>Pay</Trans>,
warningText: <Trans>Insufficient funds</Trans>,
},
[BuyButtonStates.ERROR]: {
...defaultBuyButtonState,
warningText: <Trans>Something went wrong. Please try again.</Trans>,
},
[BuyButtonStates.IN_WALLET_CONFIRMATION]: {
...defaultBuyButtonState,
buttonText: <Trans>Proceed in wallet</Trans>,
},
[BuyButtonStates.PROCESSING_TRANSACTION]: {
...defaultBuyButtonState,
buttonText: <Trans>Transaction pending</Trans>,
},
[BuyButtonStates.FETCHING_TOKEN_ROUTE]: {
...defaultBuyButtonState,
buttonText: <Trans>Fetching Route</Trans>,
},
[BuyButtonStates.INVALID_TOKEN_ROUTE]: {
...defaultBuyButtonState,
buttonText: <Trans>Pay</Trans>,
},
[BuyButtonStates.NO_TOKEN_ROUTE_FOUND]: {
...defaultBuyButtonState,
buttonText: <Trans>Insufficient liquidity</Trans>,
buttonColor: theme.backgroundInteractive,
buttonTextColor: theme.textPrimary,
helperText: <Trans>Insufficient pool liquidity to complete transaction</Trans>,
},
[BuyButtonStates.LOADING_ALLOWANCE]: {
...defaultBuyButtonState,
buttonText: <Trans>Loading Allowance</Trans>,
},
[BuyButtonStates.IN_WALLET_ALLOWANCE_APPROVAL]: {
...defaultBuyButtonState,
buttonText: <Trans>Approve in your wallet</Trans>,
},
[BuyButtonStates.PROCESSING_APPROVAL]: {
...defaultBuyButtonState,
buttonText: <Trans>Approval pending</Trans>,
},
[BuyButtonStates.REQUIRE_APPROVAL]: {
...defaultBuyButtonState,
disabled: false,
handleClick: handleClickOverride ?? (() => undefined),
helperText: <Trans>An approval is needed to use this token. </Trans>,
buttonText: <Trans>Approve</Trans>,
},
[BuyButtonStates.CONFIRM_UPDATED_PRICE]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
warningTextColor: theme.accentAction,
warningText: <Trans>Price updated</Trans>,
buttonText: <Trans>Pay</Trans>,
},
[BuyButtonStates.PRICE_IMPACT_HIGH]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonColor: priceImpact ? priceImpact.priceImpactSeverity.color : defaultBuyButtonState.buttonColor,
helperText: <Trans>Price impact warning</Trans>,
helperTextColor: priceImpact ? priceImpact.priceImpactSeverity.color : defaultBuyButtonState.helperTextColor,
buttonText: <Trans>Pay Anyway</Trans>,
},
[BuyButtonStates.PAY]: {
...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined),
disabled: false,
buttonText: <Trans>Pay</Trans>,
helperText: usingPayWithAnyToken ? <Trans>Refunds for unavailable items will be given in ETH</Trans> : undefined,
},
}
return buyButtonStateData[buyButtonState]
}
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