feat: add base promotional advert (#7124)

* feat: add base promotional advert

add mobile layout

tweak text container size

only show new banner on Base

fix import

update to new state structure

* Update src/components/Banner/BaseAnnouncementBanner/index.tsx

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

* pr feedback

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>
This commit is contained in:
Jordan Frankfurt 2023-08-10 11:03:21 -05:00 committed by GitHub
parent b14831be12
commit 0b8026e6fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 197 additions and 168 deletions

@ -0,0 +1,3 @@
<svg width="119" height="99" viewBox="0 0 119 99" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.097 29.6061C117.891 67.6376 86.8404 98.3016 48.7425 98.0958C12.5975 97.9006 -16.9048 69.9894 -19.659 34.6507L71.5199 35.1431L71.5825 23.5662L-19.5965 23.0738C-16.4608 -12.2332 13.3412 -39.824 49.4863 -39.6288C87.5842 -39.423 118.302 -8.4256 118.097 29.6061Z" fill="white" fill-opacity="0.1"/>
</svg>

After

Width:  |  Height:  |  Size: 452 B

@ -0,0 +1,92 @@
import { Trans } from '@lingui/macro'
import { InterfaceElementName } from '@uniswap/analytics-events'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { ReactComponent as AppleLogo } from 'assets/svg/apple_logo.svg'
import baseLogoUrl from 'assets/svg/base_background_icon.svg'
import { useScreenSize } from 'hooks/useScreenSize'
import { useLocation } from 'react-router-dom'
import { useHideBaseWalletBanner } from 'state/user/hooks'
import { ThemedText } from 'theme'
import { openDownloadApp, openWalletMicrosite } from 'utils/openDownloadApp'
import { isIOS, isMobileSafari } from 'utils/userAgent'
import { BannerButton, BaseBackgroundImage, ButtonRow, PopupContainer, StyledXButton } from './styled'
export default function BaseWalletBanner() {
const { chainId } = useWeb3React()
const [hideBaseWalletBanner, toggleHideBaseWalletBanner] = useHideBaseWalletBanner()
const location = useLocation()
const isLandingScreen = location.search === '?intro=true' || location.pathname === '/'
const shouldDisplay = Boolean(!hideBaseWalletBanner && !isLandingScreen && chainId === ChainId.BASE)
const screenSize = useScreenSize()
if (isMobileSafari) return null
return (
<PopupContainer show={shouldDisplay}>
<StyledXButton
data-testid="uniswap-wallet-banner"
size={20}
onClick={(e) => {
// prevent click from bubbling to UI on the page underneath, i.e. clicking a token row
e.preventDefault()
e.stopPropagation()
toggleHideBaseWalletBanner()
}}
/>
<BaseBackgroundImage src={baseLogoUrl} alt="transparent base background logo" />
<ThemedText.HeadlineMedium fontSize="24px" lineHeight="28px" color="white" maxWidth="224px">
<Trans>
Swap on{' '}
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.5689 10C19.5689 15.4038 15.1806 19.7845 9.76737 19.7845C4.63163 19.7845 0.418433 15.8414 0 10.8225H12.9554V9.17755H0C0.418433 4.15863 4.63163 0.215576 9.76737 0.215576C15.1806 0.215576 19.5689 4.59621 19.5689 10Z"
fill="white"
/>
</svg>{' '}
BASE in the Uniswap wallet
</Trans>
</ThemedText.HeadlineMedium>
<ButtonRow>
{isIOS ? (
<>
<BannerButton
backgroundColor="white"
onClick={() =>
openDownloadApp({
element: InterfaceElementName.UNISWAP_WALLET_BANNER_DOWNLOAD_BUTTON,
appStoreParams: 'pt=123625782&ct=base-app-banner&mt=8',
})
}
>
<AppleLogo width={14} height={14} />
<ThemedText.LabelSmall color="black" marginLeft="5px">
{!screenSize['xs'] ? <Trans>Download</Trans> : <Trans>Download app</Trans>}
</ThemedText.LabelSmall>
</BannerButton>
<BannerButton backgroundColor="black" onClick={() => openWalletMicrosite()}>
<ThemedText.LabelSmall color="white">
<Trans>Learn more</Trans>
</ThemedText.LabelSmall>
</BannerButton>
</>
) : (
<BannerButton backgroundColor="white" width="125px" onClick={() => openWalletMicrosite()}>
<ThemedText.LabelSmall color="black">
<Trans>Learn more</Trans>
</ThemedText.LabelSmall>
</BannerButton>
)}
</ButtonRow>
</PopupContainer>
)
}

@ -0,0 +1,84 @@
import walletBannerPhoneImageSrc from 'assets/images/wallet_banner_phone_image.png'
import { BaseButton } from 'components/Button'
import { OpacityHoverState } from 'components/Common'
import Row from 'components/Row'
import { X } from 'react-feather'
import styled from 'styled-components'
import { Z_INDEX } from 'theme/zIndex'
export const PopupContainer = styled.div<{ show: boolean }>`
display: flex;
flex-direction: column;
justify-content: space-between;
${({ show }) => !show && 'display: none'};
background: url(${walletBannerPhoneImageSrc});
background-repeat: no-repeat;
background-position: top 18px right 15px;
background-size: 166px;
:hover {
background-size: 170px;
}
transition: background-size ${({ theme }) => theme.transition.duration.medium}
${({ theme }) => theme.transition.timing.inOut};
background-color: ${({ theme }) => theme.chain_84531};
color: ${({ theme }) => theme.textPrimary};
position: fixed;
z-index: ${Z_INDEX.sticky};
padding: 24px 16px 16px;
border-radius: 20px;
bottom: 20px;
right: 20px;
width: 390px;
height: 164px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
box-shadow: ${({ theme }) => theme.deepShadow};
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
bottom: 62px;
}
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
background-position: top 32px right -10px;
width: unset;
right: 10px;
left: 10px;
height: 144px;
}
user-select: none;
`
export const BaseBackgroundImage = styled.img`
position: absolute;
top: 0;
left: 0;
height: 138px;
width: 138px;
`
export const ButtonRow = styled(Row)`
gap: 16px;
`
export const StyledXButton = styled(X)`
cursor: pointer;
position: absolute;
top: 21px;
right: 17px;
color: ${({ theme }) => theme.white};
${OpacityHoverState};
`
export const BannerButton = styled(BaseButton)`
height: 40px;
border-radius: 16px;
padding: 10px;
${OpacityHoverState};
`

@ -1,150 +0,0 @@
import { Trans } from '@lingui/macro'
import { InterfaceElementName } from '@uniswap/analytics-events'
import walletBannerPhoneImageSrc from 'assets/images/wallet_banner_phone_image.png'
import { ReactComponent as AppleLogo } from 'assets/svg/apple_logo.svg'
import { BaseButton } from 'components/Button'
import { AutoColumn } from 'components/Column'
import { OpacityHoverState } from 'components/Common'
import Row from 'components/Row'
import { useScreenSize } from 'hooks/useScreenSize'
import { X } from 'react-feather'
import { useLocation } from 'react-router-dom'
import { useHideUniswapWalletBanner } from 'state/user/hooks'
import styled from 'styled-components'
import { ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { openDownloadApp, openWalletMicrosite } from 'utils/openDownloadApp'
import { isIOS, isMobileSafari } from 'utils/userAgent'
const PopupContainer = styled.div<{ show: boolean }>`
display: flex;
flex-direction: column;
justify-content: space-between;
${({ show }) => !show && 'display: none'};
background: url(${walletBannerPhoneImageSrc});
background-repeat: no-repeat;
background-position: top 18px right 15px;
background-size: 166px;
:hover {
background-size: 170px;
}
transition: background-size ${({ theme }) => theme.transition.duration.medium}
${({ theme }) => theme.transition.timing.inOut};
background-color: ${({ theme }) => theme.promotional};
color: ${({ theme }) => theme.textPrimary};
position: fixed;
z-index: ${Z_INDEX.sticky};
padding: 24px 16px 16px;
border-radius: 20px;
bottom: 20px;
right: 20px;
width: 390px;
height: 164px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
box-shadow: ${({ theme }) => theme.deepShadow};
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
bottom: 62px;
}
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
width: unset;
right: 10px;
left: 10px;
}
user-select: none;
`
const ButtonRow = styled(Row)`
gap: 16px;
`
const StyledXButton = styled(X)`
cursor: pointer;
position: absolute;
top: 21px;
right: 17px;
color: ${({ theme }) => theme.white};
${OpacityHoverState};
`
const BannerButton = styled(BaseButton)`
height: 40px;
border-radius: 16px;
padding: 10px;
${OpacityHoverState};
`
export default function UniswapWalletBanner() {
const [hideUniswapWalletBanner, toggleHideUniswapWalletBanner] = useHideUniswapWalletBanner()
const location = useLocation()
const isLandingScreen = location.search === '?intro=true' || location.pathname === '/'
const shouldDisplay = Boolean(!hideUniswapWalletBanner && !isLandingScreen)
const screenSize = useScreenSize()
if (isMobileSafari) return null
return (
<PopupContainer show={shouldDisplay}>
<StyledXButton
data-testid="uniswap-wallet-banner"
size={20}
onClick={(e) => {
// prevent click from bubbling to UI on the page underneath, i.e. clicking a token row
e.preventDefault()
e.stopPropagation()
toggleHideUniswapWalletBanner()
}}
/>
<AutoColumn gap="8px">
<ThemedText.HeadlineMedium fontSize="24px" lineHeight="28px" color="white" maxWidth="60%">
<Trans>Uniswap in your pocket</Trans>
</ThemedText.HeadlineMedium>
</AutoColumn>
<ButtonRow>
{isIOS ? (
<>
<BannerButton
backgroundColor="white"
onClick={() =>
openDownloadApp({
element: InterfaceElementName.UNISWAP_WALLET_BANNER_DOWNLOAD_BUTTON,
})
}
>
<AppleLogo width={14} height={14} />
<ThemedText.LabelSmall color="black" marginLeft="5px">
{!screenSize['xs'] ? <Trans>Download</Trans> : <Trans>Download app</Trans>}
</ThemedText.LabelSmall>
</BannerButton>
<BannerButton backgroundColor="black" onClick={() => openWalletMicrosite()}>
<ThemedText.LabelSmall color="white">
<Trans>Learn more</Trans>
</ThemedText.LabelSmall>
</BannerButton>
</>
) : (
<BannerButton backgroundColor="white" width="125px" onClick={() => openWalletMicrosite()}>
<ThemedText.LabelSmall color="black">
<Trans>Learn more</Trans>
</ThemedText.LabelSmall>
</BannerButton>
)}
</ButtonRow>
</PopupContainer>
)
}

@ -1,7 +1,7 @@
import { useWeb3React } from '@web3-react/core'
import { OffchainActivityModal } from 'components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal'
import UniwalletModal from 'components/AccountDrawer/UniwalletModal'
import UniswapWalletBanner from 'components/Banner/UniswapWalletBanner'
import BaseAnnouncementBanner from 'components/Banner/BaseAnnouncementBanner'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import FiatOnrampModal from 'components/FiatOnrampModal'
@ -28,7 +28,7 @@ export default function TopLevelModals() {
<ConnectedAccountBlocked account={account} isOpen={accountBlocked} />
<Bag />
<UniwalletModal />
<UniswapWalletBanner />
<BaseAnnouncementBanner />
<OffchainActivityModal />
<TransactionCompleteModal />
<AirdropModal />

@ -16,8 +16,8 @@ import { AppState } from '../types'
import {
addSerializedPair,
addSerializedToken,
updateHideBaseWalletBanner,
updateHideClosedPositions,
updateHideUniswapWalletBanner,
updateUserDeadline,
updateUserLocale,
updateUserRouterPreference,
@ -211,15 +211,15 @@ export function useURLWarningVisible(): boolean {
return useAppSelector((state: AppState) => state.user.URLWarningVisible)
}
export function useHideUniswapWalletBanner(): [boolean, () => void] {
export function useHideBaseWalletBanner(): [boolean, () => void] {
const dispatch = useAppDispatch()
const hideUniswapWalletBanner = useAppSelector((state) => state.user.hideUniswapWalletBanner)
const hideBaseWalletBanner = useAppSelector((state) => state.user.hideBaseWalletBanner)
const toggleHideUniswapWalletBanner = useCallback(() => {
dispatch(updateHideUniswapWalletBanner({ hideUniswapWalletBanner: true }))
const toggleHideBaseWalletBanner = useCallback(() => {
dispatch(updateHideBaseWalletBanner({ hideBaseWalletBanner: true }))
}, [dispatch])
return [hideUniswapWalletBanner, toggleHideUniswapWalletBanner]
return [hideBaseWalletBanner, toggleHideBaseWalletBanner]
}
export function useUserDisabledUniswapX(): boolean {

@ -7,8 +7,8 @@ import reducer, {
addSerializedPair,
addSerializedToken,
initialState,
updateHideBaseWalletBanner,
updateHideClosedPositions,
updateHideUniswapWalletBanner,
updateSelectedWallet,
updateUserDeadline,
updateUserLocale,
@ -110,10 +110,10 @@ describe('swap reducer', () => {
})
})
describe('updateHideUniswapWalletBanner', () => {
it('updates the hideUniswapWalletBanner', () => {
store.dispatch(updateHideUniswapWalletBanner({ hideUniswapWalletBanner: true }))
expect(store.getState().hideUniswapWalletBanner).toEqual(true)
describe('updateHideBaseWalletBanner', () => {
it('updates the updateHideBaseWalletBanner', () => {
store.dispatch(updateHideBaseWalletBanner({ hideBaseWalletBanner: true }))
expect(store.getState().hideBaseWalletBanner).toEqual(true)
})
})

@ -47,7 +47,7 @@ export interface UserState {
timestamp: number
URLWarningVisible: boolean
hideUniswapWalletBanner: boolean
hideBaseWalletBanner: boolean
disabledUniswapX?: boolean
// undefined means has not gone through A/B split yet
showSurveyPopup?: boolean
@ -69,7 +69,7 @@ export const initialState: UserState = {
pairs: {},
timestamp: currentTimestamp(),
URLWarningVisible: true,
hideUniswapWalletBanner: false,
hideBaseWalletBanner: false,
showSurveyPopup: undefined,
}
@ -98,8 +98,8 @@ const userSlice = createSlice({
updateHideClosedPositions(state, action) {
state.userHideClosedPositions = action.payload.userHideClosedPositions
},
updateHideUniswapWalletBanner(state, action) {
state.hideUniswapWalletBanner = action.payload.hideUniswapWalletBanner
updateHideBaseWalletBanner(state, action) {
state.hideBaseWalletBanner = action.payload.hideBaseWalletBanner
},
updateDisabledUniswapX(state, action) {
state.disabledUniswapX = action.payload.disabledUniswapX
@ -214,7 +214,7 @@ export const {
updateUserDeadline,
updateUserLocale,
updateUserSlippageTolerance,
updateHideUniswapWalletBanner,
updateHideBaseWalletBanner,
updateDisabledUniswapX,
} = userSlice.actions
export default userSlice.reducer