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

fix: [ListV2] Better Collection and Listing Retry and Removal Logic (#5938)

* keep approved collection status when removing

* better map name

* improve callback logic

* improve callback logic for collection approval

* reduce zustand calls

* additional zustand reductions

* add back correct check

---------
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 16bb9470
......@@ -211,7 +211,7 @@ export const ListingButton = ({ onClick, buttonText, showWarningOverride = false
const warningWrappedClick = () => {
if ((!disableListButton && canContinue) || showWarningOverride) {
if (issues && isNftListV2 && !showResolveIssues) toggleShowResolveIssues()
if (issues && isNftListV2) !showResolveIssues && toggleShowResolveIssues()
else if (listingsBelowFloor.length) setShowWarning(true)
else onClick()
} else addWarningMessages()
......
......@@ -12,6 +12,7 @@ import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { pluralize } from 'nft/utils/roundAndPluralize'
import { Dispatch, useEffect, useMemo, useRef, useState } from 'react'
import shallow from 'zustand/shallow'
import { ListingButton } from './ListingButton'
import * as styles from './ListingModal.css'
......@@ -21,18 +22,49 @@ import { approveCollectionRow, getTotalEthValue, pauseRow, resetRow, signListing
const ListingModal = () => {
const { provider } = useWeb3React()
const sellAssets = useSellAsset((state) => state.sellAssets)
const {
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
} = useNFTList(
({
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}) => ({
listingStatus,
setListingStatus,
setListings,
setCollectionsRequiringApproval,
setListingStatusAndCallback,
setCollectionStatusAndCallback,
looksRareNonce,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}),
shallow
)
const signer = provider?.getSigner()
const listings = useNFTList((state) => state.listings)
const setListings = useNFTList((state) => state.setListings)
const collectionsRequiringApproval = useNFTList((state) => state.collectionsRequiringApproval)
const setCollectionsRequiringApproval = useNFTList((state) => state.setCollectionsRequiringApproval)
const [openIndex, setOpenIndex] = useState(0)
const listingStatus = useNFTList((state) => state.listingStatus)
const setListingStatus = useNFTList((state) => state.setListingStatus)
const [allCollectionsApproved, setAllCollectionsApproved] = useState(false)
const looksRareNonce = useNFTList((state) => state.looksRareNonce)
const setLooksRareNonce = useNFTList((state) => state.setLooksRareNonce)
const getLooksRareNonce = useNFTList((state) => state.getLooksRareNonce)
const toggleCart = useBag((state) => state.toggleBag)
const looksRareNonceRef = useRef(looksRareNonce)
const isMobile = useIsMobile()
......@@ -120,20 +152,8 @@ const ListingModal = () => {
for (const collectionRow of collectionsRequiringApproval) {
verifyStatus(collectionRow.status) &&
(isMobile
? await approveCollectionRow(
collectionRow,
collectionsRequiringApproval,
setCollectionsRequiringApproval,
signer,
pauseAllRows
)
: approveCollectionRow(
collectionRow,
collectionsRequiringApproval,
setCollectionsRequiringApproval,
signer,
pauseAllRows
))
? await approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback, pauseAllRows)
: approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback, pauseAllRows))
}
}
......@@ -146,12 +166,11 @@ const ListingModal = () => {
verifyStatus(listing.status) &&
(await signListingRow(
listing,
listings,
setListings,
signer,
provider,
getLooksRareNonce,
setLooksRareNonce,
setListingStatusAndCallback,
pauseAllRows
))
}
......
......@@ -29,25 +29,16 @@ const updateStatus = ({
export async function approveCollectionRow(
collectionRow: CollectionRow,
collectionsRequiringApproval: CollectionRow[],
setCollectionsRequiringApproval: Dispatch<CollectionRow[]>,
signer: JsonRpcSigner,
setCollectionStatusAndCallback: (
collection: CollectionRow,
status: ListingStatus,
callback?: () => Promise<void>
) => void,
pauseAllRows?: () => void
) {
updateStatus({
listing: collectionRow,
newStatus: ListingStatus.SIGNING,
rows: collectionsRequiringApproval,
setRows: setCollectionsRequiringApproval as Dispatch<AssetRow[]>,
callback: () =>
approveCollectionRow(
collectionRow,
collectionsRequiringApproval,
setCollectionsRequiringApproval,
signer,
pauseAllRows
),
})
const callback = () => approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback, pauseAllRows)
setCollectionStatusAndCallback(collectionRow, ListingStatus.SIGNING, callback)
const { marketplace, collectionAddress } = collectionRow
const addresses = addressesByNetwork[SupportedChainId.MAINNET]
const spender =
......@@ -60,12 +51,7 @@ export async function approveCollectionRow(
: addresses.TRANSFER_MANAGER_ERC721
!!collectionAddress &&
(await approveCollection(spender, collectionAddress, signer, (newStatus: ListingStatus) =>
updateStatus({
listing: collectionRow,
newStatus,
rows: collectionsRequiringApproval,
setRows: setCollectionsRequiringApproval as Dispatch<AssetRow[]>,
})
setCollectionStatusAndCallback(collectionRow, newStatus, callback)
))
if (
(collectionRow.status === ListingStatus.REJECTED || collectionRow.status === ListingStatus.FAILED) &&
......@@ -76,52 +62,34 @@ export async function approveCollectionRow(
export async function signListingRow(
listing: ListingRow,
listings: ListingRow[],
setListings: Dispatch<ListingRow[]>,
signer: JsonRpcSigner,
provider: Web3Provider,
getLooksRareNonce: () => number,
setLooksRareNonce: (nonce: number) => void,
setListingStatusAndCallback: (listing: ListingRow, status: ListingStatus, callback?: () => Promise<void>) => void,
pauseAllRows?: () => void
) {
const looksRareNonce = getLooksRareNonce()
updateStatus({
listing,
newStatus: ListingStatus.SIGNING,
rows: listings,
setRows: setListings as Dispatch<AssetRow[]>,
callback: () => {
const callback = () => {
return signListingRow(
listing,
listings,
setListings,
signer,
provider,
getLooksRareNonce,
setLooksRareNonce,
setListingStatusAndCallback,
pauseAllRows
)
},
})
}
setListingStatusAndCallback(listing, ListingStatus.SIGNING, callback)
const { asset, marketplace } = listing
const res = await signListing(marketplace, asset, signer, provider, looksRareNonce, (newStatus: ListingStatus) =>
updateStatus({
listing,
newStatus,
rows: listings,
setRows: setListings as Dispatch<AssetRow[]>,
})
setListingStatusAndCallback(listing, newStatus, callback)
)
if (listing.status === ListingStatus.REJECTED && pauseAllRows) pauseAllRows()
else {
if (listing.status === ListingStatus.REJECTED && pauseAllRows) {
pauseAllRows()
} else {
res && listing.marketplace.name === 'LooksRare' && setLooksRareNonce(looksRareNonce + 1)
const newStatus = res ? ListingStatus.APPROVED : ListingStatus.FAILED
updateStatus({
listing,
newStatus,
rows: listings,
setRows: setListings as Dispatch<AssetRow[]>,
})
}
}
......
......@@ -188,7 +188,7 @@ export const ListPage = () => {
listingStatus,
setListingStatus,
setLooksRareNonce,
setCollectionsRequiringApproval,
setCollectionStatusAndCallback,
} = useNFTList(
({
listings,
......@@ -196,14 +196,14 @@ export const ListPage = () => {
listingStatus,
setListingStatus,
setLooksRareNonce,
setCollectionsRequiringApproval,
setCollectionStatusAndCallback,
}) => ({
listings,
collectionsRequiringApproval,
listingStatus,
setListingStatus,
setLooksRareNonce,
setCollectionsRequiringApproval,
setCollectionStatusAndCallback,
}),
shallow
)
......@@ -261,13 +261,8 @@ export const ListPage = () => {
for (const collectionRow of collectionsRequiringApproval) {
verifyStatus(collectionRow.status) &&
(isMobile
? await approveCollectionRow(
collectionRow,
collectionsRequiringApproval,
setCollectionsRequiringApproval,
signer
)
: approveCollectionRow(collectionRow, collectionsRequiringApproval, setCollectionsRequiringApproval, signer))
? await approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback)
: approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback))
}
}
......
......@@ -13,6 +13,7 @@ import { X } from 'react-feather'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import shallow from 'zustand/shallow'
import { TitleRow } from '../shared'
import { ListModalSection, Section } from './ListModalSection'
......@@ -45,12 +46,31 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
const signer = provider?.getSigner()
const trace = useTrace({ modal: InterfaceModalName.NFT_LISTING })
const sellAssets = useSellAsset((state) => state.sellAssets)
const listings = useNFTList((state) => state.listings)
const collectionsRequiringApproval = useNFTList((state) => state.collectionsRequiringApproval)
const listingStatus = useNFTList((state) => state.listingStatus)
const setListings = useNFTList((state) => state.setListings)
const setLooksRareNonce = useNFTList((state) => state.setLooksRareNonce)
const getLooksRareNonce = useNFTList((state) => state.getLooksRareNonce)
const {
listingStatus,
setListingStatusAndCallback,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
} = useNFTList(
({
listingStatus,
setListingStatusAndCallback,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}) => ({
listingStatus,
setListingStatusAndCallback,
setLooksRareNonce,
getLooksRareNonce,
collectionsRequiringApproval,
listings,
}),
shallow
)
const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets])
const [openSection, toggleOpenSection] = useReducer(
......@@ -74,7 +94,7 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
if (!signer || !provider) return
// sign listings
for (const listing of listings) {
await signListingRow(listing, listings, setListings, signer, provider, getLooksRareNonce, setLooksRareNonce)
await signListingRow(listing, signer, provider, getLooksRareNonce, setLooksRareNonce, setListingStatusAndCallback)
}
sendAnalyticsEvent(NFTEventName.NFT_LISTING_COMPLETED, {
signatures_approved: listings.filter((asset) => asset.status === ListingStatus.APPROVED),
......
......@@ -135,7 +135,7 @@ export const ListModalSection = ({ sectionType, active, content, toggleSection }
{content.map((row: AssetRow) => (
<ContentRow
row={row}
key={row.name}
key={(row?.name ?? '') + row?.images[1]}
removeRow={removeRow}
isCollectionApprovalSection={isCollectionApprovalSection}
/>
......
......@@ -12,6 +12,12 @@ interface NFTListState {
setListingStatus: (status: ListingStatus) => void
setListings: (listings: ListingRow[]) => void
setCollectionsRequiringApproval: (collections: CollectionRow[]) => void
setListingStatusAndCallback: (listing: ListingRow, status: ListingStatus, callback?: () => Promise<void>) => void
setCollectionStatusAndCallback: (
collection: CollectionRow,
status: ListingStatus,
callback?: () => Promise<void>
) => void
}
export const useNFTList = create<NFTListState>()(
......@@ -34,13 +40,15 @@ export const useNFTList = create<NFTListState>()(
setListings: (listings) =>
set(() => {
const updatedListings = listings.map((listing) => {
const oldStatus = get().listings.find(
const oldListing = get().listings.find(
(oldListing) =>
oldListing.asset.asset_contract.address === listing.asset.asset_contract.address &&
oldListing.asset.tokenId === listing.asset.tokenId &&
oldListing.marketplace.name === listing.marketplace.name &&
oldListing.price === listing.price
)?.status
)
const oldStatus = oldListing?.status
const oldCallback = oldListing?.callback
const status = () => {
switch (oldStatus) {
case ListingStatus.APPROVED:
......@@ -56,6 +64,7 @@ export const useNFTList = create<NFTListState>()(
return {
...listing,
status: status(),
callback: oldCallback ?? listing.callback,
}
})
return {
......@@ -64,7 +73,75 @@ export const useNFTList = create<NFTListState>()(
}),
setCollectionsRequiringApproval: (collections) =>
set(() => {
return { collectionsRequiringApproval: collections }
const updatedCollections = collections.map((collection) => {
const oldCollection = get().collectionsRequiringApproval.find(
(oldCollection) =>
oldCollection.collectionAddress === collection.collectionAddress &&
oldCollection.marketplace.name === collection.marketplace.name
)
const oldStatus = oldCollection?.status
const oldCallback = oldCollection?.callback
const status = () => {
switch (oldStatus) {
case ListingStatus.APPROVED:
return ListingStatus.APPROVED
case ListingStatus.FAILED:
return collection.status === ListingStatus.SIGNING ? ListingStatus.SIGNING : ListingStatus.FAILED
case ListingStatus.REJECTED:
return collection.status === ListingStatus.SIGNING ? ListingStatus.SIGNING : ListingStatus.REJECTED
default:
return collection.status
}
}
return {
...collection,
status: status(),
callback: oldCallback ?? collection.callback,
}
})
return {
collectionsRequiringApproval: updatedCollections,
}
}),
setListingStatusAndCallback: (listing, status, callback) =>
set(({ listings }) => {
const listingsCopy = [...listings]
const oldListingIndex = listingsCopy.findIndex(
(oldListing) =>
oldListing.name === listing.name &&
oldListing.price === listing.price &&
oldListing.marketplace.name === listing.marketplace.name
)
if (oldListingIndex > -1) {
const updatedListing = {
...listings[oldListingIndex],
status,
callback: callback ?? listings[oldListingIndex].callback,
}
listingsCopy.splice(oldListingIndex, 1, updatedListing)
}
return {
listings: listingsCopy,
}
}),
setCollectionStatusAndCallback: (collection, status, callback) =>
set(({ collectionsRequiringApproval }) => {
const collectionsCopy = [...collectionsRequiringApproval]
const oldCollectionIndex = collectionsCopy.findIndex(
(oldCollection) =>
oldCollection.name === collection.name && oldCollection.marketplace.name === collection.marketplace.name
)
if (oldCollectionIndex > -1) {
const updatedCollection = {
...collectionsCopy[oldCollectionIndex],
status,
callback: callback ?? collectionsCopy[oldCollectionIndex].callback,
}
collectionsCopy.splice(oldCollectionIndex, 1, updatedCollection)
}
return {
collectionsRequiringApproval: collectionsCopy,
}
}),
}))
)
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