Commit cf5bb574 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

feat: [List V2] Initial Floating Bar (#5817)

* initial floating bar and VE removal

* dynamic price info

* align bottom bar to bottom

* fixed to bottom

* darkmode gradient

* dynamic overlay

* style tweaks

* toggle bag if flag enabled

* handle really small numbers

* address comments

* add border
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 5f280ffd
...@@ -3,6 +3,7 @@ import { formatEther } from '@ethersproject/units' ...@@ -3,6 +3,7 @@ import { formatEther } from '@ethersproject/units'
import { sendAnalyticsEvent } from '@uniswap/analytics' import { sendAnalyticsEvent } from '@uniswap/analytics'
import { NFTEventName } from '@uniswap/analytics-events' import { NFTEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { useIsNftDetailsPage, useIsNftPage, useIsNftProfilePage } from 'hooks/useIsNftPage' import { useIsNftDetailsPage, useIsNftPage, useIsNftProfilePage } from 'hooks/useIsNftPage'
import { BagFooter } from 'nft/components/bag/BagFooter' import { BagFooter } from 'nft/components/bag/BagFooter'
import ListingModal from 'nft/components/bag/profile/ListingModal' import ListingModal from 'nft/components/bag/profile/ListingModal'
...@@ -135,6 +136,7 @@ const Bag = () => { ...@@ -135,6 +136,7 @@ const Bag = () => {
const isDetailsPage = useIsNftDetailsPage() const isDetailsPage = useIsNftDetailsPage()
const isNFTPage = useIsNftPage() const isNFTPage = useIsNftPage()
const isMobile = useIsMobile() const isMobile = useIsMobile()
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const sendTransaction = useSendTransaction((state) => state.sendTransaction) const sendTransaction = useSendTransaction((state) => state.sendTransaction)
const transactionState = useSendTransaction((state) => state.state) const transactionState = useSendTransaction((state) => state.state)
...@@ -349,7 +351,7 @@ const Bag = () => { ...@@ -349,7 +351,7 @@ const Bag = () => {
color="white" color="white"
textAlign="center" textAlign="center"
onClick={() => { onClick={() => {
isMobile && toggleBag() ;(isMobile || isNftListV2) && toggleBag()
setProfilePageState(ProfilePageStateType.LISTING) setProfilePageState(ProfilePageStateType.LISTING)
sendAnalyticsEvent(NFTEventName.NFT_PROFILE_PAGE_START_SELL, { sendAnalyticsEvent(NFTEventName.NFT_PROFILE_PAGE_START_SELL, {
list_quantity: sellAssets.length, list_quantity: sellAssets.length,
......
...@@ -133,7 +133,7 @@ export const getTotalEthValue = (sellAssets: WalletAsset[]) => { ...@@ -133,7 +133,7 @@ export const getTotalEthValue = (sellAssets: WalletAsset[]) => {
} }
return total return total
}, 0) }, 0)
return total ? Math.round(total * 100 + Number.EPSILON) / 100 : 0 return total ? Math.round(total * 10000 + Number.EPSILON) / 10000 : 0
} }
export const getListings = (sellAssets: WalletAsset[]): [CollectionRow[], ListingRow[]] => { export const getListings = (sellAssets: WalletAsset[]): [CollectionRow[], ListingRow[]] => {
......
import { t, Trans } from '@lingui/macro'
import Column from 'components/Column'
import Row from 'components/Row'
import { SMALL_MEDIA_BREAKPOINT } from 'components/Tokens/constants' import { SMALL_MEDIA_BREAKPOINT } from 'components/Tokens/constants'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { ListingButton } from 'nft/components/bag/profile/ListingButton' import { ListingButton } from 'nft/components/bag/profile/ListingButton'
import { getListingState } from 'nft/components/bag/profile/utils' import { getListingState, getTotalEthValue } from 'nft/components/bag/profile/utils'
import { Column, Row } from 'nft/components/Flex'
import { BackArrowIcon } from 'nft/components/icons' import { BackArrowIcon } from 'nft/components/icons'
import { headlineLarge, headlineSmall } from 'nft/css/common.css' import { headlineLarge, headlineSmall } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css' import { themeVars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile, useNFTList, useProfilePageState, useSellAsset } from 'nft/hooks' import { useBag, useIsMobile, useNFTList, useProfilePageState, useSellAsset } from 'nft/hooks'
import { ListingStatus, ProfilePageStateType } from 'nft/types' import { ListingStatus, ProfilePageStateType } from 'nft/types'
import { fetchPrice, formatEth, formatUsdPrice } from 'nft/utils'
import { ListingMarkets } from 'nft/utils/listNfts' import { ListingMarkets } from 'nft/utils/listNfts'
import { useEffect, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { NFTListingsGrid } from './NFTListingsGrid' import { NFTListingsGrid } from './NFTListingsGrid'
import { SelectMarketplacesDropdown } from './SelectMarketplacesDropdown' import { SelectMarketplacesDropdown } from './SelectMarketplacesDropdown'
import { SetDurationModal } from './SetDurationModal' import { SetDurationModal } from './SetDurationModal'
const TitleWrapper = styled(Row)`
gap: 4px;
margin-bottom: 18px;
white-space: nowrap;
width: min-content;
@media screen and (min-width: ${SMALL_MEDIA_BREAKPOINT}) {
margin-bottom: 0px;
}
`
const ButtonsWrapper = styled(Row)`
gap: 12px;
width: min-content;
`
const MarketWrap = styled.section` const MarketWrap = styled.section`
gap: 48px; gap: 48px;
margin: 0px auto; margin: 0px auto;
...@@ -55,6 +77,43 @@ const MobileListButtonWrapper = styled.div` ...@@ -55,6 +77,43 @@ const MobileListButtonWrapper = styled.div`
} }
` `
const FloatingConfirmationBar = styled(Row)`
padding: 20px 32px;
border: 1px solid;
border-color: ${({ theme }) => theme.backgroundOutline};
border-radius: 20px;
white-space: nowrap;
justify-content: space-between;
background: ${({ theme }) => theme.backgroundSurface};
position: fixed;
bottom: 32px;
margin: 0px 156px;
width: calc(100vw - 312px);
z-index: ${Z_INDEX.under_dropdown};
`
const Overlay = styled.div`
position: fixed;
bottom: 0px;
height: 158px;
width: 100vw;
background: ${({ theme }) => `linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, ${theme.backgroundBackdrop} 100%)`};
`
const ProceedsAndButtonWrapper = styled(Row)`
width: min-content;
gap: 40px;
`
const ProceedsWrapper = styled(Row)`
width: min-content;
gap: 16px;
`
const ListingButtonWrapper = styled.div`
width: 170px;
`
export const ListPage = () => { export const ListPage = () => {
const { setProfilePageState: setSellPageState } = useProfilePageState() const { setProfilePageState: setSellPageState } = useProfilePageState()
const setGlobalMarketplaces = useSellAsset((state) => state.setGlobalMarketplaces) const setGlobalMarketplaces = useSellAsset((state) => state.setGlobalMarketplaces)
...@@ -64,7 +123,19 @@ export const ListPage = () => { ...@@ -64,7 +123,19 @@ export const ListPage = () => {
const collectionsRequiringApproval = useNFTList((state) => state.collectionsRequiringApproval) const collectionsRequiringApproval = useNFTList((state) => state.collectionsRequiringApproval)
const listingStatus = useNFTList((state) => state.listingStatus) const listingStatus = useNFTList((state) => state.listingStatus)
const setListingStatus = useNFTList((state) => state.setListingStatus) const setListingStatus = useNFTList((state) => state.setListingStatus)
const sellAssets = useSellAsset((state) => state.sellAssets)
const isMobile = useIsMobile() const isMobile = useIsMobile()
const isNftListV2 = useNftListV2Flag() === NftListV2Variant.Enabled
const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets])
const anyListingsMissingPrice = useMemo(() => !!listings.find((listing) => !listing.price), [listings])
const [ethPriceInUSD, setEthPriceInUSD] = useState(0)
useEffect(() => {
fetchPrice().then((price) => {
setEthPriceInUSD(price ?? 0)
})
}, [])
useEffect(() => { useEffect(() => {
const state = getListingState(collectionsRequiringApproval, listings) const state = getListingState(collectionsRequiringApproval, listings)
...@@ -89,7 +160,7 @@ export const ListPage = () => { ...@@ -89,7 +160,7 @@ export const ListPage = () => {
<Column> <Column>
<MarketWrap> <MarketWrap>
<ListingHeader> <ListingHeader>
<Row gap="4" marginBottom={{ sm: '18', md: '0' }}> <TitleWrapper>
<BackArrowIcon <BackArrowIcon
height={isMobile ? 20 : 32} height={isMobile ? 20 : 32}
width={isMobile ? 20 : 32} width={isMobile ? 20 : 32}
...@@ -98,19 +169,52 @@ export const ListPage = () => { ...@@ -98,19 +169,52 @@ export const ListPage = () => {
cursor="pointer" cursor="pointer"
/> />
<div className={isMobile ? headlineSmall : headlineLarge}>Sell NFTs</div> <div className={isMobile ? headlineSmall : headlineLarge}>Sell NFTs</div>
</Row> </TitleWrapper>
<Row gap="12"> <ButtonsWrapper>
<SelectMarketplacesDropdown setSelectedMarkets={setSelectedMarkets} selectedMarkets={selectedMarkets} /> <SelectMarketplacesDropdown setSelectedMarkets={setSelectedMarkets} selectedMarkets={selectedMarkets} />
<SetDurationModal /> <SetDurationModal />
</Row> </ButtonsWrapper>
</ListingHeader> </ListingHeader>
<GridWrapper> <GridWrapper>
<NFTListingsGrid selectedMarkets={selectedMarkets} /> <NFTListingsGrid selectedMarkets={selectedMarkets} />
</GridWrapper> </GridWrapper>
</MarketWrap> </MarketWrap>
<MobileListButtonWrapper> {isNftListV2 && (
<ListingButton onClick={toggleBag} buttonText="Continue listing" /> <>
</MobileListButtonWrapper> <FloatingConfirmationBar>
<ThemedText.HeadlineSmall lineHeight="28px">
<Trans>Proceeds if sold</Trans>
</ThemedText.HeadlineSmall>
<ProceedsAndButtonWrapper>
<ProceedsWrapper>
<ThemedText.HeadlineSmall
lineHeight="28px"
color={totalEthListingValue ? 'textPrimary' : 'textTertiary'}
>
{totalEthListingValue > 0 ? formatEth(totalEthListingValue) : '-'} ETH
</ThemedText.HeadlineSmall>
{!!totalEthListingValue && !!ethPriceInUSD && (
<ThemedText.HeadlineSmall lineHeight="28px" color="textSecondary">
{formatUsdPrice(totalEthListingValue * ethPriceInUSD)}
</ThemedText.HeadlineSmall>
)}
</ProceedsWrapper>
<ListingButtonWrapper>
<ListingButton
onClick={toggleBag}
buttonText={anyListingsMissingPrice ? t`Set prices to continue` : t`Start listing`}
/>
</ListingButtonWrapper>
</ProceedsAndButtonWrapper>
</FloatingConfirmationBar>
<Overlay />
</>
)}
{!isNftListV2 && (
<MobileListButtonWrapper>
<ListingButton onClick={toggleBag} buttonText="Continue listing" />
</MobileListButtonWrapper>
)}
</Column> </Column>
) )
} }
...@@ -18,7 +18,7 @@ export const formatEth = (price: number) => { ...@@ -18,7 +18,7 @@ export const formatEth = (price: number) => {
} else if (price < 0.001) { } else if (price < 0.001) {
return '<0.001' return '<0.001'
} else { } else {
return `${Math.round(price * 100 + Number.EPSILON) / 100}` return `${Math.round(price * 1000 + Number.EPSILON) / 1000}`
} }
} }
......
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