feat: nft explore promo banner (#5369)

* init

* it looks good and is working

* don't change image on every render

* un-move dark mode hook

* oops

* handle mobile screen smallest size

* first round comment responses

* second round comments

* rest of comments

* fix analytics changes

* remove unnecesasry z index
This commit is contained in:
lynn 2022-11-22 15:18:19 -05:00 committed by GitHub
parent ceed5e0b4c
commit c1cb712087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 176 additions and 7 deletions

@ -3,7 +3,9 @@ import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import NftExploreBanner from 'nft/components/nftExploreBanner/NftExploreBanner'
import { lazy } from 'react'
import { useLocation } from 'react-router-dom'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
@ -17,6 +19,12 @@ export default function TopLevelModals() {
const blockedAccountModalOpen = useModalIsOpen(ApplicationModal.BLOCKED_ACCOUNT)
const { account } = useWeb3React()
const location = useLocation()
const isNftEnabled = useNftFlag() === NftVariant.Enabled
const pageShowsNftPromoBanner =
location.pathname.startsWith('/swap') ||
location.pathname.startsWith('/tokens') ||
location.pathname.startsWith('/pool')
useAccountRiskCheck(account)
const open = Boolean(blockedAccountModalOpen && account)
@ -25,8 +33,13 @@ export default function TopLevelModals() {
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
<ConnectedAccountBlocked account={account} isOpen={open} />
<Bag />
{useNftFlag() === NftVariant.Enabled && <TransactionCompleteModal />}
{useNftFlag() === NftVariant.Enabled && <AirdropModal />}
{isNftEnabled && (
<>
<TransactionCompleteModal />
<AirdropModal />
{pageShowsNftPromoBanner && <NftExploreBanner />}
</>
)}
</>
)
}

@ -0,0 +1,130 @@
import { Trans } from '@lingui/macro'
import { LARGE_MEDIA_BREAKPOINT, SMALL_MOBILE_MEDIA_BREAKPOINT } from 'components/Tokens/constants'
import { Box } from 'nft/components/Box'
import { bodySmall, subhead } from 'nft/css/common.css'
import { X } from 'react-feather'
import { useNavigate } from 'react-router-dom'
import { useShowNftPromoBanner } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { StyledInternalLink } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import nftPromoImage1 from '../nftExploreBanner/nftArt1.png'
import nftPromoImage2 from '../nftExploreBanner/nftArt2.png'
import nftPromoImage3 from '../nftExploreBanner/nftArt3.png'
function getRandom(list: any[]) {
return list[Math.floor(Math.random() * list.length)]
}
const randomizedNftImage = getRandom([nftPromoImage1, nftPromoImage2, nftPromoImage3])
const PopupContainer = styled.div<{ show: boolean }>`
background-color: ${({ theme }) => theme.backgroundSurface};
box-shadow: ${({ theme }) => theme.deepShadow};
border: 1px solid ${({ theme }) => theme.backgroundOutline};
border-radius: 12px;
cursor: pointer;
color: ${({ theme }) => theme.textPrimary};
display: ${({ show }) => (show ? 'flex' : 'none')};
flex-direction: column;
position: fixed;
right: clamp(0px, 1vw, 16px);
z-index: ${Z_INDEX.sticky};
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `${duration.slow} opacity ${timing.in}`};
width: 98vw;
bottom: 55px;
@media screen and (min-width: ${LARGE_MEDIA_BREAKPOINT}) {
bottom: 48px;
}
@media screen and (min-width: ${SMALL_MOBILE_MEDIA_BREAKPOINT}) {
width: 391px;
}
:hover {
border: double 1px transparent;
border-radius: 12px;
background-image: ${({ theme }) =>
`linear-gradient(${theme.backgroundSurface}, ${theme.backgroundSurface}),
radial-gradient(circle at top left, hsla(299, 100%, 87%, 1), hsla(299, 100%, 61%, 1))`};
background-origin: border-box;
background-clip: padding-box, border-box;
}
`
const InnerContainer = styled.div`
overflow: hidden;
display: flex;
position: relative;
gap: 8px;
padding: 8px;
`
const TextContainer = styled.div`
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
`
const StyledXButton = styled(X)`
color: ${({ theme }) => theme.textSecondary};
&:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
&:active {
opacity: ${({ theme }) => theme.opacity.click};
}
`
const StyledImageContainer = styled(Box)`
width: 23%;
cursor: pointer;
aspectratio: 1;
transition: transform 0.25s ease 0s;
object-fit: contain;
`
export default function NftExploreBanner() {
const [showNftPromoBanner, stopShowingNftPromoBanner] = useShowNftPromoBanner()
const navigate = useNavigate()
const navigateToNfts = () => {
navigate('/nfts')
stopShowingNftPromoBanner()
}
return (
<PopupContainer show={showNftPromoBanner} onClick={navigateToNfts}>
<InnerContainer>
<StyledImageContainer as="img" src={randomizedNftImage} draggable={false} />
<TextContainer>
{/* <HeaderText> */}
<div className={subhead}>
<Trans>Introducing Uniswap NFT</Trans>
</div>
{/* </HeaderText> */}
{/* <Description> */}
<div className={bodySmall}>
<Trans>Buy and sell NFTs across more listings at better prices.</Trans>{' '}
<StyledInternalLink to="/nfts">
<Trans>Explore NFTs</Trans>
</StyledInternalLink>{' '}
</div>
</TextContainer>
{/* </Description> */}
<StyledXButton
size={20}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
stopShowingNftPromoBanner()
}}
/>
</InnerContainer>
</PopupContainer>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

@ -97,15 +97,23 @@ const Marginer = styled.div`
`
function getCurrentPageFromLocation(locationPathname: string): PageName | undefined {
switch (locationPathname) {
case '/swap':
switch (true) {
case locationPathname.startsWith('/swap'):
return PageName.SWAP_PAGE
case '/vote':
case locationPathname.startsWith('/vote'):
return PageName.VOTE_PAGE
case '/pool':
case locationPathname.startsWith('/pool'):
return PageName.POOL_PAGE
case '/tokens':
case locationPathname.startsWith('/tokens'):
return PageName.TOKENS_PAGE
case locationPathname.startsWith('/nfts/profile'):
return PageName.NFT_PROFILE_PAGE
case locationPathname.startsWith('/nfts/asset'):
return PageName.NFT_DETAILS_PAGE
case locationPathname.startsWith('/nfts/collection'):
return PageName.NFT_COLLECTION_PAGE
case locationPathname.startsWith('/nfts'):
return PageName.NFT_EXPLORE_PAGE
default:
return undefined
}

@ -19,6 +19,7 @@ import {
addSerializedToken,
removeSerializedToken,
updateHideClosedPositions,
updateShowNftPromoBanner,
updateShowSurveyPopup,
updateUserClientSideRouter,
updateUserDarkMode,
@ -271,6 +272,17 @@ export function useURLWarningVisible(): boolean {
return useAppSelector((state: AppState) => state.user.URLWarningVisible)
}
export function useShowNftPromoBanner(): [boolean, () => void] {
const dispatch = useAppDispatch()
const showNftPromoBanner = useAppSelector((state) => state.user.NFTPromoBannerVisible)
const stopShowingNftPromoBanner = useCallback(() => {
dispatch(updateShowNftPromoBanner({ showNftPromoBanner: false }))
}, [dispatch])
return [showNftPromoBanner, stopShowingNftPromoBanner]
}
/**
* Given two tokens return the liquidity token that represents its liquidity shares
* @param tokenA one of the two tokens

@ -48,6 +48,7 @@ export interface UserState {
timestamp: number
URLWarningVisible: boolean
NFTPromoBannerVisible: boolean // whether or not we should show the nft explore promo banner
// undefined means has not gone through A/B split yet
showSurveyPopup: boolean | undefined
@ -74,6 +75,7 @@ export const initialState: UserState = {
pairs: {},
timestamp: currentTimestamp(),
URLWarningVisible: true,
NFTPromoBannerVisible: true,
showSurveyPopup: undefined,
showDonationLink: true,
}
@ -121,6 +123,9 @@ const userSlice = createSlice({
updateShowDonationLink(state, action) {
state.showDonationLink = action.payload.showDonationLink
},
updateShowNftPromoBanner(state, action) {
state.NFTPromoBannerVisible = action.payload.NFTPromoBannerVisible
},
addSerializedToken(state, { payload: { serializedToken } }) {
if (!state.tokens) {
state.tokens = {}
@ -210,5 +215,6 @@ export const {
updateUserExpertMode,
updateUserLocale,
updateUserSlippageTolerance,
updateShowNftPromoBanner,
} = userSlice.actions
export default userSlice.reducer