feat: remove buy button and landing terminology for uk (#7386)

* feat: remove buy button and landing terminology for uk

* removing tarballs

* mocked setup

* setting compliance to gb

* turning back on defaults

* cache for user

* moving to hook and grid sizing

* fixing tests

* comments

* landinage page cards

* cypress test

* removing extra store
This commit is contained in:
Jack Short 2023-09-29 11:32:05 -05:00 committed by GitHub
parent c7a8e9e5a7
commit af80079957
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 230 additions and 108 deletions

@ -27,7 +27,10 @@ beforeEach(() => {
server_upload_time: Date.now(),
payload_size_bytes: byteSize,
events_ingested: req.body.events.length,
})
}),
{
'origin-country': 'US',
}
)
}).intercept('https://*.sentry.io', { statusCode: 200 })

@ -190,7 +190,7 @@
"@sentry/tracing": "^7.45.0",
"@sentry/types": "^7.45.0",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "^1.4.0",
"@uniswap/analytics": "1.5.0",
"@uniswap/analytics-events": "^2.24.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",

@ -52,3 +52,8 @@ export const sendAnalyticsEvent: typeof sendAnalyticsTraceEvent = (event, proper
sendAnalyticsTraceEvent(event, properties)
}
}
// This is only used for initial page load so we can get the user's country
export const sendInitializationEvent: typeof sendAnalyticsTraceEvent = (event, properties) => {
sendAnalyticsTraceEvent(event, properties)
}

@ -14,6 +14,7 @@ import Tooltip from 'components/Tooltip'
import { getConnection } from 'connection'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import useENSName from 'hooks/useENSName'
import { useIsNotOriginCountry } from 'hooks/useIsNotOriginCountry'
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { ProfilePageStateType } from 'nft/types'
@ -159,6 +160,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
const resetSellAssets = useSellAsset((state) => state.reset)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const shouldShowBuyFiatButton = useIsNotOriginCountry('GB')
const { formatNumber } = useFormatter()
const shouldDisableNFTRoutes = useDisableNFTRoutes()
@ -304,26 +306,28 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
<Trans>View and sell NFTs</Trans>
</HeaderButton>
)}
<HeaderButton
size={ButtonSize.medium}
emphasis={ButtonEmphasis.highSoft}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
data-testid="wallet-buy-crypto"
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</HeaderButton>
{shouldShowBuyFiatButton && (
<HeaderButton
size={ButtonSize.medium}
emphasis={ButtonEmphasis.highSoft}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
data-testid="wallet-buy-crypto"
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</HeaderButton>
)}
{Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && (
<FiatOnrampNotAvailableText marginTop="8px">
<Trans>Not available in your region</Trans>

@ -1,6 +1,8 @@
import userEvent from '@testing-library/user-event'
import { useWeb3React } from '@web3-react/core'
import { useAccountDrawer } from 'components/AccountDrawer'
import store from 'state'
import { setOriginCountry } from 'state/user/reducer'
import { mocked } from 'test-utils/mocked'
import { act, fireEvent, render, screen } from 'test-utils/render'
@ -45,6 +47,7 @@ describe('SwapBuyFiatButton.tsx', () => {
let useOpenModal: jest.Mock<any, any>
beforeAll(() => {
store.dispatch(setOriginCountry('US'))
toggleWalletDrawer = jest.fn()
useOpenModal = jest.fn()
})

@ -5,9 +5,11 @@ import { TraceEvent } from 'analytics'
import { useAccountDrawer } from 'components/AccountDrawer'
import { ButtonText } from 'components/Button'
import { MouseoverTooltip } from 'components/Tooltip'
import { useIsNotOriginCountry } from 'hooks/useIsNotOriginCountry'
import { useCallback, useEffect, useState } from 'react'
import styled from 'styled-components'
import { ExternalLink } from 'theme/components'
import { textFadeIn } from 'theme/styles'
import { useFiatOnrampAvailability, useOpenModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
@ -25,6 +27,7 @@ enum BuyFiatFlowState {
}
const StyledTextButton = styled(ButtonText)`
${textFadeIn}
color: ${({ theme }) => theme.neutral2};
gap: 4px;
font-weight: 485;
@ -39,6 +42,7 @@ const StyledTextButton = styled(ButtonText)`
export default function SwapBuyFiatButton() {
const { account } = useWeb3React()
const openFiatOnRampModal = useOpenModal(ApplicationModal.FIAT_ONRAMP)
const shouldShowBuyFiatButton = useIsNotOriginCountry('GB')
const [checkFiatRegionAvailability, setCheckFiatRegionAvailability] = useState(false)
const {
available: fiatOnrampAvailable,
@ -97,6 +101,10 @@ export default function SwapBuyFiatButton() {
const fiatOnRampsUnavailableTooltipDisabled =
!fiatOnrampAvailabilityChecked || (fiatOnrampAvailabilityChecked && fiatOnrampAvailable)
if (!shouldShowBuyFiatButton) {
return null
}
return (
<MouseoverTooltip
text={

@ -117,6 +117,8 @@ exports[`SwapBuyFiatButton.tsx matches base snapshot 1`] = `
}
.c4 {
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
color: #7D7D7D;
gap: 4px;
font-weight: 485;

@ -0,0 +1,7 @@
import { useAppSelector } from 'state/hooks'
import { AppState } from 'state/reducer'
export function useIsNotOriginCountry(country: string) {
const originCountry = useAppSelector((state: AppState) => state.user.originCountry)
return Boolean(originCountry) && originCountry !== country
}

@ -1,6 +1,6 @@
import { CustomUserProperties, getBrowser, SharedEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { getDeviceId, sendAnalyticsEvent, Trace, user } from 'analytics'
import { getDeviceId, sendAnalyticsEvent, sendInitializationEvent, Trace, user } from 'analytics'
import ErrorBoundary from 'components/ErrorBoundary'
import Loader from 'components/Icons/LoadingSpinner'
import NavBar, { PageTabs } from 'components/NavBar'
@ -149,7 +149,7 @@ export default function App() {
const serviceWorkerProperty = isServiceWorkerInstalled ? (isServiceWorkerHit ? 'hit' : 'miss') : 'uninstalled'
const pageLoadProperties = { service_worker: serviceWorkerProperty }
sendAnalyticsEvent(SharedEventName.APP_LOADED, pageLoadProperties)
sendInitializationEvent(SharedEventName.APP_LOADED, pageLoadProperties)
const sendWebVital =
(metric: string) =>
({ delta }: Metric) =>

@ -1152,6 +1152,8 @@ exports[`disable nft on landing page does not render nft information and card 1`
}
.c18 {
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
color: #7D7D7D;
gap: 4px;
font-weight: 485;
@ -1345,6 +1347,26 @@ exports[`disable nft on landing page does not render nft information and card 1`
pointer-events: auto;
}
.c64 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: #7D7D7D;
-webkit-text-decoration: none;
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
}
.c64:hover {
color: #CECECE;
}
.c55 {
color: transparent;
font-size: 36px;
@ -1355,6 +1377,8 @@ exports[`disable nft on landing page does not render nft information and card 1`
background: linear-gradient(10deg,rgba(255,79,184,1) 0%,rgba(255,159,251,1) 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
}
.c57 {
@ -1376,6 +1400,8 @@ exports[`disable nft on landing page does not render nft information and card 1`
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
}
.c59 {
@ -1521,26 +1547,6 @@ exports[`disable nft on landing page does not render nft information and card 1`
width: 100%;
}
.c64 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: #7D7D7D;
-webkit-text-decoration: none;
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
}
.c64:hover {
color: #CECECE;
}
@media screen and (min-width:1024px) {
.c84 {
-webkit-flex-direction: row;
@ -3780,6 +3786,8 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
}
.c18 {
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
color: #7D7D7D;
gap: 4px;
font-weight: 485;
@ -3973,6 +3981,26 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
pointer-events: auto;
}
.c64 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: #7D7D7D;
-webkit-text-decoration: none;
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
}
.c64:hover {
color: #CECECE;
}
.c55 {
color: transparent;
font-size: 36px;
@ -3983,6 +4011,8 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
background: linear-gradient(10deg,rgba(255,79,184,1) 0%,rgba(255,159,251,1) 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
}
.c57 {
@ -4004,6 +4034,8 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
}
.c59 {
@ -4149,26 +4181,6 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
width: 100%;
}
.c64 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: #7D7D7D;
-webkit-text-decoration: none;
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
}
.c64:hover {
color: #CECECE;
}
@media screen and (min-width:1024px) {
.c85 {
-webkit-flex-direction: row;

@ -1,4 +1,6 @@
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import store from 'state'
import { setOriginCountry } from 'state/user/reducer'
import { mocked } from 'test-utils/mocked'
import { render } from 'test-utils/render'
@ -7,6 +9,10 @@ import Landing from '.'
jest.mock('hooks/useDisableNFTRoutes')
describe('disable nft on landing page', () => {
beforeAll(() => {
store.dispatch(setOriginCountry('US'))
})
it('renders nft information and card', () => {
mocked(useDisableNFTRoutes).mockReturnValue(false)
const { container } = render(<Landing />)

@ -16,10 +16,11 @@ import { ArrowDownCircle } from 'react-feather'
import { Navigate, useLocation, useNavigate } from 'react-router-dom'
import { Link as NativeLink } from 'react-router-dom'
import { useAppSelector } from 'state/hooks'
import { AppState } from 'state/reducer'
import styled, { css } from 'styled-components'
import { BREAKPOINTS } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { TRANSITION_DURATIONS } from 'theme/styles'
import { textFadeIn, TRANSITION_DURATIONS } from 'theme/styles'
import { Z_INDEX } from 'theme/zIndex'
import { getDownloadAppLinkProps } from 'utils/openDownloadApp'
@ -106,7 +107,23 @@ const ContentContainer = styled.div<{ isDarkMode: boolean }>`
}
`
const TitleText = styled.h1<{ isDarkMode: boolean }>`
const DownloadWalletLink = styled.a`
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: ${({ theme }) => theme.neutral2};
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
:hover {
color: ${({ theme }) => theme.neutral3};
}
`
const TitleText = styled.h1<{ isDarkMode: boolean; $visible: boolean }>`
color: transparent;
font-size: 36px;
line-height: 44px;
@ -124,6 +141,13 @@ const TitleText = styled.h1<{ isDarkMode: boolean }>`
background-clip: text;
-webkit-background-clip: text;
${({ $visible }) =>
$visible
? css`
${textFadeIn}
`
: 'opacity: 0;'}
@media screen and (min-width: ${BREAKPOINTS.sm}px) {
font-size: 48px;
line-height: 56px;
@ -150,9 +174,16 @@ const SubText = styled.div`
}
`
const SubTextContainer = styled.div`
const SubTextContainer = styled.div<{ $visible: boolean }>`
display: flex;
justify-content: center;
${({ $visible }) =>
$visible
? css`
${textFadeIn}
`
: 'opacity: 0;'}
`
const LandingButton = styled(BaseButton)`
@ -303,9 +334,36 @@ export default function Landing() {
const cardsRef = useRef<HTMLDivElement>(null)
const selectedWallet = useAppSelector((state) => state.user.selectedWallet)
const shouldDisableNFTRoutes = useDisableNFTRoutes()
const cards = useMemo(
() => MAIN_CARDS.filter((card) => !(shouldDisableNFTRoutes && card.to.startsWith('/nft'))),
[shouldDisableNFTRoutes]
const originCountry = useAppSelector((state: AppState) => state.user.originCountry)
const renderUkSpecificText = Boolean(originCountry) && originCountry === 'GB'
const cards = useMemo(() => {
const mainCards = MAIN_CARDS.filter(
(card) =>
!(shouldDisableNFTRoutes && card.to.startsWith('/nft')) && !(card.to.startsWith('/swap') && !originCountry)
)
mainCards.forEach((card) => {
if (card.to.startsWith('/swap') && renderUkSpecificText) {
card.description = 'Explore tokens on Ethereum, Polygon, Optimism and more '
card.cta = 'Discover Tokens'
}
})
return mainCards
}, [originCountry, renderUkSpecificText, shouldDisableNFTRoutes])
const extraCards = useMemo(
() =>
MORE_CARDS.filter(
(card) =>
!(
card.to.startsWith(
'https://support.uniswap.org/hc/en-us/articles/11306574799117-How-to-use-Moon-Pay-on-the-Uniswap-web-app-'
) &&
(!originCountry || renderUkSpecificText)
)
),
[originCountry, renderUkSpecificText]
)
const [accountDrawerOpen] = useAccountDrawer()
@ -320,6 +378,35 @@ export default function Landing() {
const location = useLocation()
const queryParams = parse(location.search, { ignoreQueryPrefix: true })
const titles = useMemo(() => {
if (!originCountry) {
return {
header: null,
subHeader: null,
}
}
if (renderUkSpecificText) {
return {
header: <Trans>Go direct to DeFi with Uniswap</Trans>,
subHeader: <Trans>Swap and explore tokens and NFTs</Trans>,
}
}
if (shouldDisableNFTRoutes) {
return {
header: <Trans>Trade crypto with confidence</Trans>,
subHeader: <Trans>Buy, sell, and explore tokens</Trans>,
}
}
return {
header: <Trans>Trade crypto and NFTs with confidence</Trans>,
subHeader: <Trans>Buy, sell, and explore tokens and NFTs</Trans>,
}
}, [originCountry, renderUkSpecificText, shouldDisableNFTRoutes])
if (selectedWallet && !queryParams.intro) {
return <Navigate to={{ ...location, pathname: '/swap' }} replace />
}
@ -343,21 +430,11 @@ export default function Landing() {
<Glow />
</GlowContainer>
<ContentContainer isDarkMode={isDarkMode}>
<TitleText isDarkMode={isDarkMode}>
{shouldDisableNFTRoutes ? (
<Trans>Trade crypto with confidence</Trans>
) : (
<Trans>Trade crypto and NFTs with confidence</Trans>
)}
<TitleText isDarkMode={isDarkMode} $visible={!!originCountry}>
{titles.header}
</TitleText>
<SubTextContainer>
<SubText>
{shouldDisableNFTRoutes ? (
<Trans>Buy, sell, and explore tokens</Trans>
) : (
<Trans>Buy, sell, and explore tokens and NFTs</Trans>
)}
</SubText>
<SubTextContainer $visible={!!originCountry}>
<SubText>{titles.subHeader}</SubText>
</SubTextContainer>
<ActionsContainer>
<TraceEvent
@ -402,8 +479,8 @@ export default function Landing() {
/>
))}
</CardGrid>
<CardGrid cols={3}>
{MORE_CARDS.map(({ darkIcon, lightIcon, ...card }) => (
<CardGrid cols={extraCards.length}>
{extraCards.map(({ darkIcon, lightIcon, ...card }) => (
<Card {...card} icon={isDarkMode ? darkIcon : lightIcon} key={card.title} type={CardType.Secondary} />
))}
</CardGrid>
@ -414,19 +491,3 @@ export default function Landing() {
</Trace>
)
}
const DownloadWalletLink = styled.a`
display: inline-flex;
gap: 8px;
margin-top: 24px;
color: ${({ theme }) => theme.neutral2};
text-decoration: none;
font-size: 16px;
line-height: 24px;
font-weight: 535;
text-align: center;
:hover {
color: ${({ theme }) => theme.neutral3};
}
`

@ -91,6 +91,7 @@ interface ExpectedUserState {
showSurveyPopup?: boolean
disabledUniswapX?: boolean
optedOutOfUniswapX?: boolean
originCountry?: string
}
assert<Equals<UserState, ExpectedUserState>>()

@ -54,6 +54,8 @@ export interface UserState {
optedOutOfUniswapX?: boolean
// undefined means has not gone through A/B split yet
showSurveyPopup?: boolean
originCountry?: string
}
function pairKey(token0Address: string, token1Address: string) {
@ -73,6 +75,7 @@ export const initialState: UserState = {
timestamp: currentTimestamp(),
hideBaseWalletBanner: false,
showSurveyPopup: undefined,
originCountry: undefined,
}
const userSlice = createSlice({
@ -133,12 +136,16 @@ const userSlice = createSlice({
}
state.timestamp = currentTimestamp()
},
setOriginCountry(state, { payload: country }) {
state.originCountry = country
},
},
})
export const {
addSerializedPair,
addSerializedToken,
setOriginCountry,
updateSelectedWallet,
updateHideClosedPositions,
updateUserRouterPreference,

@ -2,8 +2,10 @@ import * as Sentry from '@sentry/react'
import { BrowserTracing } from '@sentry/tracing'
import { SharedEventName } from '@uniswap/analytics-events'
import { initializeAnalytics, OriginApplication } from 'analytics'
import { isDevelopmentEnv, isSentryEnabled } from 'utils/env'
import { getEnvName, isProductionEnv } from 'utils/env'
import store from 'state'
import { setOriginCountry } from 'state/user/reducer'
import { isDevelopmentEnv, isProductionEnv, isSentryEnabled } from 'utils/env'
import { getEnvName } from 'utils/env'
import { v4 as uuidv4 } from 'uuid'
import { beforeSend } from './errors'
@ -45,4 +47,5 @@ initializeAnalytics(AMPLITUDE_DUMMY_KEY, OriginApplication.INTERFACE, {
commitHash: process.env.REACT_APP_GIT_COMMIT_HASH,
isProductionEnv: isProductionEnv(),
debug: isDevelopmentEnv(),
reportOriginCountry: (country: string) => store.dispatch(setOriginCountry(country)),
})

@ -6050,10 +6050,10 @@
resolved "https://registry.yarnpkg.com/@uniswap/analytics-events/-/analytics-events-2.24.0.tgz#c81d0c24da4f052b7f6b2663ff42bfa787be91b5"
integrity sha512-MhX9L95Y7i28a3KxRFJnpmNxNHAgownBVPyhT+mu4PnCXiPEuSovml+uJr277tysKSqxRYqLnCeAw4LocTBIfg==
"@uniswap/analytics@^1.4.0":
version "1.4.0"
resolved "https://registry.npmjs.org/@uniswap/analytics/-/analytics-1.4.0.tgz#ef2a76d4eae5dd0de09d763840b1edae9466314b"
integrity sha512-906YmEwRF2FCtn/R0EsVMZCkMy+LE74HGVw9jeXbnh6MEQvu0i7yMfTroMJEu0f9KVmmXr3xDWT+ZA0/OmnDdQ==
"@uniswap/analytics@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@uniswap/analytics/-/analytics-1.5.0.tgz#44e9febf773778c74516896c2555d6f95b2c6d62"
integrity sha512-4Hj7uMlU8j/FbKoXxMFmx2pClgOywq+dKy26gwP7696m5GPY2ZFH14XvBrg/ZMJ/Q5tm6nlMDvtxV6xMUK8ung==
dependencies:
"@amplitude/analytics-browser" "^1.5.8"
react "^18.2.0"