From 91a5b3ef629b75c12ca9bc0bb37d21b1cd81192e Mon Sep 17 00:00:00 2001 From: Valentin Dosimont Date: Fri, 6 Dec 2024 16:02:51 +0100 Subject: [PATCH] feat: ux improvements pt.2 --- .../components/collection-header-stats.tsx | 1 - .../components/collection-header.tsx | 4 +- .../collection-items-data-grid-view.tsx | 160 +++++++------- .../collection-items-data-list-view.tsx | 138 ++++++------ .../portfolio-items-data-grid-view.tsx | 169 +++++++-------- .../portfolio-items-data-list-view.tsx | 198 +++++++++--------- .../portfolio-items-filters-content.tsx | 9 +- apps/arkmarket/src/config/homepage.ts | 2 +- apps/arkmarket/src/hooks/useSeasonPass.ts | 4 +- 9 files changed, 361 insertions(+), 324 deletions(-) diff --git a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header-stats.tsx b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header-stats.tsx index 23768e0b..70717c28 100644 --- a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header-stats.tsx +++ b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header-stats.tsx @@ -15,7 +15,6 @@ export default function CollectionHeaderStats({ const parsedCollectionFloor7dPercentage = parseFloat( collection.floor_7d_percentage, ); - console.log(collection); return (
diff --git a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header.tsx b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header.tsx index 564a8a96..82c43926 100644 --- a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header.tsx +++ b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-header.tsx @@ -32,7 +32,7 @@ export default function CollectionHeader({ collectionAddress, collection, }: CollectionHeaderProps) { - const [collapsibleOpen, setCollapsibleOpen] = useState(false); + const [collapsibleOpen, setCollapsibleOpen] = useState(true); const { data } = useCollection({ address: collectionAddress }); @@ -83,7 +83,7 @@ export default function CollectionHeader({ - {collection.description && ( + {collection.description || description && (
- return ( - - + + + + + +
+
+
- - - - -
-
-
- {token.metadata?.name ?? token.token_id} -
- {token.price ? ( -
- {formatUnits(token.price, 18)} LORDS -
- ) : ( -
- Not for sale -
- )} -
-
-

- {token.last_price ? ( - <>Last sale {formatUnits(token.last_price, 18)} LORDS - ) : null} -

-
- - {token.buy_in_progress ? ( + {tokenName} +
+ {token.price ? (
- Buy in progress - + {formatUnits(token.price, 18)} LORDS
- ) : token.is_listed && token.listing !== null && !token.listing.is_auction ? ( - ) : ( - - - Details - - +
+ Not for sale +
)} - - ); - }} - /> -
- ); +
+ +

+ {token.last_price ? ( + <>Last sale {formatUnits(token.last_price, 18)} LORDS + ) : null} +

+
+ + {token.buy_in_progress ? ( +
+ Buy in progress + +
+ ) : token.is_listed && token.listing !== null && !token.listing.is_auction ? ( + + ) : ( + + + Details + + + )} +
+ ) } diff --git a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-items-data-list-view.tsx b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-items-data-list-view.tsx index f82dbad9..9dbb5c89 100644 --- a/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-items-data-list-view.tsx +++ b/apps/arkmarket/src/app/collection/[collectionAddress]/components/collection-items-data-list-view.tsx @@ -1,6 +1,7 @@ "use client"; import { useMemo, useRef } from "react"; +import type { VirtualItem, Virtualizer } from "@tanstack/react-virtual"; import { useWindowVirtualizer } from "@tanstack/react-virtual"; import { cn, ellipsableStyles, formatUnits } from "@ark-market/ui"; @@ -20,6 +21,7 @@ import Media from "~/components/media"; import { PriceTag } from "@ark-market/ui/price-tag"; import Link from "next/link"; import { env } from "~/env"; +import { useSeasonPass } from "~/hooks/useSeasonPass"; const gridTemplateColumnValue = "grid-cols-[minmax(10rem,2fr)_repeat(5,minmax(7.25rem,1fr))]"; @@ -94,73 +96,79 @@ export default function CollectionItemsDataListView({ return null; } - return ( - rowVirtualizer.measureElement(node)} - className="group absolute flex w-full items-center" - style={{ - transform: `translateY(${virtualRow.start}px)`, - }} - > - - -
- -

- {token.metadata?.name ?? token.token_id} -

-
-
- - {token.price ? ( - - ) : ( - "_" - )} - - - {token.last_price ? ( -
- -

- {formatUnits(token.last_price, 18)}{" "} - LORDS -

-
- ) : ( - "_" - )} -
- _ - - - - _ - -
- ); + return })} ); } +function CollectionTokenItem({ token, rowVirtualizer, virtualRow }: { token: CollectionToken, rowVirtualizer: Virtualizer, virtualRow: VirtualItem }) { + const { realmName, isSeasonPass } = useSeasonPass(token); + const tokenName = isSeasonPass(token.collection_address) ? realmName : token.metadata?.name ?? token.token_id; + + return ( + rowVirtualizer.measureElement(node)} + className="group absolute flex w-full items-center" + style={{ + transform: `translateY(${virtualRow.start}px)`, + }} + > + + +
+ +

+ {tokenName} +

+
+
+ + {token.price ? ( + + ) : ( + "_" + )} + + + {token.last_price ? ( +
+ +

+ {formatUnits(token.last_price, 18)}{" "} + LORDS +

+
+ ) : ( + "_" + )} +
+ _ + + + + _ + +
+ ); +} diff --git a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-grid-view.tsx b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-grid-view.tsx index 69a11ccc..9008f584 100644 --- a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-grid-view.tsx +++ b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-grid-view.tsx @@ -24,6 +24,7 @@ import { TokenActionsCreateListing } from "~/app/token/[contractAddress]/[tokenI import Media from "~/components/media"; import { CollectionDescription } from "~/config/homepage"; import { CollectionTokenImage } from "~/app/collection/[collectionAddress]/components/collection-token-image"; +import { useSeasonPass } from "~/hooks/useSeasonPass"; const LargeGridContainer: Components["List"] = React.forwardRef( ({ style, children }, ref) => { @@ -87,94 +88,98 @@ export default function CollectionItemsDataGridView({ if (token === undefined) { return null; } + return + }} + /> + ); +} +function PortfolioTokenItem({ token, viewType, isOwner }: { token: WalletToken, viewType: string, isOwner: boolean }) { + const canListItem = isOwner && !token.list_price; + const { realmName, isSeasonPass } = useSeasonPass(token); + const tokenName = isSeasonPass(token.collection_address) ? realmName : token.metadata?.name ?? token.token_id; - const canListItem = isOwner && !token.list_price; - - return ( - // TODO @YohanTz: Extract to NftCard component and sub-components - + return ( + // TODO @YohanTz: Extract to NftCard component and sub-components + + + + {/* TODO: Media part of NftCardMedia */} + + + + +
+
- - {/* TODO: Media part of NftCardMedia */} - - +

+ {tokenName} +

- -
-
- -

- {token.metadata?.name ?? token.token_id} -

- - -

- {token.collection_name} -

- - - - {token.list_price ? ( -

- {formatUnits(token.list_price, 18)} LORDS -

- ) : ( -

Not for sale

- )} -
-
-
- {token.last_price ? ( -

- Last sale {formatEther(BigInt(token.last_price))} LORDS -

- ) : null} -
- {canListItem ? ( - - List for sale - - ) : ( - - - Details - - + - - ); - }} - /> + > +

+ {token.collection_name} +

+ + + + {token.list_price ? ( +

+ {formatUnits(token.list_price, 18)} LORDS +

+ ) : ( +

Not for sale

+ )} +
+
+
+ {token.last_price ? ( +

+ Last sale {formatEther(BigInt(token.last_price))} LORDS +

+ ) : null} +
+ {canListItem ? ( + + List for sale + + ) : ( + + + Details + + + )} +
+
); } diff --git a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-list-view.tsx b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-list-view.tsx index 64ac199e..b634b0e9 100644 --- a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-list-view.tsx +++ b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-data-list-view.tsx @@ -1,5 +1,6 @@ import { useRef } from "react"; import Link from "next/link"; +import type { VirtualItem, Virtualizer } from "@tanstack/react-virtual"; import { useWindowVirtualizer } from "@tanstack/react-virtual"; import { cn, ellipsableStyles, formatUnits, timeSince } from "@ark-market/ui"; @@ -16,15 +17,13 @@ import { import type { WalletToken } from "../queries/getWalletData"; import { TokenActionsCreateListing } from "~/app/token/[contractAddress]/[tokenId]/components/token-actions-create-listing"; -import ActivityTime from "~/components/cells/activity-time-cell"; -import TokenLastSoldCell from "~/components/cells/token-last-price-cell"; import Media from "~/components/media"; import { CollectionDescription } from "~/config/homepage"; -import { NftCardAction } from "@ark-market/ui/nft-card"; import { NoResult } from "@ark-market/ui/icons"; +import { useSeasonPass } from "~/hooks/useSeasonPass"; const gridTemplateColumnValue = - "grid-cols-[minmax(11rem,2fr)_repeat(4,minmax(10rem,1fr))_minmax(6.5rem,8rem)]"; + "grid-cols-[minmax(11rem,2fr)_repeat(4,minmax(10rem,1fr))_minmax(6.5rem,15rem)]"; interface PortfolioItemsDataListViewProps { walletTokens: WalletToken[]; @@ -95,96 +94,7 @@ export default function PortfolioItemsDataListView({ if (token === undefined) { return null; } - const canListItem = isOwner && !token.list_price; - - return ( - rowVirtualizer.measureElement(node)} // Measure dynamic row height - style={{ - transform: `translateY(${virtualRow.start}px)`, - }} - > - -
- - -

- {token.metadata?.name ?? token.token_id} -

-
-
- - {token.list_price ? ( -
- -

- {formatUnits(token.list_price, 18)}{" "} - LORDS -

-
- ) : ( - "_" - )} -
- - {token.best_offer ? ( -
- -

- {formatUnits(token.best_offer, 18)}{" "} - LORDS -

-
- ) : ( - "_" - )} -
- - {token.floor ? ( -
- -

- {formatUnits(token.floor, 18)}{" "} - ETH -

-
- ) : ( - "_" - )} -
- - {token.received_at ? timeSince(token.received_at) : "_"} - - - {canListItem ? ( - - List for sale - - ) : ( - - - Details - - - )} - -
- ); + return })} @@ -197,3 +107,103 @@ export default function PortfolioItemsDataListView({ ); } +function PortfolioTokenItem({ token, rowVirtualizer, virtualRow, isOwner }: { token: WalletToken, rowVirtualizer: Virtualizer, virtualRow: VirtualItem, isOwner: boolean }) { + const canListItem = isOwner && !token.list_price; + const { realmName, isSeasonPass } = useSeasonPass(token); + const tokenName = isSeasonPass(token.collection_address) ? realmName : token.metadata?.name ?? token.token_id; + + return ( + rowVirtualizer.measureElement(node)} // Measure dynamic row height + style={{ + transform: `translateY(${virtualRow.start}px)`, + }} + > + +
+ + +

+ {tokenName} +

+
+
+ + {token.list_price ? ( +
+ +

+ {formatUnits(token.list_price, 18)}{" "} +

+
+ ) : ( + "_" + )} +
+ + {token.best_offer ? ( +
+ +

+ {formatUnits(token.best_offer, 18)}{" "} +

+
+ ) : ( + "_" + )} +
+ + {token.floor ? ( +
+ +

+ {formatUnits(token.floor, 18)}{" "} +

+
+ ) : ( + "_" + )} +
+ + {token.received_at ? timeSince(token.received_at) : "_"} + + + {canListItem ? ( + + + + ) : ( + + )} + +
+ ); +} diff --git a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-filters-content.tsx b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-filters-content.tsx index 82cedf7b..5b77d469 100644 --- a/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-filters-content.tsx +++ b/apps/arkmarket/src/app/wallet/[walletAddress]/components/portfolio-items-filters-content.tsx @@ -21,6 +21,8 @@ import { walletCollectionFilterParser, } from "../search-params"; import useWalletCollections from "~/hooks/useWalletCollections"; +import { ChainId, CollectionAddresses, CollectionDescription } from "~/config/homepage"; +import { on } from "events"; interface PortfolioItemsFiltersContentProps { walletAddress: string; @@ -76,7 +78,10 @@ export default function PortfolioItemsFiltersContent({ ); const walletCollections = useMemo( - () => infiniteData?.pages.flatMap((page) => page.data) ?? [], + () => infiniteData?.pages.flatMap((page) => page.data).filter((c, idx) => { + const collection = CollectionDescription[c.address]; + return collection !== undefined + }).filter(Boolean) ?? [], [infiniteData], ); @@ -145,7 +150,7 @@ export default function PortfolioItemsFiltersContent({

{formatUnits( BigInt(collection.user_token_count) * - BigInt(collection.floor ?? "0"), + BigInt(collection.floor ?? "0"), 18, )}

diff --git a/apps/arkmarket/src/config/homepage.ts b/apps/arkmarket/src/config/homepage.ts index 898a835f..7e988978 100644 --- a/apps/arkmarket/src/config/homepage.ts +++ b/apps/arkmarket/src/config/homepage.ts @@ -179,7 +179,7 @@ export const CollectionDescription: Record = { [CollectionAddresses[Collections.ETERNUMSEASONPASS][ChainId.SN_MAIN]]: { created: "2024", description: - "Eternum Season Pass", + "Game passes to play Eternum Season 0, each with unique resource profiles. 8000 can be minted by Realms. All existing gamepasses shown here, but new ones are frequently minted.", }, }; diff --git a/apps/arkmarket/src/hooks/useSeasonPass.ts b/apps/arkmarket/src/hooks/useSeasonPass.ts index 4a1167ff..0be495fd 100644 --- a/apps/arkmarket/src/hooks/useSeasonPass.ts +++ b/apps/arkmarket/src/hooks/useSeasonPass.ts @@ -313,10 +313,12 @@ export function useSeasonPass(token: Token | CollectionToken | WalletToken) { } return token.metadata?.attributes.map((a) => resources.find(r => r.trait === a.value)).filter(Boolean) as Resources[] - }, [token]) + }, [token]); + const realmName = `${token.metadata?.name} #${token.token_id}`; return { isSeasonPass, realmsResources, + realmName, } }