From bb3b236cd96cc18bedacb43ecc1f26ecbeabf69c Mon Sep 17 00:00:00 2001 From: Charles Bachmeier Date: Fri, 18 Nov 2022 14:19:05 -0800 Subject: [PATCH] refactor: move listinggrid to its own file (#5326) * move listinggrid to its own file * add file * organize imports * undo unrelated testing --- src/nft/components/profile/list/ListPage.tsx | 565 +----------------- .../profile/list/NFTListingsGrid.tsx | 551 +++++++++++++++++ 2 files changed, 555 insertions(+), 561 deletions(-) create mode 100644 src/nft/components/profile/list/NFTListingsGrid.tsx diff --git a/src/nft/components/profile/list/ListPage.tsx b/src/nft/components/profile/list/ListPage.tsx index e4c82e460c..448c870316 100644 --- a/src/nft/components/profile/list/ListPage.tsx +++ b/src/nft/components/profile/list/ListPage.tsx @@ -1,576 +1,19 @@ import { ListingButton } from 'nft/components/bag/profile/ListingButton' import { getListingState } from 'nft/components/bag/profile/utils' import { Box } from 'nft/components/Box' -import { SortDropdown } from 'nft/components/common/SortDropdown' import { Column, Row } from 'nft/components/Flex' -import { - AttachPriceIcon, - BackArrowIcon, - EditPriceIcon, - RowsCollpsedIcon, - RowsExpandedIcon, - VerifiedIcon, -} from 'nft/components/icons' -import { NumericInput } from 'nft/components/layout/Input' -import { badge, body, bodySmall, headlineSmall, subheadSmall } from 'nft/css/common.css' +import { BackArrowIcon } from 'nft/components/icons' import { themeVars } from 'nft/css/sprinkles.css' import { useBag, useNFTList, useProfilePageState, useSellAsset } from 'nft/hooks' -import { - DropDownOption, - ListingMarket, - ListingStatus, - ListingWarning, - ProfilePageStateType, - WalletAsset, -} from 'nft/types' -import { formatEth, formatUsdPrice } from 'nft/utils/currency' -import { fetchPrice } from 'nft/utils/fetchPrice' +import { ListingStatus, ProfilePageStateType } from 'nft/types' import { ListingMarkets } from 'nft/utils/listNfts' -import { Dispatch, FormEvent, useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useState } from 'react' import styled from 'styled-components/macro' -import * as styles from './ListPage.css' +import { NFTListingsGrid } from './NFTListingsGrid' import { SelectMarketplacesDropdown } from './SelectMarketplacesDropdown' import { SetDurationModal } from './SetDurationModal' -enum SetPriceMethod { - SAME_PRICE, - FLOOR_PRICE, - PREV_LISTING, -} - -const NFTListingsGrid = ({ selectedMarkets }: { selectedMarkets: ListingMarket[] }) => { - const sellAssets = useSellAsset((state) => state.sellAssets) - const [globalPriceMethod, setGlobalPriceMethod] = useState() - const [globalPrice, setGlobalPrice] = useState() - - const priceDropdownOptions: DropDownOption[] = useMemo( - () => [ - { - displayText: 'Same price', - onClick: () => setGlobalPriceMethod(SetPriceMethod.SAME_PRICE), - }, - { - displayText: 'Floor price', - onClick: () => setGlobalPriceMethod(SetPriceMethod.FLOOR_PRICE), - }, - { - displayText: 'Prev. listing', - onClick: () => setGlobalPriceMethod(SetPriceMethod.PREV_LISTING), - }, - ], - [] - ) - - return ( - - Create your listings - - 1 ? '36' : '0'} - transition="500" - className={badge} - color="textSecondary" - flex="2" - > - YOUR NFTS - - - - - - - MARKETPLACE FEE - - - ROYALTIES - - - YOU RECEIVE - - - - {sellAssets.map((asset) => { - return ( - <> - - {sellAssets.indexOf(asset) < sellAssets.length - 1 &&
} - - ) - })} -
- ) -} - -enum WarningType { - BELOW_FLOOR = 'LISTING BELOW FLOOR ', - ALREADY_LISTED = 'ALREADY LISTED FOR ', - NONE = '', -} - -interface PriceTextInputProps { - listPrice?: number - setListPrice: Dispatch - isGlobalPrice: boolean - setGlobalOverride: Dispatch - globalOverride: boolean - warning?: ListingWarning - asset: WalletAsset -} - -const PriceTextInput = ({ - listPrice, - setListPrice, - isGlobalPrice, - setGlobalOverride, - globalOverride, - warning, - asset, -}: PriceTextInputProps) => { - const [focused, setFocused] = useState(false) - const [warningType, setWarningType] = useState(WarningType.NONE) - const removeMarketplaceWarning = useSellAsset((state) => state.removeMarketplaceWarning) - const removeSellAsset = useSellAsset((state) => state.removeSellAsset) - const inputRef = useRef() as React.MutableRefObject - - useEffect(() => { - inputRef.current.value = listPrice !== undefined ? `${listPrice}` : '' - setWarningType(WarningType.NONE) - if (!warning && listPrice) { - if (listPrice < asset.floorPrice) setWarningType(WarningType.BELOW_FLOOR) - else if (asset.floor_sell_order_price && listPrice >= asset.floor_sell_order_price) - setWarningType(WarningType.ALREADY_LISTED) - } else if (warning && listPrice && listPrice >= 0) removeMarketplaceWarning(asset, warning) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [listPrice]) - - return ( - - - setFocused(true)} - onBlur={() => { - setFocused(false) - }} - ref={inputRef} - onChange={(v: FormEvent) => { - const val = parseFloat(v.currentTarget.value) - setListPrice(isNaN(val) ? undefined : val) - }} - /> - = 0 ? 'textPrimary' : 'textSecondary'} marginRight="16"> -  ETH - - setGlobalOverride(!globalOverride)} - > - {globalOverride ? : } - - - - {focused ? ( - <> - - LAST: {formatEth(asset.lastPrice)} ETH - - FLOOR: {formatEth(asset.floorPrice)} ETH - - ) : ( - <> - {warning - ? warning.message - : warningType !== WarningType.NONE && ( - <> - {warningType} - {warningType === WarningType.BELOW_FLOOR - ? formatEth(asset.floorPrice) - : formatEth(asset.floor_sell_order_price)} - ETH - { - warningType === WarningType.ALREADY_LISTED && removeSellAsset(asset) - setWarningType(WarningType.NONE) - }} - > - {warningType === WarningType.BELOW_FLOOR ? 'DISMISS' : 'REMOVE ITEM'} - - - )} - - )} - - - ) -} - -const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => { - const [ethConversion, setEthConversion] = useState(3000) - useEffect(() => { - fetchPrice().then((price) => { - setEthConversion(price ?? 0) - }) - }, []) - - return ( - - - {ethPrice !== 0 ? ( - - {formatUsdPrice(ethPrice * ethConversion)} - - ) : ( - '- Eth' - )} - - - ) -} - -function maxMarketFee(markets: ListingMarket[]): number { - let max = -1 - markets.forEach((market) => { - if (market.fee > max) { - max = market.fee - } - }) - return max -} - -interface MarketplaceRowProps { - globalPriceMethod?: SetPriceMethod - globalPrice?: number - setGlobalPrice: Dispatch - selectedMarkets: ListingMarket[] - removeMarket?: () => void - asset: WalletAsset - showMarketplaceLogo: boolean -} - -const MarketplaceRow = ({ - globalPriceMethod, - globalPrice, - setGlobalPrice, - selectedMarkets, - removeMarket = undefined, - asset, - showMarketplaceLogo, -}: MarketplaceRowProps) => { - const [listPrice, setListPrice] = useState() - const [globalOverride, setGlobalOverride] = useState(false) - const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice - const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice) - const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace) - const [hovered, setHovered] = useState(false) - const handleHover = () => setHovered(!hovered) - - const marketplaceFee = selectedMarkets.length > 0 ? maxMarketFee(selectedMarkets) : 0 - const price = showGlobalPrice ? globalPrice : listPrice - const feeInEth = price && (price * (asset.basisPoints * 0.01 + marketplaceFee)) / 100 - const userReceives = price && feeInEth && price - feeInEth - const profit = userReceives && asset.lastPrice && userReceives - asset.lastPrice - const profitPercent = profit && asset.lastPrice && Math.round(profit && (profit / asset.lastPrice) * 100) - - useEffect(() => { - if (globalPriceMethod === SetPriceMethod.FLOOR_PRICE) { - setListPrice(asset.floorPrice) - setGlobalPrice(asset.floorPrice) - } else if (globalPriceMethod === SetPriceMethod.PREV_LISTING) { - setListPrice(asset.lastPrice) - setGlobalPrice(asset.lastPrice) - } else if (globalPriceMethod === SetPriceMethod.SAME_PRICE) - listPrice && !globalPrice ? setGlobalPrice(listPrice) : setListPrice(globalPrice) - - setGlobalOverride(false) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [globalPriceMethod]) - - useEffect(() => { - if (selectedMarkets.length) - for (const marketplace of selectedMarkets) setAssetListPrice(asset, listPrice, marketplace) - else setAssetListPrice(asset, listPrice) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [listPrice]) - - useEffect(() => { - let price: number | undefined = undefined - if (globalOverride) { - if (!listPrice) setListPrice(globalPrice) - price = listPrice ? listPrice : globalPrice - } else { - price = globalPrice - } - if (selectedMarkets.length) for (const marketplace of selectedMarkets) setAssetListPrice(asset, price, marketplace) - else setAssetListPrice(asset, price) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [globalOverride]) - - useEffect(() => { - if (globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride) { - if (selectedMarkets.length) - for (const marketplace of selectedMarkets) setAssetListPrice(asset, globalPrice, marketplace) - else setAssetListPrice(asset, globalPrice) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [globalPrice]) - - let warning: ListingWarning | undefined = undefined - if (asset.listingWarnings && asset.listingWarnings?.length > 0) { - if (showMarketplaceLogo) { - for (const listingWarning of asset.listingWarnings) { - if (listingWarning.marketplace.name === selectedMarkets[0].name) warning = listingWarning - } - } else { - warning = asset.listingWarnings[0] - } - } - - return ( - 1 ? '20' : '0'}> - {showMarketplaceLogo && ( - { - e.stopPropagation() - removeAssetMarketplace(asset, selectedMarkets[0]) - removeMarket && removeMarket() - }} - > - - - - - - )} - - {globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride ? ( - - ) : ( - - )} - - - - {marketplaceFee > 0 ? marketplaceFee + (selectedMarkets.length > 1 ? '% MAX' : '%') : '--%'} - - - - - {(asset.basisPoints * 0.01).toFixed(1)}% - - - - - - {(showGlobalPrice ? globalPrice && globalPrice !== 0 : listPrice !== 0) && ( - - {profit ? Profit: {formatEth(profit)} ETH : null} - {profitPercent ? ( - - ({profitPercent > 0 && '+'} - {profitPercent > 1000 ? Math.round(profitPercent / 1000) + 'K' : profitPercent}%) - - ) : null} - - )} - - - - ) -} - -export interface NFTListRowProps { - asset: WalletAsset - globalPriceMethod?: SetPriceMethod - setGlobalPrice: Dispatch - globalPrice?: number - selectedMarkets: ListingMarket[] -} - -const NFTListRow = ({ asset, globalPriceMethod, globalPrice, setGlobalPrice, selectedMarkets }: NFTListRowProps) => { - const [expandMarketplaceRows, setExpandMarketplaceRows] = useState(false) - const removeAsset = useSellAsset((state) => state.removeSellAsset) - const [localMarkets, setLocalMarkets] = useState([]) - const [hovered, setHovered] = useState(false) - const handleHover = () => setHovered(!hovered) - - useEffect(() => { - setLocalMarkets(JSON.parse(JSON.stringify(selectedMarkets))) - selectedMarkets.length < 2 && setExpandMarketplaceRows(false) - }, [selectedMarkets]) - - return ( - - - 1 ? '28px' : '0', - opacity: localMarkets.length > 1 ? '1' : '0', - }} - cursor="pointer" - onClick={() => setExpandMarketplaceRows(!expandMarketplaceRows)} - > - {expandMarketplaceRows ? : } - - { - removeAsset(asset) - }} - > - - - - 1 ? '8' : '0'} - marginRight="8" - transition="500" - src={asset.imageUrl || '/nft/svgs/image-placeholder.svg'} - /> - - - - {asset.name ? asset.name : `#${asset.tokenId}`} - - - {asset.collection?.name} - {asset.collectionIsVerified && } - - - - - {expandMarketplaceRows ? ( - localMarkets.map((market, index) => { - return ( - localMarkets.splice(index, 1)} - asset={asset} - showMarketplaceLogo={true} - key={index} - /> - ) - }) - ) : ( - - )} - - - ) -} - const MarketWrap = styled.section` gap: 48px; padding-left: 18px; diff --git a/src/nft/components/profile/list/NFTListingsGrid.tsx b/src/nft/components/profile/list/NFTListingsGrid.tsx new file mode 100644 index 0000000000..2684b5a2f3 --- /dev/null +++ b/src/nft/components/profile/list/NFTListingsGrid.tsx @@ -0,0 +1,551 @@ +import { Box } from 'nft/components/Box' +import { SortDropdown } from 'nft/components/common/SortDropdown' +import { Column, Row } from 'nft/components/Flex' +import { AttachPriceIcon, EditPriceIcon, RowsCollpsedIcon, RowsExpandedIcon, VerifiedIcon } from 'nft/components/icons' +import { NumericInput } from 'nft/components/layout/Input' +import { badge, body, bodySmall, headlineSmall, subheadSmall } from 'nft/css/common.css' +import { useSellAsset } from 'nft/hooks' +import { DropDownOption, ListingMarket, ListingWarning, WalletAsset } from 'nft/types' +import { formatEth, formatUsdPrice } from 'nft/utils/currency' +import { fetchPrice } from 'nft/utils/fetchPrice' +import { Dispatch, FormEvent, useEffect, useMemo, useRef, useState } from 'react' + +import * as styles from './ListPage.css' + +enum SetPriceMethod { + SAME_PRICE, + FLOOR_PRICE, + PREV_LISTING, +} + +export const NFTListingsGrid = ({ selectedMarkets }: { selectedMarkets: ListingMarket[] }) => { + const sellAssets = useSellAsset((state) => state.sellAssets) + const [globalPriceMethod, setGlobalPriceMethod] = useState() + const [globalPrice, setGlobalPrice] = useState() + + const priceDropdownOptions: DropDownOption[] = useMemo( + () => [ + { + displayText: 'Same price', + onClick: () => setGlobalPriceMethod(SetPriceMethod.SAME_PRICE), + }, + { + displayText: 'Floor price', + onClick: () => setGlobalPriceMethod(SetPriceMethod.FLOOR_PRICE), + }, + { + displayText: 'Prev. listing', + onClick: () => setGlobalPriceMethod(SetPriceMethod.PREV_LISTING), + }, + ], + [] + ) + + return ( + + Create your listings + + 1 ? '36' : '0'} + transition="500" + className={badge} + color="textSecondary" + flex="2" + > + YOUR NFTS + + + + + + + MARKETPLACE FEE + + + ROYALTIES + + + YOU RECEIVE + + + + {sellAssets.map((asset) => { + return ( + <> + + {sellAssets.indexOf(asset) < sellAssets.length - 1 &&
} + + ) + })} +
+ ) +} + +enum WarningType { + BELOW_FLOOR = 'LISTING BELOW FLOOR ', + ALREADY_LISTED = 'ALREADY LISTED FOR ', + NONE = '', +} + +interface PriceTextInputProps { + listPrice?: number + setListPrice: Dispatch + isGlobalPrice: boolean + setGlobalOverride: Dispatch + globalOverride: boolean + warning?: ListingWarning + asset: WalletAsset +} + +const PriceTextInput = ({ + listPrice, + setListPrice, + isGlobalPrice, + setGlobalOverride, + globalOverride, + warning, + asset, +}: PriceTextInputProps) => { + const [focused, setFocused] = useState(false) + const [warningType, setWarningType] = useState(WarningType.NONE) + const removeMarketplaceWarning = useSellAsset((state) => state.removeMarketplaceWarning) + const removeSellAsset = useSellAsset((state) => state.removeSellAsset) + const inputRef = useRef() as React.MutableRefObject + + useEffect(() => { + inputRef.current.value = listPrice !== undefined ? `${listPrice}` : '' + setWarningType(WarningType.NONE) + if (!warning && listPrice) { + if (listPrice < asset.floorPrice) setWarningType(WarningType.BELOW_FLOOR) + else if (asset.floor_sell_order_price && listPrice >= asset.floor_sell_order_price) + setWarningType(WarningType.ALREADY_LISTED) + } else if (warning && listPrice && listPrice >= 0) removeMarketplaceWarning(asset, warning) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [listPrice]) + + return ( + + + setFocused(true)} + onBlur={() => { + setFocused(false) + }} + ref={inputRef} + onChange={(v: FormEvent) => { + const val = parseFloat(v.currentTarget.value) + setListPrice(isNaN(val) ? undefined : val) + }} + /> + = 0 ? 'textPrimary' : 'textSecondary'} marginRight="16"> +  ETH + + setGlobalOverride(!globalOverride)} + > + {globalOverride ? : } + + + + {focused ? ( + <> + + LAST: {formatEth(asset.lastPrice)} ETH + + FLOOR: {formatEth(asset.floorPrice)} ETH + + ) : ( + <> + {warning + ? warning.message + : warningType !== WarningType.NONE && ( + <> + {warningType} + {warningType === WarningType.BELOW_FLOOR + ? formatEth(asset.floorPrice) + : formatEth(asset.floor_sell_order_price)} + ETH + { + warningType === WarningType.ALREADY_LISTED && removeSellAsset(asset) + setWarningType(WarningType.NONE) + }} + > + {warningType === WarningType.BELOW_FLOOR ? 'DISMISS' : 'REMOVE ITEM'} + + + )} + + )} + + + ) +} + +const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => { + const [ethConversion, setEthConversion] = useState(3000) + useEffect(() => { + fetchPrice().then((price) => { + setEthConversion(price ?? 0) + }) + }, []) + + return ( + + + {ethPrice !== 0 ? ( + + {formatUsdPrice(ethPrice * ethConversion)} + + ) : ( + '- Eth' + )} + + + ) +} + +function maxMarketFee(markets: ListingMarket[]): number { + let max = -1 + markets.forEach((market) => { + if (market.fee > max) { + max = market.fee + } + }) + return max +} + +interface MarketplaceRowProps { + globalPriceMethod?: SetPriceMethod + globalPrice?: number + setGlobalPrice: Dispatch + selectedMarkets: ListingMarket[] + removeMarket?: () => void + asset: WalletAsset + showMarketplaceLogo: boolean +} + +const MarketplaceRow = ({ + globalPriceMethod, + globalPrice, + setGlobalPrice, + selectedMarkets, + removeMarket = undefined, + asset, + showMarketplaceLogo, +}: MarketplaceRowProps) => { + const [listPrice, setListPrice] = useState() + const [globalOverride, setGlobalOverride] = useState(false) + const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice + const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice) + const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace) + const [hovered, setHovered] = useState(false) + const handleHover = () => setHovered(!hovered) + + const marketplaceFee = selectedMarkets.length > 0 ? maxMarketFee(selectedMarkets) : 0 + const price = showGlobalPrice ? globalPrice : listPrice + const feeInEth = price && (price * (asset.basisPoints * 0.01 + marketplaceFee)) / 100 + const userReceives = price && feeInEth && price - feeInEth + const profit = userReceives && asset.lastPrice && userReceives - asset.lastPrice + const profitPercent = profit && asset.lastPrice && Math.round(profit && (profit / asset.lastPrice) * 100) + + useEffect(() => { + if (globalPriceMethod === SetPriceMethod.FLOOR_PRICE) { + setListPrice(asset.floorPrice) + setGlobalPrice(asset.floorPrice) + } else if (globalPriceMethod === SetPriceMethod.PREV_LISTING) { + setListPrice(asset.lastPrice) + setGlobalPrice(asset.lastPrice) + } else if (globalPriceMethod === SetPriceMethod.SAME_PRICE) + listPrice && !globalPrice ? setGlobalPrice(listPrice) : setListPrice(globalPrice) + + setGlobalOverride(false) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [globalPriceMethod]) + + useEffect(() => { + if (selectedMarkets.length) + for (const marketplace of selectedMarkets) setAssetListPrice(asset, listPrice, marketplace) + else setAssetListPrice(asset, listPrice) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [listPrice]) + + useEffect(() => { + let price: number | undefined = undefined + if (globalOverride) { + if (!listPrice) setListPrice(globalPrice) + price = listPrice ? listPrice : globalPrice + } else { + price = globalPrice + } + if (selectedMarkets.length) for (const marketplace of selectedMarkets) setAssetListPrice(asset, price, marketplace) + else setAssetListPrice(asset, price) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [globalOverride]) + + useEffect(() => { + if (globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride) { + if (selectedMarkets.length) + for (const marketplace of selectedMarkets) setAssetListPrice(asset, globalPrice, marketplace) + else setAssetListPrice(asset, globalPrice) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [globalPrice]) + + let warning: ListingWarning | undefined = undefined + if (asset.listingWarnings && asset.listingWarnings?.length > 0) { + if (showMarketplaceLogo) { + for (const listingWarning of asset.listingWarnings) { + if (listingWarning.marketplace.name === selectedMarkets[0].name) warning = listingWarning + } + } else { + warning = asset.listingWarnings[0] + } + } + + return ( + 1 ? '20' : '0'}> + {showMarketplaceLogo && ( + { + e.stopPropagation() + removeAssetMarketplace(asset, selectedMarkets[0]) + removeMarket && removeMarket() + }} + > + + + + + + )} + + {globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride ? ( + + ) : ( + + )} + + + + {marketplaceFee > 0 ? marketplaceFee + (selectedMarkets.length > 1 ? '% MAX' : '%') : '--%'} + + + + + {(asset.basisPoints * 0.01).toFixed(1)}% + + + + + + {(showGlobalPrice ? globalPrice && globalPrice !== 0 : listPrice !== 0) && ( + + {profit ? Profit: {formatEth(profit)} ETH : null} + {profitPercent ? ( + + ({profitPercent > 0 && '+'} + {profitPercent > 1000 ? Math.round(profitPercent / 1000) + 'K' : profitPercent}%) + + ) : null} + + )} + + + + ) +} + +export interface NFTListRowProps { + asset: WalletAsset + globalPriceMethod?: SetPriceMethod + setGlobalPrice: Dispatch + globalPrice?: number + selectedMarkets: ListingMarket[] +} + +const NFTListRow = ({ asset, globalPriceMethod, globalPrice, setGlobalPrice, selectedMarkets }: NFTListRowProps) => { + const [expandMarketplaceRows, setExpandMarketplaceRows] = useState(false) + const removeAsset = useSellAsset((state) => state.removeSellAsset) + const [localMarkets, setLocalMarkets] = useState([]) + const [hovered, setHovered] = useState(false) + const handleHover = () => setHovered(!hovered) + + useEffect(() => { + setLocalMarkets(JSON.parse(JSON.stringify(selectedMarkets))) + selectedMarkets.length < 2 && setExpandMarketplaceRows(false) + }, [selectedMarkets]) + + return ( + + + 1 ? '28px' : '0', + opacity: localMarkets.length > 1 ? '1' : '0', + }} + cursor="pointer" + onClick={() => setExpandMarketplaceRows(!expandMarketplaceRows)} + > + {expandMarketplaceRows ? : } + + { + removeAsset(asset) + }} + > + + + + 1 ? '8' : '0'} + marginRight="8" + transition="500" + src={asset.imageUrl || '/nft/svgs/image-placeholder.svg'} + /> + + + + {asset.name ? asset.name : `#${asset.tokenId}`} + + + {asset.collection?.name} + {asset.collectionIsVerified && } + + + + + {expandMarketplaceRows ? ( + localMarkets.map((market, index) => { + return ( + localMarkets.splice(index, 1)} + asset={asset} + showMarketplaceLogo={true} + key={index} + /> + ) + }) + ) : ( + + )} + + + ) +}