Commit 94dc3898 authored by cartcrom's avatar cartcrom Committed by GitHub

feat: widget speedbumps on swap review (#4544)

* initial commit
* finished feature
* addressed PR comments
parent 4a8c621f
import Modal from '../Modal'
import TokenSafety from '.'
import TokenSafety, { TokenSafetyProps } from '.'
interface TokenSafetyModalProps {
interface TokenSafetyModalProps extends TokenSafetyProps {
isOpen: boolean
tokenAddress: string | null
secondTokenAddress?: string
onContinue: () => void
onCancel: () => void
showCancel?: boolean
}
export default function TokenSafetyModal({
......@@ -16,6 +11,7 @@ export default function TokenSafetyModal({
secondTokenAddress,
onContinue,
onCancel,
onBlocked,
showCancel,
}: TokenSafetyModalProps) {
return (
......@@ -23,8 +19,9 @@ export default function TokenSafetyModal({
<TokenSafety
tokenAddress={tokenAddress}
secondTokenAddress={secondTokenAddress}
onCancel={onCancel}
onContinue={onContinue}
onBlocked={onBlocked}
onCancel={onCancel}
showCancel={showCancel}
/>
</Modal>
......
......@@ -73,11 +73,13 @@ const Buttons = ({
warning,
onContinue,
onCancel,
onBlocked,
showCancel,
}: {
warning: Warning
onContinue: () => void
onCancel: () => void
onBlocked?: () => void
showCancel?: boolean
}) => {
return warning.canProceed ? (
......@@ -88,7 +90,7 @@ const Buttons = ({
{showCancel && <StyledCancelButton onClick={onCancel}>Cancel</StyledCancelButton>}
</>
) : (
<StyledCloseButton onClick={onCancel}>
<StyledCloseButton onClick={onBlocked ?? onCancel}>
<Trans>Close</Trans>
</StyledCloseButton>
)
......@@ -184,11 +186,12 @@ const StyledExternalLink = styled(ExternalLink)`
font-weight: 600;
`
interface TokenSafetyProps {
export interface TokenSafetyProps {
tokenAddress: string | null
secondTokenAddress?: string
onContinue: () => void
onCancel: () => void
onBlocked?: () => void
showCancel?: boolean
}
......@@ -197,6 +200,7 @@ export default function TokenSafety({
secondTokenAddress,
onContinue,
onCancel,
onBlocked,
showCancel,
}: TokenSafetyProps) {
const logos = []
......
......@@ -4,18 +4,16 @@ import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/CurrencyLogo'
import PriceChart from 'components/Tokens/TokenDetails/PriceChart'
import { VerifiedIcon } from 'components/TokenSafety/TokenSafetyIcon'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import { getChainInfo } from 'constants/chainInfo'
import { nativeOnChain, WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { checkWarning, WARNING_LEVEL } from 'constants/tokenSafety'
import { checkWarning } from 'constants/tokenSafety'
import { chainIdToChainName, useTokenDetailQuery } from 'graphql/data/TokenDetailQuery'
import { useCurrency, useIsUserAddedToken, useToken } from 'hooks/Tokens'
import { useCurrency, useToken } from 'hooks/Tokens'
import { useAtomValue } from 'jotai/utils'
import { darken } from 'polished'
import { Suspense, useCallback } from 'react'
import { Suspense } from 'react'
import { useState } from 'react'
import { ArrowLeft } from 'react-feather'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { CopyContractAddress } from 'theme'
import { formatDollarAmount } from 'utils/formatDollarAmt'
......@@ -178,17 +176,6 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
const isFavorited = useIsFavorited(address)
const toggleFavorite = useToggleFavorite(address)
const warning = checkWarning(address)
const navigate = useNavigate()
const isUserAddedToken = useIsUserAddedToken(token)
const [warningModalOpen, setWarningModalOpen] = useState(!!warning && !isUserAddedToken)
const handleDismissWarning = useCallback(() => {
setWarningModalOpen(false)
}, [setWarningModalOpen])
const handleCancel = useCallback(() => {
setWarningModalOpen(false)
warning && warning.level === WARNING_LEVEL.BLOCKED && navigate(-1)
}, [setWarningModalOpen, navigate, warning])
const chainInfo = getChainInfo(token?.chainId)
const networkLabel = chainInfo?.label
const networkBadgebackgroundColor = chainInfo?.backgroundColor
......@@ -286,12 +273,6 @@ export default function LoadedTokenDetail({ address }: { address: string }) {
</ContractAddress>
</Contract>
</ContractAddressSection>
<TokenSafetyModal
isOpen={warningModalOpen}
tokenAddress={address}
onCancel={handleCancel}
onContinue={handleDismissWarning}
/>
</TopArea>
</Suspense>
)
......
import { Currency, SwapWidget } from '@uniswap/widgets'
import { Currency, OnReviewSwapClick, SwapWidget } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import { RPC_URLS } from 'constants/networks'
import { useActiveLocale } from 'hooks/useActiveLocale'
......@@ -16,9 +16,10 @@ const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
export interface WidgetProps {
defaultToken?: Currency
onReviewSwapClick?: OnReviewSwapClick
}
export default function Widget({ defaultToken }: WidgetProps) {
export default function Widget({ defaultToken, onReviewSwapClick }: WidgetProps) {
const locale = useActiveLocale()
const darkMode = useIsDarkMode()
const theme = useMemo(() => (darkMode ? DARK_THEME : LIGHT_THEME), [darkMode])
......@@ -38,6 +39,7 @@ export default function Widget({ defaultToken }: WidgetProps) {
width={WIDGET_WIDTH}
locale={locale}
theme={theme}
onReviewSwapClick={onReviewSwapClick}
// defaultChainId is excluded - it is always inferred from the passed provider
provider={provider}
{...inputs}
......
......@@ -11,14 +11,15 @@ import LoadingTokenDetail from 'components/Tokens/TokenDetails/LoadingTokenDetai
import NetworkBalance from 'components/Tokens/TokenDetails/NetworkBalance'
import TokenDetail from 'components/Tokens/TokenDetails/TokenDetail'
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget, { WIDGET_WIDTH } from 'components/Widget'
import { getChainInfo } from 'constants/chainInfo'
import { L1_CHAIN_IDS, L2_CHAIN_IDS, SupportedChainId, TESTNET_CHAIN_IDS } from 'constants/chains'
import { checkWarning } from 'constants/tokenSafety'
import { useToken } from 'hooks/Tokens'
import { useIsUserAddedToken, useToken } from 'hooks/Tokens'
import { useNetworkTokenBalances } from 'hooks/useNetworkTokenBalances'
import { useMemo } from 'react'
import { Navigate, useLocation, useParams } from 'react-router-dom'
import { useCallback, useMemo, useState } from 'react'
import { Navigate, useLocation, useNavigate, useParams } from 'react-router-dom'
import styled from 'styled-components/macro'
const Footer = styled.div`
......@@ -79,10 +80,28 @@ export default function TokenDetails() {
const location = useLocation()
const { tokenAddress } = useParams<{ tokenAddress?: string }>()
const token = useToken(tokenAddress)
const tokenWarning = tokenAddress ? checkWarning(tokenAddress) : null
const isBlockedToken = tokenWarning?.canProceed === false
const navigate = useNavigate()
const [continueSwap, setContinueSwap] = useState<{ resolve: (value: boolean | PromiseLike<boolean>) => void }>()
const shouldShowSpeedbump = !useIsUserAddedToken(token) && tokenWarning !== null
// Show token safety modal if Swap-reviewing a warning token, at all times if the current token is blocked
const onReviewSwap = useCallback(() => {
return new Promise<boolean>((resolve) => {
shouldShowSpeedbump ? setContinueSwap({ resolve }) : resolve(true)
})
}, [shouldShowSpeedbump])
const onResolveSwap = useCallback(
(value: boolean) => {
continueSwap?.resolve(value)
setContinueSwap(undefined)
},
[continueSwap, setContinueSwap]
)
const tokenWarning = token ? checkWarning(token.address) : null
/* network balance handling */
const { data: networkData } = NetworkBalances(token?.address)
const { chainId: connectedChainId } = useWeb3React()
const totalBalance = 4.3 // dummy data
......@@ -128,7 +147,7 @@ export default function TokenDetails() {
<>
<TokenDetail address={token.address} />
<RightPanel>
<Widget defaultToken={token ?? undefined} />
<Widget defaultToken={token ?? undefined} onReviewSwapClick={onReviewSwap} />
{tokenWarning && <TokenSafetyMessage tokenAddress={token.address} warning={tokenWarning} />}
<BalanceSummary address={token.address} />
</RightPanel>
......@@ -139,6 +158,14 @@ export default function TokenDetails() {
networkBalances={balancesByNetwork}
/>
</Footer>
<TokenSafetyModal
isOpen={isBlockedToken || !!continueSwap}
tokenAddress={token.address}
onContinue={() => onResolveSwap(true)}
onBlocked={() => navigate(-1)}
onCancel={() => onResolveSwap(false)}
showCancel={true}
/>
</>
)}
</TokenDetailsLayout>
......
......@@ -5103,9 +5103,9 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
uri-js "^4.2.2"
ajv@^8.0.1:
version "8.6.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571"
integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==
version "8.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
......
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