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