From db4987f5577bf0a29e2c67c3c1eadf4266e086ec Mon Sep 17 00:00:00 2001 From: Charles Bachmeier Date: Thu, 6 Oct 2022 10:44:14 -0700 Subject: [PATCH] feat: Sell Mode Button (#4803) * remove sort and search functionality * add sell mode button toggle * update cards to sell mode * split wallet asset display file * remove unused style * address comments Co-authored-by: Charles Bachmeier --- .../components/profile/view/FilterSidebar.tsx | 7 +- .../components/profile/view/ProfilePage.tsx | 368 +++--------------- .../profile/view/WalletAssetDisplay.tsx | 129 ++++++ src/nft/utils/asset.ts | 10 +- 4 files changed, 201 insertions(+), 313 deletions(-) create mode 100644 src/nft/components/profile/view/WalletAssetDisplay.tsx diff --git a/src/nft/components/profile/view/FilterSidebar.tsx b/src/nft/components/profile/view/FilterSidebar.tsx index 36808fcf8e..ce943958b9 100644 --- a/src/nft/components/profile/view/FilterSidebar.tsx +++ b/src/nft/components/profile/view/FilterSidebar.tsx @@ -11,7 +11,7 @@ import { useSpring } from 'react-spring' import * as styles from './ProfilePage.css' -export const FilterSidebar = ({ SortDropdown }: { SortDropdown: () => JSX.Element }) => { +export const FilterSidebar = () => { const collectionFilters = useWalletCollections((state) => state.collectionFilters) const setCollectionFilters = useWalletCollections((state) => state.setCollectionFilters) @@ -62,11 +62,6 @@ export const FilterSidebar = ({ SortDropdown }: { SortDropdown: () => JSX.Elemen - {isMobile && ( - - - - )} { - if (price > 1000000) { - return `${Math.round(price / 1000000)}M` - } else if (price > 1000) { - return `${Math.round(price / 1000)}K` - } else { - return `${Math.round(price * 100 + Number.EPSILON) / 100}` +const SellModeButton = styled.button<{ active: boolean }>` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 10px 12px; + border-radius: 12px; + gap: 8px; + cursor: pointer; + background-color: ${({ theme, active }) => (active ? theme.accentAction : theme.backgroundInteractive)}; + color: #fff; + border: none; + outline: none; + &:hover { + background-color: ${({ theme }) => theme.accentAction}; } -} +` function roundFloorPrice(price?: number, n?: number) { return price ? Math.round(price * Math.pow(10, n ?? 3) + Number.EPSILON) / Math.pow(10, n ?? 3) : 0 @@ -65,6 +55,26 @@ export const ProfilePage = () => { const collectionFilters = useWalletCollections((state) => state.collectionFilters) const setCollectionFilters = useWalletCollections((state) => state.setCollectionFilters) const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters) + const walletAssets = useWalletCollections((state) => state.walletAssets) + const setWalletAssets = useWalletCollections((state) => state.setWalletAssets) + const displayAssets = useWalletCollections((state) => state.displayAssets) + const setDisplayAssets = useWalletCollections((state) => state.setDisplayAssets) + const walletCollections = useWalletCollections((state) => state.walletCollections) + const setWalletCollections = useWalletCollections((state) => state.setWalletCollections) + const listFilter = useWalletCollections((state) => state.listFilter) + const sellAssets = useSellAsset((state) => state.sellAssets) + const reset = useSellAsset((state) => state.reset) + const resetSellAssets = useSellAsset((state) => state.reset) + const setSellPageState = useProfilePageState((state) => state.setProfilePageState) + const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded() + const isBagExpanded = useBag((state) => state.bagExpanded) + const isMobile = useIsMobile() + const [isSellMode, toggleSellMode] = useReducer((s) => !s, false) + + const handleSellModeClick = () => { + resetSellAssets() + toggleSellMode() + } const { data: ownerCollections } = useQuery( ['ownerCollections', address], @@ -108,21 +118,9 @@ export const ProfilePage = () => { const ownerAssets = useMemo(() => (isSuccess ? ownerAssetsData?.pages.flat() : null), [isSuccess, ownerAssetsData]) - const walletAssets = useWalletCollections((state) => state.walletAssets) - const setWalletAssets = useWalletCollections((state) => state.setWalletAssets) - const displayAssets = useWalletCollections((state) => state.displayAssets) - const setDisplayAssets = useWalletCollections((state) => state.setDisplayAssets) - const walletCollections = useWalletCollections((state) => state.walletCollections) - const setWalletCollections = useWalletCollections((state) => state.setWalletCollections) - const listFilter = useWalletCollections((state) => state.listFilter) - const sellAssets = useSellAsset((state) => state.sellAssets) - const reset = useSellAsset((state) => state.reset) - const setSellPageState = useProfilePageState((state) => state.setProfilePageState) - const [sortBy, setSortBy] = useState(SortBy.DateAcquired) - const [orderByASC, setOrderBy] = useState(true) - const [searchText, setSearchText] = useState('') - const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded() - const isMobile = useIsMobile() + useEffect(() => { + setDisplayAssets(walletAssets, listFilter) + }, [walletAssets, listFilter, setDisplayAssets]) useEffect(() => { setWalletAssets(ownerAssets?.flat() ?? []) @@ -132,15 +130,6 @@ export const ProfilePage = () => { ownerCollections && setWalletCollections(ownerCollections) }, [ownerCollections, setWalletCollections]) - useEffect(() => { - if (searchText) { - const filtered = walletAssets.filter((asset) => asset.name?.toLowerCase().includes(searchText.toLowerCase())) - setDisplayAssets(filtered, listFilter) - } else { - setDisplayAssets(walletAssets, listFilter) - } - }, [searchText, walletAssets, listFilter, setDisplayAssets]) - useEffect(() => { if (ownerCollections?.length && collectionStats?.length) { const ownerCollectionsCopy = [...ownerCollections] @@ -152,31 +141,6 @@ export const ProfilePage = () => { } }, [collectionStats, ownerCollections, setWalletCollections]) - useEffect(() => { - const sorted = displayAssets && [...displayAssets] - if (sortBy === SortBy.FloorPrice && orderByASC) sorted?.sort((a, b) => (b.floorPrice || 0) - (a.floorPrice || 0)) - else if (sortBy === SortBy.FloorPrice && !orderByASC) - sorted?.sort((a, b) => (a.floorPrice || 0) - (b.floorPrice || 0)) - else if (sortBy === SortBy.LastPrice && orderByASC) sorted?.sort((a, b) => b.lastPrice - a.lastPrice) - else if (sortBy === SortBy.LastPrice && !orderByASC) sorted?.sort((a, b) => a.lastPrice - b.lastPrice) - else if (sortBy === SortBy.DateCreated && orderByASC) - sorted?.sort( - (a, b) => new Date(a.asset_contract.created_date).getTime() - new Date(b.asset_contract.created_date).getTime() - ) - else if (sortBy === SortBy.DateCreated && !orderByASC) - sorted?.sort( - (a, b) => new Date(b.asset_contract.created_date).getTime() - new Date(a.asset_contract.created_date).getTime() - ) - else if (sortBy === SortBy.DateAcquired && orderByASC) - sorted?.sort((a, b) => new Date(a.date_acquired).getTime() - new Date(b.date_acquired).getTime()) - else if (sortBy === SortBy.DateAcquired && !orderByASC) - sorted?.sort((a, b) => new Date(b.date_acquired).getTime() - new Date(a.date_acquired).getTime()) - else if (sortBy === SortBy.DateListed && orderByASC) sorted?.sort((a, b) => +b.listing_date - +a.listing_date) - else if (sortBy === SortBy.DateListed && !orderByASC) sorted?.sort((a, b) => +a.listing_date - +b.listing_date) - setDisplayAssets(sorted, listFilter) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sortBy, orderByASC, listFilter]) - useEffect(() => { if (ownerCollections?.length && collectionStats?.length) { const ownerCollectionsCopy = [...ownerCollections] @@ -188,76 +152,22 @@ export const ProfilePage = () => { } }, [collectionStats, ownerCollections, setWalletCollections]) - const { gridX, gridWidthOffset } = useSpring({ + const { gridX } = useSpring({ gridX: isFiltersExpanded ? 300 : -16, - gridWidthOffset: isFiltersExpanded ? 300 /* right padding */ : 0, }) - const sortDropDownOptions: DropDownOption[] = useMemo( - () => [ - { - displayText: 'Floor price', - onClick: () => { - setOrderBy(false) - setSortBy(SortBy.FloorPrice) - }, - icon: , - reverseOnClick: () => setOrderBy(!orderByASC), - }, - { - displayText: 'Last price', - onClick: () => { - setOrderBy(false) - setSortBy(SortBy.LastPrice) - }, - icon: , - reverseOnClick: () => setOrderBy(!orderByASC), - }, - { - displayText: 'Date acquired', - onClick: () => { - setOrderBy(false) - setSortBy(SortBy.DateAcquired) - }, - icon: , - reverseOnClick: () => setOrderBy(!orderByASC), - }, - { - displayText: 'Date created', - onClick: () => { - setOrderBy(false) - setSortBy(SortBy.DateCreated) - }, - icon: , - reverseOnClick: () => setOrderBy(!orderByASC), - }, - { - displayText: 'Date listed', - onClick: () => { - setOrderBy(false) - setSortBy(SortBy.DateListed) - }, - icon: , - reverseOnClick: () => setOrderBy(!orderByASC), - }, - ], - [orderByASC] - ) - - const SortWalletAssetsDropdown = () => - return ( {walletAssets.length === 0 ? ( ) : ( - + {(!isMobile || !isFiltersExpanded) && ( @@ -267,19 +177,22 @@ export const ProfilePage = () => { flexShrink="0" style={{ transform: gridX.to((x) => `translate(${Number(x) - (!isMobile && isFiltersExpanded ? 300 : 0)}px)`), - width: gridWidthOffset.to((x) => `calc(100% - ${x}px)`), }} > - + setFiltersExpanded(!isFiltersExpanded)} /> - {!isMobile && } - - + + {isSellMode && } + + + Sell + + { >
{displayAssets && displayAssets.length - ? displayAssets.map((asset, index) => ) + ? displayAssets.map((asset, index) => ( + + )) : null}
@@ -358,125 +273,6 @@ export const ProfilePage = () => { ) } -export const WalletAssetDisplay = ({ asset }: { asset: WalletAsset }) => { - const sellAssets = useSellAsset((state) => state.sellAssets) - const selectSellAsset = useSellAsset((state) => state.selectSellAsset) - const removeSellAsset = useSellAsset((state) => state.removeSellAsset) - const cartExpanded = useBag((state) => state.bagExpanded) - const toggleCart = useBag((state) => state.toggleBag) - const isMobile = useIsMobile() - - const [boxHovered, toggleBoxHovered] = useReducer((state) => { - return !state - }, false) - const [buttonHovered, toggleButtonHovered] = useReducer((state) => { - return !state - }, false) - - const isSelected = useMemo(() => { - return sellAssets.some((item) => asset.id === item.id) - }, [asset, sellAssets]) - - const handleSelect = () => { - isSelected ? removeSellAsset(asset) : selectSellAsset(asset) - if ( - !cartExpanded && - !sellAssets.find( - (x) => x.tokenId === asset.tokenId && x.asset_contract.address === asset.asset_contract.address - ) && - !isMobile - ) - toggleCart() - } - - return ( - - - - - - {asset.name ? asset.name : `#${asset.tokenId}`} - - - {asset.collection?.name} - {asset.collectionIsVerified ? : null} - - - Last:  - {asset.lastPrice ? ( - <> - {formatEth(asset.lastPrice)} -  ETH - - ) : ( - - — - - )} - - - Floor:  - {asset.floorPrice ? ( - <> - {formatEth(asset.floorPrice)} -  ETH - - ) : ( - - — - - )} - - { - e.preventDefault() - e.stopPropagation() - handleSelect() - }} - > - {isSelected ? 'Remove' : 'Select'} - - - - - ) -} - const SelectAllButton = () => { const [isAllSelected, setIsAllSelected] = useState(false) const displayAssets = useWalletCollections((state) => state.displayAssets) @@ -497,23 +293,13 @@ const SelectAllButton = () => { } return ( {isAllSelected ? 'Deselect all' : 'Select all'} @@ -602,29 +388,3 @@ const CollectionFilterItem = ({
) } - -const CollectionSearch = ({ - searchText, - setSearchText, -}: { - searchText: string - setSearchText: Dispatch> -}) => { - return ( - ) => setSearchText(e.currentTarget.value)} - /> - ) -} diff --git a/src/nft/components/profile/view/WalletAssetDisplay.tsx b/src/nft/components/profile/view/WalletAssetDisplay.tsx new file mode 100644 index 0000000000..bd5e68fd18 --- /dev/null +++ b/src/nft/components/profile/view/WalletAssetDisplay.tsx @@ -0,0 +1,129 @@ +import { Box } from 'nft/components/Box' +import { Column } from 'nft/components/Flex' +import { VerifiedIcon } from 'nft/components/icons' +import { subheadSmall } from 'nft/css/common.css' +import { useBag, useIsMobile, useSellAsset } from 'nft/hooks' +import { DetailsOrigin, WalletAsset } from 'nft/types' +import { formatEth, getAssetHref } from 'nft/utils' +import { useMemo, useReducer } from 'react' +import { Link } from 'react-router-dom' + +import * as styles from './ProfilePage.css' + +export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset; isSellMode: boolean }) => { + const sellAssets = useSellAsset((state) => state.sellAssets) + const selectSellAsset = useSellAsset((state) => state.selectSellAsset) + const removeSellAsset = useSellAsset((state) => state.removeSellAsset) + const cartExpanded = useBag((state) => state.bagExpanded) + const toggleCart = useBag((state) => state.toggleBag) + const isMobile = useIsMobile() + + const [boxHovered, toggleBoxHovered] = useReducer((state) => { + return !state + }, false) + const [buttonHovered, toggleButtonHovered] = useReducer((state) => { + return !state + }, false) + + const isSelected = useMemo(() => { + return sellAssets.some((item) => asset.id === item.id) + }, [asset, sellAssets]) + + const handleSelect = () => { + isSelected ? removeSellAsset(asset) : selectSellAsset(asset) + if ( + !cartExpanded && + !sellAssets.find( + (x) => x.tokenId === asset.tokenId && x.asset_contract.address === asset.asset_contract.address + ) && + !isMobile + ) + toggleCart() + } + + return ( + + + + + + {asset.name ? asset.name : `#${asset.tokenId}`} + + + {asset.collection?.name} + {asset.collectionIsVerified ? : null} + + + Last:  + {asset.lastPrice ? ( + <> + {formatEth(asset.lastPrice)} +  ETH + + ) : ( + + — + + )} + + + Floor:  + {asset.floorPrice ? ( + <> + {formatEth(asset.floorPrice)} +  ETH + + ) : ( + + — + + )} + + {isSellMode && ( + { + e.preventDefault() + e.stopPropagation() + handleSelect() + }} + > + {isSelected ? 'Remove' : 'Select'} + + )} + + + + ) +} diff --git a/src/nft/utils/asset.ts b/src/nft/utils/asset.ts index 92a8046731..d731b8cf42 100644 --- a/src/nft/utils/asset.ts +++ b/src/nft/utils/asset.ts @@ -1,4 +1,4 @@ -import { DetailsOrigin, GenieAsset } from 'nft/types' +import { DetailsOrigin, GenieAsset, WalletAsset } from 'nft/types' export function getRarityStatus( rarityStatusCache: Map, @@ -17,6 +17,10 @@ export function getRarityStatus( return hasRarity } -export const getAssetHref = (asset: GenieAsset, origin?: DetailsOrigin) => { - return `/nfts/asset/${asset.address}/${asset.tokenId}${origin ? `?origin=${origin}` : ''}` +export const getAssetHref = (asset: GenieAsset | WalletAsset, origin?: DetailsOrigin) => { + const address = + (asset as GenieAsset).address !== undefined + ? (asset as GenieAsset).address + : (asset as WalletAsset).asset_contract.address + return `/nfts/asset/${address}/${asset.tokenId}${origin ? `?origin=${origin}` : ''}` }