feat: nft sweep (#4972)
* adding slider * removing test css * initial sweep pass * cleaning up sweep functionality * adding eth amount for sweep * handling input * sweep does not add duplicates * updating fetcher to handle traits + price * locking sweeped items on contract addr change/ filter change * final touches to sweep for desktop * desktop sweep finalizations * keeping state after close * added mobile sweep * loading state * dedup * addressing comments
This commit is contained in:
parent
b12e5270fa
commit
d66002dc75
@ -15,7 +15,7 @@ export const BagContent = () => {
|
||||
const setDidOpenUnavailableAssets = useBag((s) => s.setDidOpenUnavailableAssets)
|
||||
const uncheckedItemsInBag = useBag((s) => s.itemsInBag)
|
||||
const setItemsInBag = useBag((s) => s.setItemsInBag)
|
||||
const removeAssetFromBag = useBag((s) => s.removeAssetFromBag)
|
||||
const removeAssetsFromBag = useBag((s) => s.removeAssetsFromBag)
|
||||
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
@ -75,16 +75,19 @@ export const BagContent = () => {
|
||||
))}
|
||||
</Column>
|
||||
<Column gap="8">
|
||||
{unchangedAssets.map((asset) => (
|
||||
<BagRow
|
||||
key={asset.id}
|
||||
asset={asset}
|
||||
usdPrice={fetchedPriceData}
|
||||
removeAsset={removeAssetFromBag}
|
||||
showRemove={true}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
))}
|
||||
{unchangedAssets
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map((asset) => (
|
||||
<BagRow
|
||||
key={asset.id}
|
||||
asset={asset}
|
||||
usdPrice={fetchedPriceData}
|
||||
removeAsset={removeAssetsFromBag}
|
||||
showRemove={true}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</>
|
||||
)
|
||||
|
@ -46,7 +46,7 @@ const NoContentContainer = () => (
|
||||
interface BagRowProps {
|
||||
asset: UpdatedGenieAsset
|
||||
usdPrice: number | undefined
|
||||
removeAsset: (asset: GenieAsset) => void
|
||||
removeAsset: (assets: GenieAsset[]) => void
|
||||
showRemove?: boolean
|
||||
grayscale?: boolean
|
||||
isMobile: boolean
|
||||
@ -72,7 +72,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
|
||||
onClick={(e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
removeAsset(asset)
|
||||
removeAsset([asset])
|
||||
}}
|
||||
transition="250"
|
||||
zIndex="1"
|
||||
@ -113,7 +113,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
removeAsset(asset)
|
||||
removeAsset([asset])
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
|
@ -100,8 +100,8 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName }: Ac
|
||||
)
|
||||
|
||||
const itemsInBag = useBag((state) => state.itemsInBag)
|
||||
const addAssetToBag = useBag((state) => state.addAssetToBag)
|
||||
const removeAssetFromBag = useBag((state) => state.removeAssetFromBag)
|
||||
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((state) => state.removeAssetsFromBag)
|
||||
const cartExpanded = useBag((state) => state.bagExpanded)
|
||||
const toggleCart = useBag((state) => state.toggleBag)
|
||||
const isMobile = useIsMobile()
|
||||
@ -167,8 +167,8 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName }: Ac
|
||||
<BuyCell
|
||||
event={event}
|
||||
collectionName={collectionName}
|
||||
selectAsset={addAssetToBag}
|
||||
removeAsset={removeAssetFromBag}
|
||||
selectAsset={addAssetsToBag}
|
||||
removeAsset={removeAssetsFromBag}
|
||||
itemsInBag={itemsInBag}
|
||||
cartExpanded={cartExpanded}
|
||||
toggleCart={toggleCart}
|
||||
|
@ -45,8 +45,8 @@ const formatListingStatus = (status: OrderStatus): string => {
|
||||
interface BuyCellProps {
|
||||
event: ActivityEvent
|
||||
collectionName: string
|
||||
selectAsset: (asset: GenieAsset) => void
|
||||
removeAsset: (asset: GenieAsset) => void
|
||||
selectAsset: (assets: GenieAsset[]) => void
|
||||
removeAsset: (assets: GenieAsset[]) => void
|
||||
itemsInBag: BagItem[]
|
||||
cartExpanded: boolean
|
||||
toggleCart: () => void
|
||||
@ -81,7 +81,7 @@ export const BuyCell = ({
|
||||
className={event.orderStatus === OrderStatus.VALID && isSelected ? styles.removeCell : styles.buyCell}
|
||||
onClick={(e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
isSelected ? removeAsset(asset) : selectAsset(asset)
|
||||
isSelected ? removeAsset([asset]) : selectAsset([asset])
|
||||
!isSelected && !cartExpanded && !isMobile && toggleCart()
|
||||
}}
|
||||
disabled={event.orderStatus !== OrderStatus.VALID}
|
||||
|
@ -31,8 +31,8 @@ export const CollectionAsset = ({
|
||||
setCurrentTokenPlayingMedia,
|
||||
rarityVerified,
|
||||
}: CollectionAssetProps) => {
|
||||
const addAssetToBag = useBag((state) => state.addAssetToBag)
|
||||
const removeAssetFromBag = useBag((state) => state.removeAssetFromBag)
|
||||
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((state) => state.removeAssetsFromBag)
|
||||
const itemsInBag = useBag((state) => state.itemsInBag)
|
||||
const bagExpanded = useBag((state) => state.bagExpanded)
|
||||
const toggleBag = useBag((state) => state.toggleBag)
|
||||
@ -124,12 +124,12 @@ export const CollectionAsset = ({
|
||||
selectedChildren={'Remove'}
|
||||
onClick={(e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
addAssetToBag(asset)
|
||||
addAssetsToBag([asset])
|
||||
!bagExpanded && !isMobile && toggleBag()
|
||||
}}
|
||||
onSelectedClick={(e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
removeAssetFromBag(asset)
|
||||
removeAssetsFromBag([asset])
|
||||
}}
|
||||
>
|
||||
{'Buy now'}
|
||||
|
@ -5,7 +5,6 @@ import { sprinkles } from '../../css/sprinkles.css'
|
||||
export const assetList = style([
|
||||
sprinkles({
|
||||
display: 'grid',
|
||||
marginTop: '24',
|
||||
gap: { sm: '8', md: '12', lg: '20' },
|
||||
}),
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
import clsx from 'clsx'
|
||||
import { loadingAnimation } from 'components/Loader/styled'
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
import { AnimatedBox, Box } from 'nft/components/Box'
|
||||
import { CollectionSearch, FilterButton } from 'nft/components/collection'
|
||||
@ -6,13 +7,14 @@ import { CollectionAsset } from 'nft/components/collection/CollectionAsset'
|
||||
import * as styles from 'nft/components/collection/CollectionNfts.css'
|
||||
import { SortDropdown } from 'nft/components/common/SortDropdown'
|
||||
import { Center, Row } from 'nft/components/Flex'
|
||||
import { NonRarityIcon, RarityIcon } from 'nft/components/icons'
|
||||
import { NonRarityIcon, RarityIcon, SweepIcon } from 'nft/components/icons'
|
||||
import { bodySmall, buttonTextMedium, headlineMedium } from 'nft/css/common.css'
|
||||
import { vars } from 'nft/css/sprinkles.css'
|
||||
import {
|
||||
CollectionFilters,
|
||||
initialCollectionFilterState,
|
||||
SortBy,
|
||||
useBag,
|
||||
useCollectionFilters,
|
||||
useFiltersExpanded,
|
||||
useIsMobile,
|
||||
@ -20,7 +22,7 @@ import {
|
||||
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
|
||||
import { usePriceRange } from 'nft/hooks/usePriceRange'
|
||||
import { AssetsFetcher } from 'nft/queries'
|
||||
import { DropDownOption, GenieCollection, UniformHeight, UniformHeights } from 'nft/types'
|
||||
import { DropDownOption, GenieCollection, TokenType, UniformHeight, UniformHeights } from 'nft/types'
|
||||
import { getRarityStatus } from 'nft/utils/asset'
|
||||
import { pluralize } from 'nft/utils/roundAndPluralize'
|
||||
import { scrollToTop } from 'nft/utils/scrollToTop'
|
||||
@ -29,10 +31,12 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||
import { useInfiniteQuery } from 'react-query'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { CollectionAssetLoading } from './CollectionAssetLoading'
|
||||
import { marketPlaceItems } from './MarketplaceSelect'
|
||||
import { Sweep } from './Sweep'
|
||||
import { TraitChip } from './TraitChip'
|
||||
|
||||
interface CollectionNftsProps {
|
||||
@ -43,6 +47,12 @@ interface CollectionNftsProps {
|
||||
|
||||
const rarityStatusCache = new Map<string, boolean>()
|
||||
|
||||
const ActionsContainer = styled.div`
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
const ClearAllButton = styled.button`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
padding-left: 8px;
|
||||
@ -54,6 +64,45 @@ const ClearAllButton = styled.button`
|
||||
background: none;
|
||||
`
|
||||
|
||||
const SweepButton = styled.div<{ toggled: boolean; disabled?: boolean }>`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 10px 18px 10px 12px;
|
||||
cursor: ${({ disabled }) => (disabled ? 'auto' : 'pointer')};
|
||||
color: ${({ toggled, disabled, theme }) => (toggled && !disabled ? theme.white : theme.textPrimary)};
|
||||
background: ${({ theme, toggled, disabled }) =>
|
||||
!disabled && toggled
|
||||
? 'radial-gradient(101.8% 4091.31% at 0% 0%, #4673FA 0%, #9646FA 100%)'
|
||||
: theme.backgroundInteractive};
|
||||
opacity: ${({ disabled }) => (disabled ? 0.4 : 1)};
|
||||
:hover {
|
||||
background-color: ${({ theme }) => theme.hoverState};
|
||||
transition: ${({
|
||||
theme: {
|
||||
transition: { duration, timing },
|
||||
},
|
||||
}) => `${duration.fast} background-color ${timing.in}`};
|
||||
}
|
||||
`
|
||||
|
||||
export const LoadingButton = styled.div`
|
||||
border-radius: 12px;
|
||||
height: 44px;
|
||||
width: 114px;
|
||||
animation: ${loadingAnimation} 1.5s infinite;
|
||||
animation-fill-mode: both;
|
||||
background: linear-gradient(
|
||||
to left,
|
||||
${({ theme }) => theme.backgroundInteractive} 25%,
|
||||
${({ theme }) => theme.backgroundOutline} 50%,
|
||||
${({ theme }) => theme.backgroundInteractive} 75%
|
||||
);
|
||||
will-change: background-position;
|
||||
background-size: 400%;
|
||||
`
|
||||
|
||||
export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => {
|
||||
const traits = useCollectionFilters((state) => state.traits)
|
||||
const minPrice = useCollectionFilters((state) => state.minPrice)
|
||||
@ -78,10 +127,17 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
const setMin = useCollectionFilters((state) => state.setMinPrice)
|
||||
const setMax = useCollectionFilters((state) => state.setMaxPrice)
|
||||
|
||||
const toggleBag = useBag((state) => state.toggleBag)
|
||||
const bagExpanded = useBag((state) => state.bagExpanded)
|
||||
|
||||
const theme = useTheme()
|
||||
|
||||
const debouncedMinPrice = useDebounce(minPrice, 500)
|
||||
const debouncedMaxPrice = useDebounce(maxPrice, 500)
|
||||
const debouncedSearchByNameText = useDebounce(searchByNameText, 500)
|
||||
|
||||
const [sweepIsOpen, setSweepOpen] = useState(false)
|
||||
|
||||
const {
|
||||
data: collectionAssets,
|
||||
isSuccess: AssetsFetchSuccess,
|
||||
@ -213,6 +269,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
|
||||
useEffect(() => {
|
||||
setUniformHeight(UniformHeights.unset)
|
||||
setSweepOpen(false)
|
||||
return () => {
|
||||
useCollectionFilters.setState(initialCollectionFilterState)
|
||||
}
|
||||
@ -236,6 +293,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
)
|
||||
|
||||
const hasNfts = collectionNfts && collectionNfts.length > 0
|
||||
const hasErc1155s = hasNfts && collectionNfts[0] && collectionNfts[0].tokenType === TokenType.ERC1155
|
||||
|
||||
const minMaxPriceChipText: string | undefined = useMemo(() => {
|
||||
if (debouncedMinPrice && debouncedMaxPrice) {
|
||||
@ -294,24 +352,61 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatedBox position="sticky" top="72" width="full" zIndex="3">
|
||||
<Box
|
||||
backgroundColor="backgroundFloating"
|
||||
width="full"
|
||||
paddingBottom="8"
|
||||
style={{ backdropFilter: 'blur(24px)' }}
|
||||
>
|
||||
<Row marginTop="12" gap="12">
|
||||
<FilterButton
|
||||
isMobile={isMobile}
|
||||
isFiltersExpanded={isFiltersExpanded}
|
||||
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
|
||||
collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
|
||||
/>
|
||||
<SortDropdown dropDownOptions={sortDropDownOptions} />
|
||||
<CollectionSearch />
|
||||
</Row>
|
||||
<Row paddingTop="12" gap="8" flexWrap="wrap">
|
||||
<AnimatedBox position="sticky" top="72" width="full" zIndex="3" marginBottom="20">
|
||||
<Box backgroundColor="backgroundFloating" width="full" style={{ backdropFilter: 'blur(24px)' }}>
|
||||
<ActionsContainer>
|
||||
<Row gap="12">
|
||||
<FilterButton
|
||||
isMobile={isMobile}
|
||||
isFiltersExpanded={isFiltersExpanded}
|
||||
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
|
||||
collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
|
||||
/>
|
||||
<SortDropdown dropDownOptions={sortDropDownOptions} />
|
||||
<CollectionSearch />
|
||||
</Row>
|
||||
{!hasErc1155s ? (
|
||||
isLoading ? (
|
||||
<LoadingButton />
|
||||
) : (
|
||||
<SweepButton
|
||||
toggled={sweepIsOpen}
|
||||
disabled={!buyNow}
|
||||
onClick={() => {
|
||||
if (!buyNow || hasErc1155s) return
|
||||
if (!sweepIsOpen) {
|
||||
scrollToTop()
|
||||
if (!bagExpanded && !isMobile) toggleBag()
|
||||
}
|
||||
setSweepOpen(!sweepIsOpen)
|
||||
}}
|
||||
>
|
||||
<SweepIcon width="24px" height="24px" />
|
||||
<ThemedText.BodyPrimary
|
||||
fontWeight={600}
|
||||
color={sweepIsOpen && buyNow ? theme.white : theme.textPrimary}
|
||||
lineHeight="20px"
|
||||
marginTop="2px"
|
||||
marginBottom="2px"
|
||||
>
|
||||
Sweep
|
||||
</ThemedText.BodyPrimary>
|
||||
</SweepButton>
|
||||
)
|
||||
) : null}
|
||||
</ActionsContainer>
|
||||
<Sweep
|
||||
contractAddress={contractAddress}
|
||||
collectionStats={collectionStats}
|
||||
minPrice={debouncedMinPrice}
|
||||
maxPrice={debouncedMaxPrice}
|
||||
showSweep={sweepIsOpen && buyNow && !hasErc1155s}
|
||||
/>
|
||||
<Row
|
||||
paddingTop={!!markets.length || !!traits.length || minMaxPriceChipText ? '12' : '0'}
|
||||
gap="8"
|
||||
flexWrap="wrap"
|
||||
>
|
||||
{markets.map((market) => (
|
||||
<TraitChip
|
||||
key={market}
|
||||
@ -347,7 +442,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{traits.length || markets.length > 0 || minMaxPriceChipText ? (
|
||||
{!!traits.length || !!markets.length || minMaxPriceChipText ? (
|
||||
<ClearAllButton
|
||||
onClick={() => {
|
||||
reset()
|
||||
|
475
src/nft/components/collection/Sweep.tsx
Normal file
475
src/nft/components/collection/Sweep.tsx
Normal file
@ -0,0 +1,475 @@
|
||||
import 'rc-slider/assets/index.css'
|
||||
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { formatEther, parseEther } from '@ethersproject/units'
|
||||
import { useBag, useCollectionFilters } from 'nft/hooks'
|
||||
import { fetchSweep } from 'nft/queries'
|
||||
import { GenieAsset, GenieCollection, Markets } from 'nft/types'
|
||||
import { calcPoolPrice, formatWeiToDecimal } from 'nft/utils'
|
||||
import { default as Slider } from 'rc-slider'
|
||||
import { useEffect, useMemo, useReducer, useState } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
const SweepContainer = styled.div<{ showSweep: boolean }>`
|
||||
display: ${({ showSweep }) => (showSweep ? 'flex' : 'none')};
|
||||
gap: 60px;
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
background-color: ${({ theme }) => theme.backgroundModule};
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
const StyledSlider = styled(Slider)`
|
||||
cursor: pointer;
|
||||
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const SweepLeftmostContainer = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 24px;
|
||||
`
|
||||
|
||||
const SweepRightmostContainer = styled.div`
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 160px;
|
||||
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const SweepHeaderContainer = styled.div`
|
||||
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const SweepSubContainer = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const InputContainer = styled.input`
|
||||
width: 96px;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
background: none;
|
||||
border-radius: 8px;
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 400px;
|
||||
line-height: 20px;
|
||||
|
||||
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
outline: none;
|
||||
border: 1px solid ${({ theme }) => theme.accentAction};
|
||||
}
|
||||
`
|
||||
|
||||
const ToggleContainer = styled.div`
|
||||
display: flex;
|
||||
border: 1px solid ${({ theme }) => theme.backgroundOutline};
|
||||
background: none;
|
||||
border-radius: 12px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const ToggleSwitch = styled.div<{ active: boolean }>`
|
||||
color: ${({ theme, active }) => (active ? theme.textPrimary : theme.textSecondary)};
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
background-color: ${({ theme, active }) => (active ? theme.backgroundInteractive : `none`)};
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
`
|
||||
|
||||
const NftDisplayContainer = styled.div`
|
||||
position: relative;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
`
|
||||
|
||||
const NftHolder = styled.div<{ index: number; src: string | undefined }>`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 4px;
|
||||
background: ${({ theme, src }) => (src ? `url(${src})` : theme.textTertiary)};
|
||||
background-size: 26px;
|
||||
opacity: ${({ src, index }) => (src ? 1.0 : index === 0 ? 0.9 : index === 1 ? 0.6 : 0.3)};
|
||||
transform: ${({ index }) =>
|
||||
index === 0
|
||||
? 'translate(-50%, -50%) rotate(-4.42deg)'
|
||||
: index === 1
|
||||
? 'translate(-50%, -50%) rotate(-14.01deg)'
|
||||
: 'translate(-50%, -50%) rotate(10.24deg)'};
|
||||
z-index: ${({ index }) => 3 - index};
|
||||
`
|
||||
|
||||
const wholeNumberRegex = RegExp(`^(0|[1-9][0-9]*)$`)
|
||||
const twoDecimalPlacesRegex = RegExp(`^\\d*\\.?\\d{0,2}$`)
|
||||
|
||||
interface NftDisplayProps {
|
||||
nfts: GenieAsset[]
|
||||
}
|
||||
|
||||
export const NftDisplay = ({ nfts }: NftDisplayProps) => {
|
||||
return (
|
||||
<NftDisplayContainer>
|
||||
{[...Array(3)].map((_, index) => {
|
||||
return (
|
||||
<NftHolder
|
||||
key={index}
|
||||
index={index}
|
||||
src={nfts.length - 1 >= index ? nfts[nfts.length - 1 - index].smallImageUrl : undefined}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</NftDisplayContainer>
|
||||
)
|
||||
}
|
||||
|
||||
interface SweepProps {
|
||||
contractAddress: string
|
||||
collectionStats: GenieCollection
|
||||
minPrice: string
|
||||
maxPrice: string
|
||||
showSweep: boolean
|
||||
}
|
||||
|
||||
export const Sweep = ({ contractAddress, collectionStats, minPrice, maxPrice, showSweep }: SweepProps) => {
|
||||
const theme = useTheme()
|
||||
|
||||
const [isItemsToggled, toggleSweep] = useReducer((state) => !state, true)
|
||||
const [sweepAmount, setSweepAmount] = useState<string>('')
|
||||
|
||||
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((state) => state.removeAssetsFromBag)
|
||||
const itemsInBag = useBag((state) => state.itemsInBag)
|
||||
const lockSweepItems = useBag((state) => state.lockSweepItems)
|
||||
|
||||
const traits = useCollectionFilters((state) => state.traits)
|
||||
const markets = useCollectionFilters((state) => state.markets)
|
||||
|
||||
const getSweepFetcherParams = (market: Markets.NFTX | Markets.NFT20 | 'others') => {
|
||||
const isMarketFiltered = !!markets.length
|
||||
const allOtherMarkets = [Markets.Opensea, Markets.X2Y2, Markets.LooksRare]
|
||||
|
||||
if (isMarketFiltered) {
|
||||
if (market === 'others') {
|
||||
return { contractAddress, traits, markets }
|
||||
}
|
||||
if (!markets.includes(market)) return { contractAddress: '', traits: [], markets: [] }
|
||||
}
|
||||
|
||||
switch (market) {
|
||||
case Markets.NFTX:
|
||||
case Markets.NFT20:
|
||||
return {
|
||||
contractAddress,
|
||||
traits,
|
||||
markets: [market],
|
||||
|
||||
price: {
|
||||
low: minPrice,
|
||||
high: maxPrice,
|
||||
symbol: 'ETH',
|
||||
},
|
||||
}
|
||||
case 'others':
|
||||
return {
|
||||
contractAddress,
|
||||
traits,
|
||||
markets: allOtherMarkets,
|
||||
|
||||
price: {
|
||||
low: minPrice,
|
||||
high: maxPrice,
|
||||
symbol: 'ETH',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: collectionAssets, isFetched: isCollectionAssetsFetched } = useQuery(
|
||||
['sweepAssets', getSweepFetcherParams('others')],
|
||||
() => fetchSweep(getSweepFetcherParams('others')),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
|
||||
const { data: nftxCollectionAssets, isFetched: isNftxCollectionAssetsFetched } = useQuery(
|
||||
['nftxSweepAssets', collectionStats, getSweepFetcherParams(Markets.NFTX)],
|
||||
() =>
|
||||
collectionStats.marketplaceCount?.some(
|
||||
(marketStat) => marketStat.marketplace === Markets.NFTX && marketStat.count > 0
|
||||
)
|
||||
? fetchSweep(getSweepFetcherParams(Markets.NFTX))
|
||||
: [],
|
||||
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
|
||||
const { data: nft20CollectionAssets, isFetched: isNft20CollectionAssetsFetched } = useQuery(
|
||||
['nft20SweepAssets', getSweepFetcherParams(Markets.NFT20)],
|
||||
() =>
|
||||
collectionStats.marketplaceCount?.some(
|
||||
(marketStat) => marketStat.marketplace === Markets.NFT20 && marketStat.count > 0
|
||||
)
|
||||
? fetchSweep(getSweepFetcherParams(Markets.NFT20))
|
||||
: [],
|
||||
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
|
||||
const allAssetsFetched = isCollectionAssetsFetched && isNftxCollectionAssetsFetched && isNft20CollectionAssetsFetched
|
||||
|
||||
const { sortedAssets, sortedAssetsTotalEth } = useMemo(() => {
|
||||
if (!allAssetsFetched || !collectionAssets || !nftxCollectionAssets || !nft20CollectionAssets)
|
||||
return { sortedAssets: undefined, sortedAssetsTotalEth: BigNumber.from(0) }
|
||||
|
||||
let counterNFTX = 0
|
||||
let counterNFT20 = 0
|
||||
|
||||
let jointCollections = [...nftxCollectionAssets, ...nft20CollectionAssets]
|
||||
|
||||
jointCollections.forEach((asset) => {
|
||||
if (!asset.openseaSusFlag) {
|
||||
const isNFTX = asset.marketplace === Markets.NFTX
|
||||
asset.currentEthPrice = calcPoolPrice(asset, isNFTX ? counterNFTX : counterNFT20)
|
||||
BigNumber.from(asset.currentEthPrice).gte(0) && (isNFTX ? counterNFTX++ : counterNFT20++)
|
||||
}
|
||||
})
|
||||
|
||||
jointCollections = collectionAssets.concat(jointCollections)
|
||||
|
||||
jointCollections.sort((a, b) => {
|
||||
return BigNumber.from(a.currentEthPrice).gt(BigNumber.from(b.currentEthPrice)) ? 1 : -1
|
||||
})
|
||||
|
||||
let validAssets = jointCollections.filter(
|
||||
(asset) => BigNumber.from(asset.currentEthPrice).gte(0) && !asset.openseaSusFlag
|
||||
)
|
||||
|
||||
validAssets = validAssets.slice(
|
||||
0,
|
||||
Math.max(collectionAssets.length, nftxCollectionAssets.length, nft20CollectionAssets.length)
|
||||
)
|
||||
|
||||
return {
|
||||
sortedAssets: validAssets,
|
||||
sortedAssetsTotalEth: validAssets.reduce(
|
||||
(total, asset) => total.add(BigNumber.from(asset.priceInfo.ETHPrice)),
|
||||
BigNumber.from(0)
|
||||
),
|
||||
}
|
||||
}, [collectionAssets, nftxCollectionAssets, nft20CollectionAssets, allAssetsFetched])
|
||||
|
||||
const { sweepItemsInBag, sweepEthPrice } = useMemo(() => {
|
||||
const sweepItemsInBag = itemsInBag
|
||||
.filter((item) => item.inSweep && item.asset.address === contractAddress)
|
||||
.map((item) => item.asset)
|
||||
|
||||
const sweepEthPrice = sweepItemsInBag.reduce(
|
||||
(total, asset) => total.add(BigNumber.from(asset.priceInfo.ETHPrice)),
|
||||
BigNumber.from(0)
|
||||
)
|
||||
|
||||
return { sweepItemsInBag, sweepEthPrice }
|
||||
}, [itemsInBag, contractAddress])
|
||||
|
||||
useEffect(() => {
|
||||
if (sweepItemsInBag.length === 0) setSweepAmount('')
|
||||
}, [sweepItemsInBag])
|
||||
|
||||
useEffect(() => {
|
||||
lockSweepItems(contractAddress)
|
||||
}, [contractAddress, traits, markets, minPrice, maxPrice, lockSweepItems])
|
||||
|
||||
const clearSweep = () => {
|
||||
setSweepAmount('')
|
||||
removeAssetsFromBag(sweepItemsInBag)
|
||||
}
|
||||
|
||||
const handleSweep = (value: number) => {
|
||||
if (sortedAssets) {
|
||||
if (isItemsToggled) {
|
||||
if (sweepItemsInBag.length < value) {
|
||||
addAssetsToBag(sortedAssets.slice(sweepItemsInBag.length, value), true)
|
||||
} else {
|
||||
removeAssetsFromBag(sweepItemsInBag.slice(value, sweepItemsInBag.length))
|
||||
}
|
||||
setSweepAmount(value < 1 ? '' : value.toString())
|
||||
} else {
|
||||
const wishValueInWei = parseEther(value.toString())
|
||||
if (sweepEthPrice.lte(wishValueInWei)) {
|
||||
let curIndex = sweepItemsInBag.length
|
||||
let curTotal = sweepEthPrice
|
||||
const wishAssets: GenieAsset[] = []
|
||||
|
||||
while (
|
||||
curIndex < sortedAssets.length &&
|
||||
curTotal.add(BigNumber.from(sortedAssets[curIndex].priceInfo.ETHPrice)).lte(wishValueInWei)
|
||||
) {
|
||||
wishAssets.push(sortedAssets[curIndex])
|
||||
curTotal = curTotal.add(BigNumber.from(sortedAssets[curIndex].priceInfo.ETHPrice))
|
||||
curIndex++
|
||||
}
|
||||
|
||||
if (wishAssets.length > 0) {
|
||||
addAssetsToBag(wishAssets, true)
|
||||
}
|
||||
} else {
|
||||
let curIndex = sweepItemsInBag.length - 1
|
||||
let curTotal = sweepEthPrice
|
||||
const wishAssets: GenieAsset[] = []
|
||||
|
||||
while (curIndex >= 0 && curTotal.gt(wishValueInWei)) {
|
||||
wishAssets.push(sweepItemsInBag[curIndex])
|
||||
curTotal = curTotal.sub(BigNumber.from(sweepItemsInBag[curIndex].priceInfo.ETHPrice))
|
||||
curIndex--
|
||||
}
|
||||
|
||||
if (wishAssets.length > 0) {
|
||||
removeAssetsFromBag(wishAssets)
|
||||
}
|
||||
}
|
||||
|
||||
setSweepAmount(value === 0 ? '' : value.toFixed(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleSliderChange = (value: number | number[]) => {
|
||||
if (typeof value === 'number') {
|
||||
if (sortedAssets) {
|
||||
if (isItemsToggled) {
|
||||
if (Math.floor(value) !== Math.floor(sweepAmount !== '' ? parseFloat(sweepAmount) : 0))
|
||||
handleSweep(Math.floor(value))
|
||||
setSweepAmount(value < 1 ? '' : value.toString())
|
||||
} else {
|
||||
handleSweep(value)
|
||||
setSweepAmount(value === 0 ? '' : value.toFixed(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleInput = (value: string) => {
|
||||
if (value === '') {
|
||||
handleSweep(0)
|
||||
setSweepAmount('')
|
||||
} else if (isItemsToggled && wholeNumberRegex.test(value)) {
|
||||
handleSweep(parseFloat(value))
|
||||
setSweepAmount(value)
|
||||
} else if (!isItemsToggled && twoDecimalPlacesRegex.test(value)) {
|
||||
handleSweep(parseFloat(value))
|
||||
setSweepAmount(value)
|
||||
}
|
||||
}
|
||||
|
||||
const handleToggleSweep = () => {
|
||||
clearSweep()
|
||||
toggleSweep()
|
||||
}
|
||||
|
||||
return (
|
||||
<SweepContainer showSweep={showSweep}>
|
||||
<SweepLeftmostContainer>
|
||||
<SweepHeaderContainer>
|
||||
<ThemedText.SubHeaderSmall color="textPrimary" lineHeight="20px" paddingTop="6px" paddingBottom="6px">
|
||||
Sweep
|
||||
</ThemedText.SubHeaderSmall>
|
||||
</SweepHeaderContainer>
|
||||
<SweepSubContainer>
|
||||
<StyledSlider
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
max={isItemsToggled ? sortedAssets?.length ?? 0 : parseFloat(formatEther(sortedAssetsTotalEth).toString())}
|
||||
value={isItemsToggled ? sweepItemsInBag.length : parseFloat(formatWeiToDecimal(sweepEthPrice.toString()))}
|
||||
step={isItemsToggled ? 1 : 0.01}
|
||||
trackStyle={{
|
||||
top: '3px',
|
||||
height: '8px',
|
||||
background: `radial-gradient(101.8% 4091.31% at 0% 0%, #4673FA 0%, #9646FA 100%)`,
|
||||
}}
|
||||
handleStyle={{
|
||||
top: '3px',
|
||||
width: '12px',
|
||||
height: '20px',
|
||||
backgroundColor: `${theme.textPrimary}`,
|
||||
borderRadius: '4px',
|
||||
border: 'none',
|
||||
boxShadow: `${theme.shallowShadow.slice(0, -1)}`,
|
||||
}}
|
||||
railStyle={{
|
||||
top: '3px',
|
||||
height: '8px',
|
||||
backgroundColor: `${theme.accentActionSoft}`,
|
||||
}}
|
||||
onChange={handleSliderChange}
|
||||
/>
|
||||
<InputContainer
|
||||
inputMode="decimal"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
// text-specific options
|
||||
type="text"
|
||||
pattern="^[0-9]*[.,]?[0-9]*$"
|
||||
placeholder="0"
|
||||
minLength={1}
|
||||
maxLength={79}
|
||||
spellCheck="false"
|
||||
value={
|
||||
isItemsToggled ? (sweepAmount !== '' ? Math.floor(parseFloat(sweepAmount)) : sweepAmount) : sweepAmount
|
||||
}
|
||||
onChange={(event) => {
|
||||
handleInput(event.target.value.replace(/,/g, '.'))
|
||||
}}
|
||||
/>
|
||||
<ToggleContainer onClick={handleToggleSweep}>
|
||||
<ToggleSwitch active={isItemsToggled}>Items</ToggleSwitch>
|
||||
<ToggleSwitch active={!isItemsToggled}>ETH</ToggleSwitch>
|
||||
</ToggleContainer>
|
||||
</SweepSubContainer>
|
||||
</SweepLeftmostContainer>
|
||||
<SweepRightmostContainer>
|
||||
<ThemedText.SubHeader font-size="14px">{`${formatWeiToDecimal(
|
||||
sweepEthPrice.toString()
|
||||
)} ETH`}</ThemedText.SubHeader>
|
||||
<NftDisplay nfts={sweepItemsInBag} />
|
||||
</SweepRightmostContainer>
|
||||
</SweepContainer>
|
||||
)
|
||||
}
|
@ -133,7 +133,7 @@ export const SortDropdown = ({
|
||||
</Box>
|
||||
<Box
|
||||
position="absolute"
|
||||
zIndex="2"
|
||||
zIndex="3"
|
||||
width={inFilters ? 'auto' : 'inherit'}
|
||||
right={inFilters ? '16' : 'auto'}
|
||||
paddingBottom="8"
|
||||
|
@ -112,8 +112,8 @@ interface AssetDetailsProps {
|
||||
export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
const { pathname, search } = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const addAssetToBag = useBag((state) => state.addAssetToBag)
|
||||
const removeAssetFromBag = useBag((state) => state.removeAssetFromBag)
|
||||
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((state) => state.removeAssetsFromBag)
|
||||
const itemsInBag = useBag((state) => state.itemsInBag)
|
||||
const bagExpanded = useBag((state) => state.bagExpanded)
|
||||
const [creatorAddress, setCreatorAddress] = useState('')
|
||||
@ -387,8 +387,8 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
boxShadow={{ hover: 'elevation' }}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
removeAssetFromBag(asset)
|
||||
} else addAssetToBag(asset)
|
||||
removeAssetsFromBag([asset])
|
||||
} else addAssetsToBag([asset])
|
||||
setSelected((x) => !x)
|
||||
}}
|
||||
>
|
||||
|
@ -187,8 +187,8 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
|
||||
const cheapestOrder = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
|
||||
const expirationDate = cheapestOrder ? new Date(cheapestOrder.orderClosingDate) : undefined
|
||||
const itemsInBag = useBag((s) => s.itemsInBag)
|
||||
const addAssetToBag = useBag((s) => s.addAssetToBag)
|
||||
const removeAssetFromBag = useBag((s) => s.removeAssetFromBag)
|
||||
const addAssetsToBag = useBag((s) => s.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((s) => s.removeAssetsFromBag)
|
||||
const isErc1555 = asset.tokenType === TokenType.ERC1155
|
||||
|
||||
const { quantity, assetInBag } = useMemo(() => {
|
||||
@ -235,19 +235,19 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
|
||||
assetInBag={assetInBag}
|
||||
margin={true}
|
||||
useAccentColor={true}
|
||||
onClick={() => (assetInBag ? removeAssetFromBag(asset) : addAssetToBag(asset))}
|
||||
onClick={() => (assetInBag ? removeAssetsFromBag([asset]) : addAssetsToBag([asset]))}
|
||||
>
|
||||
<ThemedText.SubHeader lineHeight={'20px'}>{assetInBag ? 'Remove' : 'Buy Now'}</ThemedText.SubHeader>
|
||||
</BuyNowButton>
|
||||
) : (
|
||||
<Erc1155BuyNowButton>
|
||||
<Erc1155ChangeButton remove={true} onClick={() => removeAssetFromBag(asset)}>
|
||||
<Erc1155ChangeButton remove={true} onClick={() => removeAssetsFromBag([asset])}>
|
||||
<MinusIcon width="20px" height="20px" />
|
||||
</Erc1155ChangeButton>
|
||||
<Erc1155BuyNowText>
|
||||
<ThemedText.SubHeader lineHeight={'20px'}>{quantity}</ThemedText.SubHeader>
|
||||
</Erc1155BuyNowText>
|
||||
<Erc1155ChangeButton remove={false} onClick={() => addAssetToBag(asset)}>
|
||||
<Erc1155ChangeButton remove={false} onClick={() => addAssetsToBag([asset])}>
|
||||
<PlusIcon width="20px" height="20px" />
|
||||
</Erc1155ChangeButton>
|
||||
</Erc1155BuyNowButton>
|
||||
|
@ -398,29 +398,16 @@ export const DownloadIcon = (props: SVGProps) => (
|
||||
)
|
||||
|
||||
export const SweepIcon = (props: SVGProps) => (
|
||||
<svg {...props} width="23" height="23" viewBox="0 0 23 23" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M22.5957 4.81261C22.8834 5.10528 23.0178 5.41067 22.9989 5.72877C22.98 6.04687 22.8004 6.354 22.46 6.65017L15.1344 13.1029L13.2046 11.1637L19.6611 3.84484C19.9593 3.5078 20.2663 3.32781 20.5824 3.30487C20.8984 3.28194 21.2018 3.41655 21.4925 3.70872L22.5957 4.81261Z"
|
||||
fill="white"
|
||||
d="M13.4177 11.9534C12.3508 11.6675 11.2541 12.3006 10.9682 13.3676C9.90129 13.0817 8.80461 13.7148 8.51873 14.7818L8.25991 15.7477M13.4177 11.9534C14.4846 12.2392 15.1178 13.3359 14.8319 14.4028C15.8989 14.6887 16.532 15.7855 16.2461 16.8524L15.9873 17.8183M13.4177 11.9534L16.0059 2.2941M8.25991 15.7477L15.9873 17.8183M8.25991 15.7477C8.25991 15.7477 7.74227 17.6796 7.48345 18.6455C7.22463 19.6114 5.99989 20.3185 5.99989 20.3185C9.86359 21.3538 12.3131 19.9396 12.3131 19.9396L11.7954 21.8714C13.4053 22.3028 14.9197 21.8027 15.2109 20.716L15.9873 17.8183"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.5252 14.1402C14.7538 14.3706 14.8683 14.6161 14.8688 14.8769C14.8693 15.1376 14.7582 15.3795 14.5356 15.6023L14.2375 15.8955C14.0118 16.1224 13.7723 16.2338 13.5189 16.2298C13.2655 16.2258 13.0227 16.1091 12.7907 15.8798L10.2564 13.3317C10.0238 13.0984 9.90678 12.8538 9.90529 12.5981C9.9038 12.3423 10.0156 12.1012 10.2407 11.8749L10.5277 11.5817C10.7498 11.3588 10.9901 11.2466 11.2485 11.2451C11.5069 11.2436 11.7523 11.3593 11.9849 11.5922L14.5252 14.1402Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M6.54973 15.645C6.5695 15.6425 6.58747 15.6323 6.5997 15.6165C6.61193 15.6007 6.61742 15.5807 6.61495 15.5609C6.61248 15.541 6.60225 15.523 6.58653 15.5107C6.5708 15.4985 6.55086 15.493 6.53109 15.4954C6.35891 15.4954 5.1402 15.4954 4.15406 15.3773C3.30371 15.2745 2.46362 15.0994 1.64286 14.8538C1.60087 14.8372 1.56568 14.8069 1.54298 14.7677C1.25974 14.0415 1.17029 13.5225 1.02122 12.8547C0.982456 12.7215 0.978729 12.6011 1.08159 12.4949C1.18446 12.3887 1.31713 12.3925 1.45056 12.4343C3.00916 12.9197 4.42613 13.2428 6.05106 13.4171C7.49338 13.5719 8.92899 13.5614 10.3467 13.2084C10.4137 13.1821 10.4835 13.1636 10.5547 13.1531L13.0487 15.3922C13.0487 15.5126 13.0487 15.5111 13.0487 15.6301C13.0398 16.6272 12.9814 17.6244 12.8736 18.6216C12.8009 19.326 12.6853 20.0254 12.5277 20.7157C12.3943 21.2878 12.2296 21.928 11.9448 22.4478C11.7823 22.7425 11.5334 22.8479 11.2061 22.7814C10.6657 22.7118 10.0299 22.6273 9.66767 22.4471C10.2275 21.6655 10.6866 20.4988 10.9684 19.5266C11.0618 19.1816 11.1379 18.8321 11.1965 18.4795C11.2023 18.4604 11.2003 18.4397 11.1909 18.422C11.1816 18.4043 11.1656 18.3911 11.1465 18.3853C11.1274 18.3794 11.1068 18.3814 11.0892 18.3908C11.0716 18.4002 11.0584 18.4162 11.0526 18.4354C10.8469 18.9051 10.5308 19.5841 10.1976 20.1727C9.8488 20.7845 8.56674 22.1075 8.33418 21.9811C7.17659 21.501 6.26499 20.967 5.36755 20.1929C5.35488 20.1824 6.56836 19.5198 7.50084 18.9215C7.75278 18.7592 9.05496 17.6636 9.28976 17.3293C9.29671 17.3222 9.30221 17.3139 9.30595 17.3047C9.3097 17.2956 9.3116 17.2858 9.31157 17.2759C9.31153 17.266 9.30956 17.2562 9.30575 17.2471C9.30194 17.2379 9.29638 17.2296 9.28939 17.2227C9.28239 17.2157 9.27409 17.2102 9.26496 17.2064C9.25583 17.2027 9.24606 17.2008 9.2362 17.2008C9.22633 17.2008 9.21658 17.2028 9.20748 17.2066C9.19838 17.2105 9.19012 17.216 9.18317 17.2231L9.10863 17.2777C8.55034 17.674 7.2802 18.2858 6.14648 18.656C5.59735 18.8275 5.03372 18.9482 4.46265 19.0165C4.15183 19.0344 4.14288 19.0255 3.90659 18.7846C3.1141 17.9679 2.48015 17.0103 2.03717 15.9606C2.02152 15.9374 3.37886 16.0294 4.58862 15.9606C5.25025 15.913 5.90645 15.8074 6.54973 15.645Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M8.03215 3.20189C7.96945 2.92219 7.91547 2.70033 7.87341 2.53869C7.83915 2.39971 7.77253 2.27076 7.67895 2.16232C7.57558 2.06162 7.4479 1.98929 7.30829 1.95235C7.1432 1.90085 6.91144 1.84538 6.61856 1.78596C6.53919 1.76773 6.49633 1.72574 6.49633 1.65918C6.49528 1.64336 6.49773 1.62748 6.50349 1.6127C6.50925 1.59792 6.51818 1.58457 6.52966 1.5736C6.55489 1.55175 6.58564 1.53723 6.61856 1.53162C6.85148 1.48841 7.08176 1.43207 7.30829 1.36285C7.44771 1.32418 7.57517 1.25116 7.67895 1.15049C7.77253 1.04206 7.83915 0.913108 7.87341 0.774127C7.91944 0.611431 7.97236 0.391955 8.03215 0.11569C8.04723 0.0364546 8.08771 0 8.15438 0C8.22105 0 8.26312 0.0388317 8.28217 0.11569C8.3409 0.391955 8.39381 0.611431 8.44091 0.774127C8.47622 0.913188 8.54363 1.04208 8.63774 1.15049C8.74183 1.25117 8.86954 1.32418 9.0092 1.36285C9.23565 1.43236 9.46594 1.48871 9.69893 1.53162C9.73114 1.53346 9.76134 1.54787 9.78302 1.57173C9.80469 1.59559 9.81611 1.627 9.81481 1.65918C9.81481 1.72574 9.77592 1.76773 9.69893 1.78596C9.46636 1.8298 9.23616 1.88534 9.0092 1.95235C8.86936 1.9893 8.74142 2.06162 8.63774 2.16232C8.54363 2.27074 8.47622 2.39963 8.44091 2.53869C8.39249 2.69716 8.33931 2.92219 8.28217 3.20189C8.26312 3.28112 8.22105 3.31836 8.15438 3.31836C8.08771 3.31836 8.04564 3.27954 8.03215 3.20189Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12.3409 5.87055C12.2751 5.52982 12.2134 5.25823 12.1576 5.05662C12.1161 4.88332 12.0332 4.72267 11.9161 4.58843C11.7866 4.46353 11.6275 4.37354 11.4538 4.32685C11.1741 4.24532 10.8898 4.18022 10.6025 4.1319C10.5017 4.1169 10.4509 4.06442 10.4509 3.97528C10.4498 3.955 10.4529 3.93472 10.4599 3.91566C10.4669 3.89661 10.4777 3.8792 10.4917 3.86448C10.5223 3.83612 10.5611 3.81806 10.6025 3.81283C10.8896 3.76432 11.1738 3.70007 11.4538 3.62038C11.6283 3.57572 11.7879 3.48545 11.9161 3.3588C12.0329 3.22336 12.1157 3.06199 12.1576 2.8881C12.2134 2.6865 12.2751 2.41491 12.3409 2.07334C12.3412 2.05339 12.3455 2.03371 12.3536 2.01549C12.3618 1.99727 12.3735 1.98089 12.3881 1.96734C12.4028 1.95378 12.42 1.94335 12.4388 1.93665C12.4576 1.92996 12.4776 1.92714 12.4975 1.92839C12.5808 1.92839 12.6349 1.9767 12.6549 2.07334C12.721 2.41491 12.7821 2.6865 12.8381 2.8881C12.8793 3.0616 12.9609 3.22289 13.0764 3.3588C13.204 3.4861 13.3638 3.57648 13.5386 3.62038C13.8198 3.69992 14.1051 3.76416 14.3933 3.81283C14.4144 3.81297 14.4352 3.8175 14.4545 3.82613C14.4738 3.83476 14.4911 3.84732 14.5053 3.86298C14.5194 3.87865 14.5302 3.89709 14.5369 3.91714C14.5436 3.93719 14.546 3.95841 14.544 3.97945C14.544 4.06859 14.494 4.12107 14.3933 4.13607C14.1049 4.18455 13.8195 4.24963 13.5386 4.33101C13.3646 4.37691 13.2053 4.46702 13.0764 4.5926C12.9606 4.72734 12.8789 4.88791 12.8381 5.06079C12.7826 5.2624 12.7215 5.53371 12.6549 5.87472C12.6349 5.97219 12.5833 6.02051 12.4975 6.02051C12.4772 6.02165 12.4568 6.0186 12.4378 6.01156C12.4187 6.00452 12.4013 5.99363 12.3866 5.97957C12.3719 5.9655 12.3603 5.94856 12.3524 5.92981C12.3445 5.91105 12.3406 5.89088 12.3409 5.87055Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M6.68457 11.7993C6.65283 11.8001 6.62123 11.7946 6.59165 11.783C6.56206 11.7715 6.53508 11.7541 6.51227 11.732C6.46495 11.6861 6.43286 11.6267 6.42032 11.5619C6.33379 11.0466 6.24828 10.6157 6.1638 10.2694C6.09886 9.97221 5.99165 9.68585 5.84546 9.41909C5.72369 9.205 5.5492 9.02565 5.3386 8.89806C5.08255 8.75332 4.80501 8.65049 4.51649 8.59348C4.17806 8.51773 3.75567 8.44583 3.24932 8.3778C3.18053 8.37018 3.11658 8.33874 3.06852 8.28891C3.02398 8.24144 2.99961 8.17853 3.00052 8.11343C2.99944 8.04831 3.02384 7.98533 3.06852 7.93795C3.11638 7.88786 3.18044 7.85636 3.24932 7.84905C3.75619 7.78412 4.17935 7.713 4.5188 7.63569C4.81087 7.57587 5.09204 7.47158 5.3525 7.32648C5.56664 7.20036 5.74549 7.02222 5.87251 6.80855C6.0212 6.54514 6.12856 6.26043 6.19084 5.9644C6.27171 5.61859 6.34898 5.18672 6.42264 4.66879C6.43207 4.60298 6.46337 4.54226 6.5115 4.49641C6.53457 4.47357 6.56197 4.45559 6.59209 4.4435C6.62221 4.43142 6.65444 4.42549 6.68689 4.42606C6.71949 4.4249 6.752 4.43026 6.78251 4.44181C6.81302 4.45335 6.84093 4.47086 6.8646 4.49331C6.9118 4.53934 6.94388 4.59867 6.95655 4.66338C7.04618 5.18132 7.13323 5.61318 7.21771 5.95899C7.28349 6.25595 7.39065 6.5422 7.53605 6.80933C7.65852 7.02267 7.83287 7.20162 8.04291 7.32958C8.29831 7.47496 8.57494 7.57931 8.86271 7.63879C9.19959 7.71609 9.6212 7.78721 10.1276 7.85215C10.1964 7.85945 10.2605 7.89095 10.3084 7.94105C10.3528 7.98851 10.3769 8.05149 10.3756 8.11652C10.3765 8.18217 10.3525 8.24572 10.3084 8.29432C10.2615 8.34542 10.1967 8.37643 10.1276 8.3809C9.7006 8.41009 9.27569 8.46403 8.85498 8.54246C8.56063 8.59403 8.27716 8.69518 8.01664 8.84162C7.79886 8.97348 7.61932 9.16002 7.49587 9.38275C7.34879 9.65617 7.24258 9.9497 7.18062 10.254C7.09924 10.6085 7.02454 11.0448 6.95655 11.5627C6.94854 11.6294 6.918 11.6913 6.87001 11.7382C6.8447 11.7602 6.81526 11.7769 6.78343 11.7874C6.75159 11.7978 6.71799 11.8019 6.68457 11.7993Z"
|
||||
d="M5.18229 6.58808C5.25706 6.38601 5.54287 6.38601 5.61764 6.58808C5.99377 7.60457 6.79521 8.406 7.8117 8.78214C8.01377 8.85691 8.01377 9.14272 7.8117 9.21749C6.79521 9.59363 5.99377 10.3951 5.61764 11.4116C5.54286 11.6136 5.25706 11.6136 5.18229 11.4116C4.80615 10.3951 4.00471 9.59363 2.98822 9.21749C2.78615 9.14272 2.78615 8.85691 2.98822 8.78214C4.00471 8.406 4.80615 7.60457 5.18229 6.58808Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { BagItem, BagItemStatus, BagStatus, UpdatedGenieAsset } from 'nft/types'
|
||||
import { BagItem, BagItemStatus, BagStatus, TokenType, UpdatedGenieAsset } from 'nft/types'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import create from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
@ -13,9 +13,10 @@ interface BagState {
|
||||
setTotalEthPrice: (totalEthPrice: BigNumber) => void
|
||||
totalUsdPrice: number | undefined
|
||||
setTotalUsdPrice: (totalUsdPrice: number | undefined) => void
|
||||
addAssetToBag: (asset: UpdatedGenieAsset) => void
|
||||
removeAssetFromBag: (asset: UpdatedGenieAsset) => void
|
||||
addAssetsToBag: (asset: UpdatedGenieAsset[], fromSweep?: boolean) => void
|
||||
removeAssetsFromBag: (assets: UpdatedGenieAsset[]) => void
|
||||
markAssetAsReviewed: (asset: UpdatedGenieAsset, toKeep: boolean) => void
|
||||
lockSweepItems: (contractAddress: string) => void
|
||||
didOpenUnavailableAssets: boolean
|
||||
setDidOpenUnavailableAssets: (didOpen: boolean) => void
|
||||
bagExpanded: boolean
|
||||
@ -76,34 +77,70 @@ export const useBag = create<BagState>()(
|
||||
set(() => ({
|
||||
totalUsdPrice,
|
||||
})),
|
||||
addAssetToBag: (asset) =>
|
||||
addAssetsToBag: (assets, fromSweep = false) =>
|
||||
set(({ itemsInBag }) => {
|
||||
if (get().isLocked) return { itemsInBag: get().itemsInBag }
|
||||
const assetWithId = { asset: { id: uuidv4(), ...asset }, status: BagItemStatus.ADDED_TO_BAG }
|
||||
const items: BagItem[] = []
|
||||
const itemsInBagCopy = [...itemsInBag]
|
||||
assets.forEach((asset) => {
|
||||
let index = -1
|
||||
if (asset.tokenType !== TokenType.ERC1155) {
|
||||
index = itemsInBag.findIndex(
|
||||
(n) => n.asset.tokenId === asset.tokenId && n.asset.address === asset.address
|
||||
)
|
||||
}
|
||||
if (index !== -1) {
|
||||
itemsInBagCopy[index].inSweep = fromSweep
|
||||
} else {
|
||||
const assetWithId = {
|
||||
asset: { id: uuidv4(), ...asset },
|
||||
status: BagItemStatus.ADDED_TO_BAG,
|
||||
inSweep: fromSweep,
|
||||
}
|
||||
items.push(assetWithId)
|
||||
}
|
||||
})
|
||||
if (itemsInBag.length === 0)
|
||||
return {
|
||||
itemsInBag: [assetWithId],
|
||||
itemsInBag: items,
|
||||
bagStatus: BagStatus.ADDING_TO_BAG,
|
||||
}
|
||||
else
|
||||
return {
|
||||
itemsInBag: [...itemsInBag, assetWithId],
|
||||
itemsInBag: [...itemsInBagCopy, ...items],
|
||||
bagStatus: BagStatus.ADDING_TO_BAG,
|
||||
}
|
||||
}),
|
||||
removeAssetFromBag: (asset) => {
|
||||
removeAssetsFromBag: (assets) => {
|
||||
set(({ itemsInBag }) => {
|
||||
if (get().isLocked) return { itemsInBag: get().itemsInBag }
|
||||
if (itemsInBag.length === 0) return { itemsInBag: [] }
|
||||
const itemsCopy = [...itemsInBag]
|
||||
const index = itemsCopy.findIndex((n) =>
|
||||
asset.id ? n.asset.id === asset.id : n.asset.tokenId === asset.tokenId && n.asset.address === asset.address
|
||||
const itemsCopy = itemsInBag.filter(
|
||||
(item) =>
|
||||
!assets.some((asset) =>
|
||||
asset.id
|
||||
? asset.id === item.asset.id
|
||||
: asset.tokenId === item.asset.tokenId && asset.address === item.asset.address
|
||||
)
|
||||
)
|
||||
if (index === -1) return { itemsInBag: get().itemsInBag }
|
||||
itemsCopy.splice(index, 1)
|
||||
return { itemsInBag: itemsCopy }
|
||||
})
|
||||
},
|
||||
lockSweepItems: (contractAddress) =>
|
||||
set(({ itemsInBag }) => {
|
||||
if (get().isLocked) return { itemsInBag: get().itemsInBag }
|
||||
const itemsInBagCopy = itemsInBag.map((item) =>
|
||||
item.asset.address === contractAddress && item.inSweep ? { ...item, inSweep: false } : item
|
||||
)
|
||||
if (itemsInBag.length === 0)
|
||||
return {
|
||||
itemsInBag,
|
||||
}
|
||||
else
|
||||
return {
|
||||
itemsInBag: [...itemsInBagCopy],
|
||||
}
|
||||
}),
|
||||
reset: () =>
|
||||
set(() => {
|
||||
if (!get().isLocked)
|
||||
|
@ -1,19 +1,31 @@
|
||||
import { Trait } from '../../hooks/useCollectionFilters'
|
||||
import { AssetPayload, GenieAsset } from '../../types'
|
||||
import { parseEther } from '@ethersproject/units'
|
||||
import { Trait } from 'nft/hooks/useCollectionFilters'
|
||||
import { AssetPayload, GenieAsset } from 'nft/types'
|
||||
|
||||
import { formatTraits } from './AssetsFetcher'
|
||||
|
||||
const formatPrice = (x: number | string) => parseEther(x.toString()).toString()
|
||||
|
||||
export const fetchSweep = async ({
|
||||
contractAddress,
|
||||
markets,
|
||||
traits = [],
|
||||
price,
|
||||
rarityRange,
|
||||
traits,
|
||||
}: {
|
||||
contractAddress: string
|
||||
markets?: string[]
|
||||
price?: { high?: number | string; low?: number | string; symbol: string }
|
||||
rarityRange?: Record<string, unknown>
|
||||
traits?: Trait[]
|
||||
}): Promise<GenieAsset[]> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_API_URL}/assets`
|
||||
const payload: AssetPayload = {
|
||||
filters: { address: contractAddress.toLowerCase(), traits: {}, notForSale: false },
|
||||
filters: {
|
||||
address: contractAddress.toLowerCase(),
|
||||
traits: {},
|
||||
...rarityRange,
|
||||
},
|
||||
fields: {
|
||||
address: 1,
|
||||
name: 1,
|
||||
@ -24,8 +36,10 @@ export const fetchSweep = async ({
|
||||
paymentToken: 1,
|
||||
animationUrl: 1,
|
||||
notForSale: 1,
|
||||
rarity: 1,
|
||||
tokenId: 1,
|
||||
},
|
||||
limit: 99,
|
||||
limit: 50,
|
||||
offset: 0,
|
||||
}
|
||||
|
||||
@ -37,9 +51,19 @@ export const fetchSweep = async ({
|
||||
payload.filters.traits = formatTraits(traits)
|
||||
}
|
||||
|
||||
const numberOfTraits = traits.filter((trait) => trait.trait_type === 'Number of traits')
|
||||
if (numberOfTraits) {
|
||||
payload.filters.numTraits = numberOfTraits.map((el) => ({ traitCount: el.trait_value }))
|
||||
const low = price?.low ? parseFloat(formatPrice(price.low)) : undefined
|
||||
const high = price?.high ? parseFloat(formatPrice(price.high)) : undefined
|
||||
|
||||
if (low || high) {
|
||||
payload.filters.currentEthPrice = {}
|
||||
}
|
||||
|
||||
if (low && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$gte = low
|
||||
}
|
||||
|
||||
if (high && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$lte = high
|
||||
}
|
||||
|
||||
const r = await fetch(url, {
|
||||
@ -49,7 +73,7 @@ export const fetchSweep = async ({
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const data = await r.json()
|
||||
|
||||
return data.data
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ export enum BagItemStatus {
|
||||
export type BagItem = {
|
||||
asset: UpdatedGenieAsset
|
||||
status: BagItemStatus
|
||||
inSweep?: boolean
|
||||
}
|
||||
|
||||
export enum BagStatus {
|
||||
|
@ -154,6 +154,8 @@ export interface GenieCollection {
|
||||
}
|
||||
|
||||
export enum Markets {
|
||||
LooksRare = 'looksrare',
|
||||
X2Y2 = 'x2y2',
|
||||
NFT20 = 'nft20',
|
||||
NFTX = 'nftx',
|
||||
Opensea = 'opensea',
|
||||
|
@ -51,6 +51,7 @@ export const ethNumberStandardFormatter = (
|
||||
const amountInDecimals = parseFloat(amount.toString())
|
||||
const conditionalDollarSign = includeDollarSign ? '$' : ''
|
||||
|
||||
if (amountInDecimals === 0) return '-'
|
||||
if (amountInDecimals < 0.0001) return `< ${conditionalDollarSign}0.00001`
|
||||
if (amountInDecimals < 1) return `${conditionalDollarSign}${amountInDecimals.toFixed(3)}`
|
||||
const formattedPrice = (removeZeroes ? parseFloat(amountInDecimals.toFixed(2)) : amountInDecimals.toFixed(2))
|
||||
|
Loading…
Reference in New Issue
Block a user