@medialane/ui 0.312.0 → 0.313.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,9 +38,8 @@ var import_swr = __toESM(require("swr"), 1);
38
38
  var import_lucide_react = require("lucide-react");
39
39
  var import_load_more_sentinel = require("./load-more-sentinel.js");
40
40
  var import_skeleton = require("./skeleton.js");
41
- var import_tabs = require("./tabs.js");
42
- var import_underline_tabs = require("./underline-tabs.js");
43
- var import_address_display = require("./address-display.js");
41
+ var import_portfolio_header = require("./portfolio-header.js");
42
+ var import_portfolio_chip_filter = require("./portfolio-chip-filter.js");
44
43
  var import_hidden_content_banner = require("./hidden-content-banner.js");
45
44
  var import_tab_empty_state = require("./tab-empty-state.js");
46
45
  var import_token_card = require("./token-card.js");
@@ -61,6 +60,12 @@ var import_use_tokens = require("../utils/use-tokens.js");
61
60
  var import_use_collections = require("../utils/use-collections.js");
62
61
  var import_use_orders = require("../utils/use-orders.js");
63
62
  var import_use_activities = require("../utils/use-activities.js");
63
+ const TABS = [
64
+ { key: "assets", label: "Assets" },
65
+ { key: "collections", label: "Collections" },
66
+ { key: "listings", label: "Listings" },
67
+ { key: "activity", label: "Activity" }
68
+ ];
64
69
  const GRID = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4";
65
70
  const isNft = (itemType) => itemType === "ERC721" || itemType === "ERC1155";
66
71
  function AccountPage({ address, useAssetActions, useMarketActions }) {
@@ -79,53 +84,50 @@ function AccountPage({ address, useAssetActions, useMarketActions }) {
79
84
  const actions = useAssetActions({ refresh: () => void refreshTokens() });
80
85
  const market = useMarketActions();
81
86
  const listings = orders.filter((o) => o.status === "ACTIVE" && isNft(o.offer.itemType));
82
- const tabs = [
83
- { id: "assets", label: "Assets", icon: import_lucide_react.Image, count: total ?? void 0 },
84
- { id: "collections", label: "Collections", icon: import_lucide_react.LayoutGrid, count: collectionsLoading ? void 0 : collections.length },
85
- { id: "listings", label: "Listings", icon: import_lucide_react.ShoppingBag, count: ordersLoading ? void 0 : listings.length },
86
- { id: "activity", label: "Activity", icon: import_lucide_react.Activity }
87
- ];
88
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-h-screen overflow-x-hidden pb-20", children: [
87
+ const counts = {
88
+ assets: total ?? void 0,
89
+ collections: collectionsLoading ? void 0 : collections.length,
90
+ listings: ordersLoading ? void 0 : listings.length
91
+ };
92
+ const tabs = TABS.map(({ key, label }) => ({ key, label: counts[key] === void 0 ? label : `${label} (${counts[key]})` }));
93
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-6 px-4 pt-20 pb-8 sm:px-6 lg:px-8", children: [
89
94
  hidden === true ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_hidden_content_banner.HiddenContentBanner, {}) : null,
90
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3 px-6 pt-20 pb-6", children: [
91
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_address_display.AddressDisplay, { address, chars: 6, className: "min-w-0 text-2xl font-black tracking-tight tabular-nums sm:text-3xl" }),
92
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "shrink-0 rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground", children: "Starknet" })
93
- ] }),
94
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
95
- import_underline_tabs.UnderlineTabs,
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
+ import_portfolio_header.PortfolioHeader,
96
97
  {
97
- tabs,
98
- value: activeTab,
99
- onValueChange: (id) => setActiveTab(id),
100
- trailing: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_more_menu.MoreMenu, { explorerHref: `${import_explorer_url.EXPLORER_URL}/contract/${address}`, shareTitle: (0, import_address.shortenAddress)(address), onReport: () => setReportOpen(true) }),
101
- children: [
102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tabs.TabsContent, { value: "assets", className: "mt-6 px-6", children: tokensLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_token_card.TokenCardSkeleton, {}, i)) }) : tokens.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.Image, heading: "No assets yet", body: "Works this account owns appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
103
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: tokens.map((t) => {
104
- const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));
105
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
106
- import_token_card.TokenCard,
107
- {
108
- token: t,
109
- isOwner,
110
- usdValue: (0, import_order_usd.orderUsd)(listing?.price, prices),
111
- onList: isOwner ? actions.list : void 0,
112
- onTransfer: isOwner ? actions.transfer : void 0,
113
- onCancel: isOwner ? actions.cancel : void 0,
114
- onBuy: !isOwner && listing ? () => market.buy(listing) : void 0,
115
- onOffer: isOwner ? void 0 : market.offer,
116
- onReport: setReportToken
117
- },
118
- `${t.contractAddress}-${t.tokenId}`
119
- );
120
- }) }),
121
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_load_more_sentinel.LoadMoreSentinel, { hasMore, isLoading: isLoadingMore, onLoadMore: loadMore })
122
- ] }) }),
123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tabs.TabsContent, { value: "collections", className: "mt-6 px-6", children: collectionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.LayoutGrid, heading: "No collections yet", body: "Collections this account owns appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCard, { collection: c }, c.contractAddress)) }) }),
124
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tabs.TabsContent, { value: "listings", className: "mt-6 px-6", children: ordersLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCardSkeleton, {}, i)) }) : listings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.ShoppingBag, heading: "No active listings", body: "Works this account lists for sale appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: listings.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCard, { order: o, onBuy: isOwner ? void 0 : market.buy, usdValue: (0, import_order_usd.orderUsd)(o.price, prices), overflowMenu: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card_menu.ListingCardMenu, { order: o }) }, o.orderHash)) }) }),
125
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tabs.TabsContent, { value: "activity", className: "mt-6 max-w-2xl px-6", children: activitiesLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_skeleton.Skeleton, { className: "h-10 w-full rounded-xl" }, i)) }) : activities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.Activity, heading: "No activity yet", body: "This account's activity appears here as it happens." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: activities.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_activity_timeline_row.ActivityTimelineRow, { event: a, isLast: i === activities.length - 1 }, i)) }) })
126
- ]
98
+ address,
99
+ label: "Account",
100
+ trailing: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground", children: "Starknet" }),
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_more_menu.MoreMenu, { explorerHref: `${import_explorer_url.EXPLORER_URL}/contract/${address}`, shareTitle: (0, import_address.shortenAddress)(address), onReport: () => setReportOpen(true) })
103
+ ] })
127
104
  }
128
105
  ),
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_portfolio_chip_filter.PortfolioChipFilter, { options: tabs, value: activeTab, onChange: (key) => setActiveTab(key), showAll: false }),
107
+ activeTab === "assets" && (tokensLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_token_card.TokenCardSkeleton, {}, i)) }) : tokens.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.Image, heading: "No assets yet", body: "Works this account owns appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: tokens.map((t) => {
109
+ const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));
110
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
111
+ import_token_card.TokenCard,
112
+ {
113
+ token: t,
114
+ isOwner,
115
+ usdValue: (0, import_order_usd.orderUsd)(listing?.price, prices),
116
+ onList: isOwner ? actions.list : void 0,
117
+ onTransfer: isOwner ? actions.transfer : void 0,
118
+ onCancel: isOwner ? actions.cancel : void 0,
119
+ onBuy: !isOwner && listing ? () => market.buy(listing) : void 0,
120
+ onOffer: isOwner ? void 0 : market.offer,
121
+ onReport: setReportToken
122
+ },
123
+ `${t.contractAddress}-${t.tokenId}`
124
+ );
125
+ }) }),
126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_load_more_sentinel.LoadMoreSentinel, { hasMore, isLoading: isLoadingMore, onLoadMore: loadMore })
127
+ ] })),
128
+ activeTab === "collections" && (collectionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.LayoutGrid, heading: "No collections yet", body: "Collections this account owns appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCard, { collection: c }, c.contractAddress)) })),
129
+ activeTab === "listings" && (ordersLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCardSkeleton, {}, i)) }) : listings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.ShoppingBag, heading: "No active listings", body: "Works this account lists for sale appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: listings.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCard, { order: o, onBuy: isOwner ? void 0 : market.buy, usdValue: (0, import_order_usd.orderUsd)(o.price, prices), overflowMenu: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card_menu.ListingCardMenu, { order: o }) }, o.orderHash)) })),
130
+ activeTab === "activity" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "max-w-2xl", children: activitiesLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_skeleton.Skeleton, { className: "h-10 w-full rounded-xl" }, i)) }) : activities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.Activity, heading: "No activity yet", body: "This account's activity appears here as it happens." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: activities.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_activity_timeline_row.ActivityTimelineRow, { event: a, isLast: i === activities.length - 1 }, i)) }) }) : null,
129
131
  actions.dialogs,
130
132
  market.dialogs,
131
133
  reportToken ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/account-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport useSWR from \"swr\";\nimport { Activity, Image as ImageIcon, LayoutGrid, ShoppingBag } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { TabsContent } from \"./tabs.js\";\nimport { UnderlineTabs, type UnderlineTab } from \"./underline-tabs.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { TabEmptyState } from \"./tab-empty-state.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { ActivityTimelineRow } from \"./activity-timeline-row.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport type { UsePortfolioAssetActions } from \"./portfolio-assets.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { shortenAddress } from \"../utils/address.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { orderUsd } from \"../utils/order-usd.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useUsdPrices } from \"../utils/use-usd-prices.js\";\nimport { useOwnedTokens } from \"../utils/use-tokens.js\";\nimport { useCollectionsByOwner } from \"../utils/use-collections.js\";\nimport { useUserOrders } from \"../utils/use-orders.js\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\n\ntype TabId = \"assets\" | \"collections\" | \"listings\" | \"activity\";\n\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\";\nconst isNft = (itemType: string) => itemType === \"ERC721\" || itemType === \"ERC1155\";\n\nexport interface AccountPageProps {\n address: string;\n useAssetActions: UsePortfolioAssetActions;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function AccountPage({ address, useAssetActions, useMarketActions }: AccountPageProps) {\n const { walletAddress } = useMedialaneApp();\n const client = useMedialaneClient();\n const prices = useUsdPrices();\n const [activeTab, setActiveTab] = useState<TabId>(\"assets\");\n const [reportOpen, setReportOpen] = useState(false);\n const [reportToken, setReportToken] = useState<ApiToken | null>(null);\n\n const isOwner = !!walletAddress && isSameAddress(address, walletAddress);\n\n const { tokens, total, isLoading: tokensLoading, isLoadingMore, hasMore, loadMore, mutate: refreshTokens } = useOwnedTokens(address);\n const { collections, isLoading: collectionsLoading } = useCollectionsByOwner(address);\n const { orders, isLoading: ordersLoading } = useUserOrders(address);\n const { activities, isLoading: activitiesLoading } = useActivitiesByAddress(activeTab === \"activity\" ? address : null);\n const { data: hidden } = useSWR(`creator-hidden-${address}`, () => client.api.creators.isHidden(address).catch(() => false));\n const actions = useAssetActions({ refresh: () => void refreshTokens() });\n const market = useMarketActions();\n\n const listings = orders.filter((o) => o.status === \"ACTIVE\" && isNft(o.offer.itemType));\n const tabs: UnderlineTab[] = [\n { id: \"assets\", label: \"Assets\", icon: ImageIcon, count: total ?? undefined },\n { id: \"collections\", label: \"Collections\", icon: LayoutGrid, count: collectionsLoading ? undefined : collections.length },\n { id: \"listings\", label: \"Listings\", icon: ShoppingBag, count: ordersLoading ? undefined : listings.length },\n { id: \"activity\", label: \"Activity\", icon: Activity },\n ];\n\n return (\n <div className=\"min-h-screen overflow-x-hidden pb-20\">\n {hidden === true ? <HiddenContentBanner /> : null}\n\n <div className=\"flex items-center justify-between gap-3 px-6 pt-20 pb-6\">\n <AddressDisplay address={address} chars={6} className=\"min-w-0 text-2xl font-black tracking-tight tabular-nums sm:text-3xl\" />\n <span className=\"shrink-0 rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground\">Starknet</span>\n </div>\n\n <UnderlineTabs\n tabs={tabs}\n value={activeTab}\n onValueChange={(id) => setActiveTab(id as TabId)}\n trailing={<MoreMenu explorerHref={`${EXPLORER_URL}/contract/${address}`} shareTitle={shortenAddress(address)} onReport={() => setReportOpen(true)} />}\n >\n <TabsContent value=\"assets\" className=\"mt-6 px-6\">\n {tokensLoading ? (\n <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <TokenCardSkeleton key={i} />)}</div>\n ) : tokens.length === 0 ? (\n <TabEmptyState icon={ImageIcon} heading=\"No assets yet\" body=\"Works this account owns appear here.\" />\n ) : (\n <>\n <div className={GRID}>\n {tokens.map((t: ApiToken) => {\n const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));\n return (\n <TokenCard\n key={`${t.contractAddress}-${t.tokenId}`}\n token={t}\n isOwner={isOwner}\n usdValue={orderUsd(listing?.price, prices)}\n onList={isOwner ? actions.list : undefined}\n onTransfer={isOwner ? actions.transfer : undefined}\n onCancel={isOwner ? actions.cancel : undefined}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n onReport={setReportToken}\n />\n );\n })}\n </div>\n <LoadMoreSentinel hasMore={hasMore} isLoading={isLoadingMore} onLoadMore={loadMore} />\n </>\n )}\n </TabsContent>\n\n <TabsContent value=\"collections\" className=\"mt-6 px-6\">\n {collectionsLoading ? (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{Array.from({ length: 6 }).map((_, i) => <CollectionCardSkeleton key={i} />)}</div>\n ) : collections.length === 0 ? (\n <TabEmptyState icon={LayoutGrid} heading=\"No collections yet\" body=\"Collections this account owns appear here.\" />\n ) : (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{collections.map((c) => <CollectionCard key={c.contractAddress} collection={c} />)}</div>\n )}\n </TabsContent>\n\n <TabsContent value=\"listings\" className=\"mt-6 px-6\">\n {ordersLoading ? (\n <div className={GRID}>{Array.from({ length: 4 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>\n ) : listings.length === 0 ? (\n <TabEmptyState icon={ShoppingBag} heading=\"No active listings\" body=\"Works this account lists for sale appear here.\" />\n ) : (\n <div className={GRID}>\n {listings.map((o) => <ListingCard key={o.orderHash} order={o} onBuy={isOwner ? undefined : market.buy} usdValue={orderUsd(o.price, prices)} overflowMenu={<ListingCardMenu order={o} />} />)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"activity\" className=\"mt-6 max-w-2xl px-6\">\n {activitiesLoading ? (\n <div className=\"space-y-5\">{Array.from({ length: 6 }).map((_, i) => <Skeleton key={i} className=\"h-10 w-full rounded-xl\" />)}</div>\n ) : activities.length === 0 ? (\n <TabEmptyState icon={Activity} heading=\"No activity yet\" body=\"This account's activity appears here as it happens.\" />\n ) : (\n <div>{activities.map((a, i) => <ActivityTimelineRow key={i} event={a} isLast={i === activities.length - 1} />)}</div>\n )}\n </TabsContent>\n </UnderlineTabs>\n\n {actions.dialogs}\n {market.dialogs}\n {reportToken ? (\n <ReportDialog\n open={!!reportToken}\n onOpenChange={(open) => { if (!open) setReportToken(null); }}\n target={{ type: \"TOKEN\", contract: reportToken.contractAddress, tokenId: reportToken.tokenId, name: reportToken.metadata?.name ?? undefined }}\n />\n ) : null}\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"CREATOR\", address, name: shortenAddress(address) }} />\n </div>\n );\n}\n"],"mappings":"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","names":["useSWR","ImageIcon"]}
1
+ {"version":3,"sources":["../../src/components/account-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport useSWR from \"swr\";\nimport { Activity, Image as ImageIcon, LayoutGrid, ShoppingBag } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { PortfolioHeader } from \"./portfolio-header.js\";\nimport { PortfolioChipFilter } from \"./portfolio-chip-filter.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { TabEmptyState } from \"./tab-empty-state.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { ActivityTimelineRow } from \"./activity-timeline-row.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport type { UsePortfolioAssetActions } from \"./portfolio-assets.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { shortenAddress } from \"../utils/address.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { orderUsd } from \"../utils/order-usd.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useUsdPrices } from \"../utils/use-usd-prices.js\";\nimport { useOwnedTokens } from \"../utils/use-tokens.js\";\nimport { useCollectionsByOwner } from \"../utils/use-collections.js\";\nimport { useUserOrders } from \"../utils/use-orders.js\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\n\ntype TabId = \"assets\" | \"collections\" | \"listings\" | \"activity\";\n\nconst TABS: { key: TabId; label: string }[] = [\n { key: \"assets\", label: \"Assets\" },\n { key: \"collections\", label: \"Collections\" },\n { key: \"listings\", label: \"Listings\" },\n { key: \"activity\", label: \"Activity\" },\n];\n\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\";\nconst isNft = (itemType: string) => itemType === \"ERC721\" || itemType === \"ERC1155\";\n\nexport interface AccountPageProps {\n address: string;\n useAssetActions: UsePortfolioAssetActions;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function AccountPage({ address, useAssetActions, useMarketActions }: AccountPageProps) {\n const { walletAddress } = useMedialaneApp();\n const client = useMedialaneClient();\n const prices = useUsdPrices();\n const [activeTab, setActiveTab] = useState<TabId>(\"assets\");\n const [reportOpen, setReportOpen] = useState(false);\n const [reportToken, setReportToken] = useState<ApiToken | null>(null);\n\n const isOwner = !!walletAddress && isSameAddress(address, walletAddress);\n\n const { tokens, total, isLoading: tokensLoading, isLoadingMore, hasMore, loadMore, mutate: refreshTokens } = useOwnedTokens(address);\n const { collections, isLoading: collectionsLoading } = useCollectionsByOwner(address);\n const { orders, isLoading: ordersLoading } = useUserOrders(address);\n const { activities, isLoading: activitiesLoading } = useActivitiesByAddress(activeTab === \"activity\" ? address : null);\n const { data: hidden } = useSWR(`creator-hidden-${address}`, () => client.api.creators.isHidden(address).catch(() => false));\n const actions = useAssetActions({ refresh: () => void refreshTokens() });\n const market = useMarketActions();\n\n const listings = orders.filter((o) => o.status === \"ACTIVE\" && isNft(o.offer.itemType));\n const counts: Partial<Record<TabId, number | undefined>> = {\n assets: total ?? undefined,\n collections: collectionsLoading ? undefined : collections.length,\n listings: ordersLoading ? undefined : listings.length,\n };\n const tabs = TABS.map(({ key, label }) => ({ key, label: counts[key] === undefined ? label : `${label} (${counts[key]})` }));\n\n return (\n <div className=\"space-y-6 px-4 pt-20 pb-8 sm:px-6 lg:px-8\">\n {hidden === true ? <HiddenContentBanner /> : null}\n\n <PortfolioHeader\n address={address}\n label=\"Account\"\n trailing={\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground\">Starknet</span>\n <MoreMenu explorerHref={`${EXPLORER_URL}/contract/${address}`} shareTitle={shortenAddress(address)} onReport={() => setReportOpen(true)} />\n </div>\n }\n />\n\n <PortfolioChipFilter options={tabs} value={activeTab} onChange={(key) => setActiveTab(key as TabId)} showAll={false} />\n\n {activeTab === \"assets\" && (\n tokensLoading ? (\n <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <TokenCardSkeleton key={i} />)}</div>\n ) : tokens.length === 0 ? (\n <TabEmptyState icon={ImageIcon} heading=\"No assets yet\" body=\"Works this account owns appear here.\" />\n ) : (\n <>\n <div className={GRID}>\n {tokens.map((t: ApiToken) => {\n const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));\n return (\n <TokenCard\n key={`${t.contractAddress}-${t.tokenId}`}\n token={t}\n isOwner={isOwner}\n usdValue={orderUsd(listing?.price, prices)}\n onList={isOwner ? actions.list : undefined}\n onTransfer={isOwner ? actions.transfer : undefined}\n onCancel={isOwner ? actions.cancel : undefined}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n onReport={setReportToken}\n />\n );\n })}\n </div>\n <LoadMoreSentinel hasMore={hasMore} isLoading={isLoadingMore} onLoadMore={loadMore} />\n </>\n )\n )}\n\n {activeTab === \"collections\" && (\n collectionsLoading ? (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{Array.from({ length: 6 }).map((_, i) => <CollectionCardSkeleton key={i} />)}</div>\n ) : collections.length === 0 ? (\n <TabEmptyState icon={LayoutGrid} heading=\"No collections yet\" body=\"Collections this account owns appear here.\" />\n ) : (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{collections.map((c) => <CollectionCard key={c.contractAddress} collection={c} />)}</div>\n )\n )}\n\n {activeTab === \"listings\" && (\n ordersLoading ? (\n <div className={GRID}>{Array.from({ length: 4 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>\n ) : listings.length === 0 ? (\n <TabEmptyState icon={ShoppingBag} heading=\"No active listings\" body=\"Works this account lists for sale appear here.\" />\n ) : (\n <div className={GRID}>\n {listings.map((o) => <ListingCard key={o.orderHash} order={o} onBuy={isOwner ? undefined : market.buy} usdValue={orderUsd(o.price, prices)} overflowMenu={<ListingCardMenu order={o} />} />)}\n </div>\n )\n )}\n\n {activeTab === \"activity\" ? (\n <div className=\"max-w-2xl\">\n {activitiesLoading ? (\n <div className=\"space-y-5\">{Array.from({ length: 6 }).map((_, i) => <Skeleton key={i} className=\"h-10 w-full rounded-xl\" />)}</div>\n ) : activities.length === 0 ? (\n <TabEmptyState icon={Activity} heading=\"No activity yet\" body=\"This account's activity appears here as it happens.\" />\n ) : (\n <div>{activities.map((a, i) => <ActivityTimelineRow key={i} event={a} isLast={i === activities.length - 1} />)}</div>\n )}\n </div>\n ) : null}\n\n {actions.dialogs}\n {market.dialogs}\n {reportToken ? (\n <ReportDialog\n open={!!reportToken}\n onOpenChange={(open) => { if (!open) setReportToken(null); }}\n target={{ type: \"TOKEN\", contract: reportToken.contractAddress, tokenId: reportToken.tokenId, name: reportToken.metadata?.name ?? undefined }}\n />\n ) : null}\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"CREATOR\", address, name: shortenAddress(address) }} />\n </div>\n );\n}\n"],"mappings":"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","names":["useSWR","ImageIcon"]}
@@ -5,9 +5,8 @@ import useSWR from "swr";
5
5
  import { Activity, Image as ImageIcon, LayoutGrid, ShoppingBag } from "lucide-react";
6
6
  import { LoadMoreSentinel } from "./load-more-sentinel.js";
7
7
  import { Skeleton } from "./skeleton.js";
8
- import { TabsContent } from "./tabs.js";
9
- import { UnderlineTabs } from "./underline-tabs.js";
10
- import { AddressDisplay } from "./address-display.js";
8
+ import { PortfolioHeader } from "./portfolio-header.js";
9
+ import { PortfolioChipFilter } from "./portfolio-chip-filter.js";
11
10
  import { HiddenContentBanner } from "./hidden-content-banner.js";
12
11
  import { TabEmptyState } from "./tab-empty-state.js";
13
12
  import { TokenCard, TokenCardSkeleton } from "./token-card.js";
@@ -28,6 +27,12 @@ import { useOwnedTokens } from "../utils/use-tokens.js";
28
27
  import { useCollectionsByOwner } from "../utils/use-collections.js";
29
28
  import { useUserOrders } from "../utils/use-orders.js";
30
29
  import { useActivitiesByAddress } from "../utils/use-activities.js";
30
+ const TABS = [
31
+ { key: "assets", label: "Assets" },
32
+ { key: "collections", label: "Collections" },
33
+ { key: "listings", label: "Listings" },
34
+ { key: "activity", label: "Activity" }
35
+ ];
31
36
  const GRID = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4";
32
37
  const isNft = (itemType) => itemType === "ERC721" || itemType === "ERC1155";
33
38
  function AccountPage({ address, useAssetActions, useMarketActions }) {
@@ -46,53 +51,50 @@ function AccountPage({ address, useAssetActions, useMarketActions }) {
46
51
  const actions = useAssetActions({ refresh: () => void refreshTokens() });
47
52
  const market = useMarketActions();
48
53
  const listings = orders.filter((o) => o.status === "ACTIVE" && isNft(o.offer.itemType));
49
- const tabs = [
50
- { id: "assets", label: "Assets", icon: ImageIcon, count: total ?? void 0 },
51
- { id: "collections", label: "Collections", icon: LayoutGrid, count: collectionsLoading ? void 0 : collections.length },
52
- { id: "listings", label: "Listings", icon: ShoppingBag, count: ordersLoading ? void 0 : listings.length },
53
- { id: "activity", label: "Activity", icon: Activity }
54
- ];
55
- return /* @__PURE__ */ jsxs("div", { className: "min-h-screen overflow-x-hidden pb-20", children: [
54
+ const counts = {
55
+ assets: total ?? void 0,
56
+ collections: collectionsLoading ? void 0 : collections.length,
57
+ listings: ordersLoading ? void 0 : listings.length
58
+ };
59
+ const tabs = TABS.map(({ key, label }) => ({ key, label: counts[key] === void 0 ? label : `${label} (${counts[key]})` }));
60
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 px-4 pt-20 pb-8 sm:px-6 lg:px-8", children: [
56
61
  hidden === true ? /* @__PURE__ */ jsx(HiddenContentBanner, {}) : null,
57
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 px-6 pt-20 pb-6", children: [
58
- /* @__PURE__ */ jsx(AddressDisplay, { address, chars: 6, className: "min-w-0 text-2xl font-black tracking-tight tabular-nums sm:text-3xl" }),
59
- /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground", children: "Starknet" })
60
- ] }),
61
- /* @__PURE__ */ jsxs(
62
- UnderlineTabs,
62
+ /* @__PURE__ */ jsx(
63
+ PortfolioHeader,
63
64
  {
64
- tabs,
65
- value: activeTab,
66
- onValueChange: (id) => setActiveTab(id),
67
- trailing: /* @__PURE__ */ jsx(MoreMenu, { explorerHref: `${EXPLORER_URL}/contract/${address}`, shareTitle: shortenAddress(address), onReport: () => setReportOpen(true) }),
68
- children: [
69
- /* @__PURE__ */ jsx(TabsContent, { value: "assets", className: "mt-6 px-6", children: tokensLoading ? /* @__PURE__ */ jsx("div", { className: GRID, children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(TokenCardSkeleton, {}, i)) }) : tokens.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: ImageIcon, heading: "No assets yet", body: "Works this account owns appear here." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
70
- /* @__PURE__ */ jsx("div", { className: GRID, children: tokens.map((t) => {
71
- const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));
72
- return /* @__PURE__ */ jsx(
73
- TokenCard,
74
- {
75
- token: t,
76
- isOwner,
77
- usdValue: orderUsd(listing?.price, prices),
78
- onList: isOwner ? actions.list : void 0,
79
- onTransfer: isOwner ? actions.transfer : void 0,
80
- onCancel: isOwner ? actions.cancel : void 0,
81
- onBuy: !isOwner && listing ? () => market.buy(listing) : void 0,
82
- onOffer: isOwner ? void 0 : market.offer,
83
- onReport: setReportToken
84
- },
85
- `${t.contractAddress}-${t.tokenId}`
86
- );
87
- }) }),
88
- /* @__PURE__ */ jsx(LoadMoreSentinel, { hasMore, isLoading: isLoadingMore, onLoadMore: loadMore })
89
- ] }) }),
90
- /* @__PURE__ */ jsx(TabsContent, { value: "collections", className: "mt-6 px-6", children: collectionsLoading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: LayoutGrid, heading: "No collections yet", body: "Collections this account owns appear here." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ jsx(CollectionCard, { collection: c }, c.contractAddress)) }) }),
91
- /* @__PURE__ */ jsx(TabsContent, { value: "listings", className: "mt-6 px-6", children: ordersLoading ? /* @__PURE__ */ jsx("div", { className: GRID, children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(ListingCardSkeleton, {}, i)) }) : listings.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: ShoppingBag, heading: "No active listings", body: "Works this account lists for sale appear here." }) : /* @__PURE__ */ jsx("div", { className: GRID, children: listings.map((o) => /* @__PURE__ */ jsx(ListingCard, { order: o, onBuy: isOwner ? void 0 : market.buy, usdValue: orderUsd(o.price, prices), overflowMenu: /* @__PURE__ */ jsx(ListingCardMenu, { order: o }) }, o.orderHash)) }) }),
92
- /* @__PURE__ */ jsx(TabsContent, { value: "activity", className: "mt-6 max-w-2xl px-6", children: activitiesLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full rounded-xl" }, i)) }) : activities.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: Activity, heading: "No activity yet", body: "This account's activity appears here as it happens." }) : /* @__PURE__ */ jsx("div", { children: activities.map((a, i) => /* @__PURE__ */ jsx(ActivityTimelineRow, { event: a, isLast: i === activities.length - 1 }, i)) }) })
93
- ]
65
+ address,
66
+ label: "Account",
67
+ trailing: /* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
68
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground", children: "Starknet" }),
69
+ /* @__PURE__ */ jsx(MoreMenu, { explorerHref: `${EXPLORER_URL}/contract/${address}`, shareTitle: shortenAddress(address), onReport: () => setReportOpen(true) })
70
+ ] })
94
71
  }
95
72
  ),
73
+ /* @__PURE__ */ jsx(PortfolioChipFilter, { options: tabs, value: activeTab, onChange: (key) => setActiveTab(key), showAll: false }),
74
+ activeTab === "assets" && (tokensLoading ? /* @__PURE__ */ jsx("div", { className: GRID, children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(TokenCardSkeleton, {}, i)) }) : tokens.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: ImageIcon, heading: "No assets yet", body: "Works this account owns appear here." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
75
+ /* @__PURE__ */ jsx("div", { className: GRID, children: tokens.map((t) => {
76
+ const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));
77
+ return /* @__PURE__ */ jsx(
78
+ TokenCard,
79
+ {
80
+ token: t,
81
+ isOwner,
82
+ usdValue: orderUsd(listing?.price, prices),
83
+ onList: isOwner ? actions.list : void 0,
84
+ onTransfer: isOwner ? actions.transfer : void 0,
85
+ onCancel: isOwner ? actions.cancel : void 0,
86
+ onBuy: !isOwner && listing ? () => market.buy(listing) : void 0,
87
+ onOffer: isOwner ? void 0 : market.offer,
88
+ onReport: setReportToken
89
+ },
90
+ `${t.contractAddress}-${t.tokenId}`
91
+ );
92
+ }) }),
93
+ /* @__PURE__ */ jsx(LoadMoreSentinel, { hasMore, isLoading: isLoadingMore, onLoadMore: loadMore })
94
+ ] })),
95
+ activeTab === "collections" && (collectionsLoading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: LayoutGrid, heading: "No collections yet", body: "Collections this account owns appear here." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ jsx(CollectionCard, { collection: c }, c.contractAddress)) })),
96
+ activeTab === "listings" && (ordersLoading ? /* @__PURE__ */ jsx("div", { className: GRID, children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(ListingCardSkeleton, {}, i)) }) : listings.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: ShoppingBag, heading: "No active listings", body: "Works this account lists for sale appear here." }) : /* @__PURE__ */ jsx("div", { className: GRID, children: listings.map((o) => /* @__PURE__ */ jsx(ListingCard, { order: o, onBuy: isOwner ? void 0 : market.buy, usdValue: orderUsd(o.price, prices), overflowMenu: /* @__PURE__ */ jsx(ListingCardMenu, { order: o }) }, o.orderHash)) })),
97
+ activeTab === "activity" ? /* @__PURE__ */ jsx("div", { className: "max-w-2xl", children: activitiesLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full rounded-xl" }, i)) }) : activities.length === 0 ? /* @__PURE__ */ jsx(TabEmptyState, { icon: Activity, heading: "No activity yet", body: "This account's activity appears here as it happens." }) : /* @__PURE__ */ jsx("div", { children: activities.map((a, i) => /* @__PURE__ */ jsx(ActivityTimelineRow, { event: a, isLast: i === activities.length - 1 }, i)) }) }) : null,
96
98
  actions.dialogs,
97
99
  market.dialogs,
98
100
  reportToken ? /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/account-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport useSWR from \"swr\";\nimport { Activity, Image as ImageIcon, LayoutGrid, ShoppingBag } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { TabsContent } from \"./tabs.js\";\nimport { UnderlineTabs, type UnderlineTab } from \"./underline-tabs.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { TabEmptyState } from \"./tab-empty-state.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { ActivityTimelineRow } from \"./activity-timeline-row.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport type { UsePortfolioAssetActions } from \"./portfolio-assets.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { shortenAddress } from \"../utils/address.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { orderUsd } from \"../utils/order-usd.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useUsdPrices } from \"../utils/use-usd-prices.js\";\nimport { useOwnedTokens } from \"../utils/use-tokens.js\";\nimport { useCollectionsByOwner } from \"../utils/use-collections.js\";\nimport { useUserOrders } from \"../utils/use-orders.js\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\n\ntype TabId = \"assets\" | \"collections\" | \"listings\" | \"activity\";\n\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\";\nconst isNft = (itemType: string) => itemType === \"ERC721\" || itemType === \"ERC1155\";\n\nexport interface AccountPageProps {\n address: string;\n useAssetActions: UsePortfolioAssetActions;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function AccountPage({ address, useAssetActions, useMarketActions }: AccountPageProps) {\n const { walletAddress } = useMedialaneApp();\n const client = useMedialaneClient();\n const prices = useUsdPrices();\n const [activeTab, setActiveTab] = useState<TabId>(\"assets\");\n const [reportOpen, setReportOpen] = useState(false);\n const [reportToken, setReportToken] = useState<ApiToken | null>(null);\n\n const isOwner = !!walletAddress && isSameAddress(address, walletAddress);\n\n const { tokens, total, isLoading: tokensLoading, isLoadingMore, hasMore, loadMore, mutate: refreshTokens } = useOwnedTokens(address);\n const { collections, isLoading: collectionsLoading } = useCollectionsByOwner(address);\n const { orders, isLoading: ordersLoading } = useUserOrders(address);\n const { activities, isLoading: activitiesLoading } = useActivitiesByAddress(activeTab === \"activity\" ? address : null);\n const { data: hidden } = useSWR(`creator-hidden-${address}`, () => client.api.creators.isHidden(address).catch(() => false));\n const actions = useAssetActions({ refresh: () => void refreshTokens() });\n const market = useMarketActions();\n\n const listings = orders.filter((o) => o.status === \"ACTIVE\" && isNft(o.offer.itemType));\n const tabs: UnderlineTab[] = [\n { id: \"assets\", label: \"Assets\", icon: ImageIcon, count: total ?? undefined },\n { id: \"collections\", label: \"Collections\", icon: LayoutGrid, count: collectionsLoading ? undefined : collections.length },\n { id: \"listings\", label: \"Listings\", icon: ShoppingBag, count: ordersLoading ? undefined : listings.length },\n { id: \"activity\", label: \"Activity\", icon: Activity },\n ];\n\n return (\n <div className=\"min-h-screen overflow-x-hidden pb-20\">\n {hidden === true ? <HiddenContentBanner /> : null}\n\n <div className=\"flex items-center justify-between gap-3 px-6 pt-20 pb-6\">\n <AddressDisplay address={address} chars={6} className=\"min-w-0 text-2xl font-black tracking-tight tabular-nums sm:text-3xl\" />\n <span className=\"shrink-0 rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground\">Starknet</span>\n </div>\n\n <UnderlineTabs\n tabs={tabs}\n value={activeTab}\n onValueChange={(id) => setActiveTab(id as TabId)}\n trailing={<MoreMenu explorerHref={`${EXPLORER_URL}/contract/${address}`} shareTitle={shortenAddress(address)} onReport={() => setReportOpen(true)} />}\n >\n <TabsContent value=\"assets\" className=\"mt-6 px-6\">\n {tokensLoading ? (\n <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <TokenCardSkeleton key={i} />)}</div>\n ) : tokens.length === 0 ? (\n <TabEmptyState icon={ImageIcon} heading=\"No assets yet\" body=\"Works this account owns appear here.\" />\n ) : (\n <>\n <div className={GRID}>\n {tokens.map((t: ApiToken) => {\n const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));\n return (\n <TokenCard\n key={`${t.contractAddress}-${t.tokenId}`}\n token={t}\n isOwner={isOwner}\n usdValue={orderUsd(listing?.price, prices)}\n onList={isOwner ? actions.list : undefined}\n onTransfer={isOwner ? actions.transfer : undefined}\n onCancel={isOwner ? actions.cancel : undefined}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n onReport={setReportToken}\n />\n );\n })}\n </div>\n <LoadMoreSentinel hasMore={hasMore} isLoading={isLoadingMore} onLoadMore={loadMore} />\n </>\n )}\n </TabsContent>\n\n <TabsContent value=\"collections\" className=\"mt-6 px-6\">\n {collectionsLoading ? (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{Array.from({ length: 6 }).map((_, i) => <CollectionCardSkeleton key={i} />)}</div>\n ) : collections.length === 0 ? (\n <TabEmptyState icon={LayoutGrid} heading=\"No collections yet\" body=\"Collections this account owns appear here.\" />\n ) : (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{collections.map((c) => <CollectionCard key={c.contractAddress} collection={c} />)}</div>\n )}\n </TabsContent>\n\n <TabsContent value=\"listings\" className=\"mt-6 px-6\">\n {ordersLoading ? (\n <div className={GRID}>{Array.from({ length: 4 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>\n ) : listings.length === 0 ? (\n <TabEmptyState icon={ShoppingBag} heading=\"No active listings\" body=\"Works this account lists for sale appear here.\" />\n ) : (\n <div className={GRID}>\n {listings.map((o) => <ListingCard key={o.orderHash} order={o} onBuy={isOwner ? undefined : market.buy} usdValue={orderUsd(o.price, prices)} overflowMenu={<ListingCardMenu order={o} />} />)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"activity\" className=\"mt-6 max-w-2xl px-6\">\n {activitiesLoading ? (\n <div className=\"space-y-5\">{Array.from({ length: 6 }).map((_, i) => <Skeleton key={i} className=\"h-10 w-full rounded-xl\" />)}</div>\n ) : activities.length === 0 ? (\n <TabEmptyState icon={Activity} heading=\"No activity yet\" body=\"This account's activity appears here as it happens.\" />\n ) : (\n <div>{activities.map((a, i) => <ActivityTimelineRow key={i} event={a} isLast={i === activities.length - 1} />)}</div>\n )}\n </TabsContent>\n </UnderlineTabs>\n\n {actions.dialogs}\n {market.dialogs}\n {reportToken ? (\n <ReportDialog\n open={!!reportToken}\n onOpenChange={(open) => { if (!open) setReportToken(null); }}\n target={{ type: \"TOKEN\", contract: reportToken.contractAddress, tokenId: reportToken.tokenId, name: reportToken.metadata?.name ?? undefined }}\n />\n ) : null}\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"CREATOR\", address, name: shortenAddress(address) }} />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/account-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport useSWR from \"swr\";\nimport { Activity, Image as ImageIcon, LayoutGrid, ShoppingBag } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { PortfolioHeader } from \"./portfolio-header.js\";\nimport { PortfolioChipFilter } from \"./portfolio-chip-filter.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { TabEmptyState } from \"./tab-empty-state.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { ActivityTimelineRow } from \"./activity-timeline-row.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport type { UsePortfolioAssetActions } from \"./portfolio-assets.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { shortenAddress } from \"../utils/address.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { orderUsd } from \"../utils/order-usd.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useUsdPrices } from \"../utils/use-usd-prices.js\";\nimport { useOwnedTokens } from \"../utils/use-tokens.js\";\nimport { useCollectionsByOwner } from \"../utils/use-collections.js\";\nimport { useUserOrders } from \"../utils/use-orders.js\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\n\ntype TabId = \"assets\" | \"collections\" | \"listings\" | \"activity\";\n\nconst TABS: { key: TabId; label: string }[] = [\n { key: \"assets\", label: \"Assets\" },\n { key: \"collections\", label: \"Collections\" },\n { key: \"listings\", label: \"Listings\" },\n { key: \"activity\", label: \"Activity\" },\n];\n\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\";\nconst isNft = (itemType: string) => itemType === \"ERC721\" || itemType === \"ERC1155\";\n\nexport interface AccountPageProps {\n address: string;\n useAssetActions: UsePortfolioAssetActions;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function AccountPage({ address, useAssetActions, useMarketActions }: AccountPageProps) {\n const { walletAddress } = useMedialaneApp();\n const client = useMedialaneClient();\n const prices = useUsdPrices();\n const [activeTab, setActiveTab] = useState<TabId>(\"assets\");\n const [reportOpen, setReportOpen] = useState(false);\n const [reportToken, setReportToken] = useState<ApiToken | null>(null);\n\n const isOwner = !!walletAddress && isSameAddress(address, walletAddress);\n\n const { tokens, total, isLoading: tokensLoading, isLoadingMore, hasMore, loadMore, mutate: refreshTokens } = useOwnedTokens(address);\n const { collections, isLoading: collectionsLoading } = useCollectionsByOwner(address);\n const { orders, isLoading: ordersLoading } = useUserOrders(address);\n const { activities, isLoading: activitiesLoading } = useActivitiesByAddress(activeTab === \"activity\" ? address : null);\n const { data: hidden } = useSWR(`creator-hidden-${address}`, () => client.api.creators.isHidden(address).catch(() => false));\n const actions = useAssetActions({ refresh: () => void refreshTokens() });\n const market = useMarketActions();\n\n const listings = orders.filter((o) => o.status === \"ACTIVE\" && isNft(o.offer.itemType));\n const counts: Partial<Record<TabId, number | undefined>> = {\n assets: total ?? undefined,\n collections: collectionsLoading ? undefined : collections.length,\n listings: ordersLoading ? undefined : listings.length,\n };\n const tabs = TABS.map(({ key, label }) => ({ key, label: counts[key] === undefined ? label : `${label} (${counts[key]})` }));\n\n return (\n <div className=\"space-y-6 px-4 pt-20 pb-8 sm:px-6 lg:px-8\">\n {hidden === true ? <HiddenContentBanner /> : null}\n\n <PortfolioHeader\n address={address}\n label=\"Account\"\n trailing={\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground\">Starknet</span>\n <MoreMenu explorerHref={`${EXPLORER_URL}/contract/${address}`} shareTitle={shortenAddress(address)} onReport={() => setReportOpen(true)} />\n </div>\n }\n />\n\n <PortfolioChipFilter options={tabs} value={activeTab} onChange={(key) => setActiveTab(key as TabId)} showAll={false} />\n\n {activeTab === \"assets\" && (\n tokensLoading ? (\n <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <TokenCardSkeleton key={i} />)}</div>\n ) : tokens.length === 0 ? (\n <TabEmptyState icon={ImageIcon} heading=\"No assets yet\" body=\"Works this account owns appear here.\" />\n ) : (\n <>\n <div className={GRID}>\n {tokens.map((t: ApiToken) => {\n const listing = t.activeOrders?.find((o) => isNft(o.offer.itemType));\n return (\n <TokenCard\n key={`${t.contractAddress}-${t.tokenId}`}\n token={t}\n isOwner={isOwner}\n usdValue={orderUsd(listing?.price, prices)}\n onList={isOwner ? actions.list : undefined}\n onTransfer={isOwner ? actions.transfer : undefined}\n onCancel={isOwner ? actions.cancel : undefined}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n onReport={setReportToken}\n />\n );\n })}\n </div>\n <LoadMoreSentinel hasMore={hasMore} isLoading={isLoadingMore} onLoadMore={loadMore} />\n </>\n )\n )}\n\n {activeTab === \"collections\" && (\n collectionsLoading ? (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{Array.from({ length: 6 }).map((_, i) => <CollectionCardSkeleton key={i} />)}</div>\n ) : collections.length === 0 ? (\n <TabEmptyState icon={LayoutGrid} heading=\"No collections yet\" body=\"Collections this account owns appear here.\" />\n ) : (\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-3\">{collections.map((c) => <CollectionCard key={c.contractAddress} collection={c} />)}</div>\n )\n )}\n\n {activeTab === \"listings\" && (\n ordersLoading ? (\n <div className={GRID}>{Array.from({ length: 4 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>\n ) : listings.length === 0 ? (\n <TabEmptyState icon={ShoppingBag} heading=\"No active listings\" body=\"Works this account lists for sale appear here.\" />\n ) : (\n <div className={GRID}>\n {listings.map((o) => <ListingCard key={o.orderHash} order={o} onBuy={isOwner ? undefined : market.buy} usdValue={orderUsd(o.price, prices)} overflowMenu={<ListingCardMenu order={o} />} />)}\n </div>\n )\n )}\n\n {activeTab === \"activity\" ? (\n <div className=\"max-w-2xl\">\n {activitiesLoading ? (\n <div className=\"space-y-5\">{Array.from({ length: 6 }).map((_, i) => <Skeleton key={i} className=\"h-10 w-full rounded-xl\" />)}</div>\n ) : activities.length === 0 ? (\n <TabEmptyState icon={Activity} heading=\"No activity yet\" body=\"This account's activity appears here as it happens.\" />\n ) : (\n <div>{activities.map((a, i) => <ActivityTimelineRow key={i} event={a} isLast={i === activities.length - 1} />)}</div>\n )}\n </div>\n ) : null}\n\n {actions.dialogs}\n {market.dialogs}\n {reportToken ? (\n <ReportDialog\n open={!!reportToken}\n onOpenChange={(open) => { if (!open) setReportToken(null); }}\n target={{ type: \"TOKEN\", contract: reportToken.contractAddress, tokenId: reportToken.tokenId, name: reportToken.metadata?.name ?? undefined }}\n />\n ) : null}\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"CREATOR\", address, name: shortenAddress(address) }} />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -39,6 +39,8 @@ var import_address_display = require("./address-display.js");
39
39
  function PortfolioHeader({
40
40
  address,
41
41
  score,
42
+ label = "Portfolio",
43
+ trailing,
42
44
  className
43
45
  }) {
44
46
  const scoreChip = score && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white", children: [
@@ -50,7 +52,7 @@ function PortfolioHeader({
50
52
  ] });
51
53
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_cn.cn)("flex items-center justify-between gap-4", className), children: [
52
54
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
53
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children: "Portfolio" }),
55
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children: label }),
54
56
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
55
57
  import_address_display.AddressDisplay,
56
58
  {
@@ -60,6 +62,7 @@ function PortfolioHeader({
60
62
  }
61
63
  )
62
64
  ] }),
65
+ trailing,
63
66
  score?.href ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: score.href, className: "shrink-0 active:opacity-80", children: scoreChip }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "shrink-0", children: scoreChip })
64
67
  ] });
65
68
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n Portfolio\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BM;AAzBN,kBAAiB;AACjB,gBAAmB;AACnB,6BAA+B;AAgBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,6CAAC,SAAI,WAAU,uIACb;AAAA,gDAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,6CAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,6CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GACrE;AAAA,iDAAC,SAAI,WAAU,WACb;AAAA,kDAAC,OAAE,WAAU,6CAA4C,uBAEzD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC,OAAO,OACN,4CAAC,YAAAA,SAAA,EAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,4CAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":["Link"]}
1
+ {"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n label?: string;\n trailing?: ReactNode;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n label = \"Portfolio\",\n trailing,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {trailing}\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCM;AA7BN,kBAAiB;AACjB,gBAAmB;AACnB,6BAA+B;AAkBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,6CAAC,SAAI,WAAU,uIACb;AAAA,gDAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,6CAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,6CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GACrE;AAAA,iDAAC,SAAI,WAAU,WACb;AAAA,kDAAC,OAAE,WAAU,6CACV,iBACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC;AAAA,IACA,OAAO,OACN,4CAAC,YAAAA,SAAA,EAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,4CAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":["Link"]}
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  interface PortfolioHeaderScore {
4
5
  levelName: string;
@@ -8,8 +9,10 @@ interface PortfolioHeaderScore {
8
9
  interface PortfolioHeaderProps {
9
10
  address: string;
10
11
  score?: PortfolioHeaderScore | null;
12
+ label?: string;
13
+ trailing?: ReactNode;
11
14
  className?: string;
12
15
  }
13
- declare function PortfolioHeader({ address, score, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
16
+ declare function PortfolioHeader({ address, score, label, trailing, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
14
17
 
15
18
  export { PortfolioHeader, type PortfolioHeaderProps, type PortfolioHeaderScore };
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  interface PortfolioHeaderScore {
4
5
  levelName: string;
@@ -8,8 +9,10 @@ interface PortfolioHeaderScore {
8
9
  interface PortfolioHeaderProps {
9
10
  address: string;
10
11
  score?: PortfolioHeaderScore | null;
12
+ label?: string;
13
+ trailing?: ReactNode;
11
14
  className?: string;
12
15
  }
13
- declare function PortfolioHeader({ address, score, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
16
+ declare function PortfolioHeader({ address, score, label, trailing, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
14
17
 
15
18
  export { PortfolioHeader, type PortfolioHeaderProps, type PortfolioHeaderScore };
@@ -6,6 +6,8 @@ import { AddressDisplay } from "./address-display.js";
6
6
  function PortfolioHeader({
7
7
  address,
8
8
  score,
9
+ label = "Portfolio",
10
+ trailing,
9
11
  className
10
12
  }) {
11
13
  const scoreChip = score && /* @__PURE__ */ jsxs("div", { className: "rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white", children: [
@@ -17,7 +19,7 @@ function PortfolioHeader({
17
19
  ] });
18
20
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4", className), children: [
19
21
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
20
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Portfolio" }),
22
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: label }),
21
23
  /* @__PURE__ */ jsx(
22
24
  AddressDisplay,
23
25
  {
@@ -27,6 +29,7 @@ function PortfolioHeader({
27
29
  }
28
30
  )
29
31
  ] }),
32
+ trailing,
30
33
  score?.href ? /* @__PURE__ */ jsx(Link, { href: score.href, className: "shrink-0 active:opacity-80", children: scoreChip }) : /* @__PURE__ */ jsx("div", { className: "shrink-0", children: scoreChip })
31
34
  ] });
32
35
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n Portfolio\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";AA2BM,cAGA,YAHA;AAzBN,OAAO,UAAU;AACjB,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAgBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,qBAAC,SAAI,WAAU,uIACb;AAAA,wBAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,qBAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,qBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GACrE;AAAA,yBAAC,SAAI,WAAU,WACb;AAAA,0BAAC,OAAE,WAAU,6CAA4C,uBAEzD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC,OAAO,OACN,oBAAC,QAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,oBAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n label?: string;\n trailing?: ReactNode;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n label = \"Portfolio\",\n trailing,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {trailing}\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";AAgCM,cAGA,YAHA;AA7BN,OAAO,UAAU;AACjB,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAkBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,qBAAC,SAAI,WAAU,uIACb;AAAA,wBAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,qBAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,qBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GACrE;AAAA,yBAAC,SAAI,WAAU,WACb;AAAA,0BAAC,OAAE,WAAU,6CACV,iBACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC;AAAA,IACA,OAAO,OACN,oBAAC,QAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,oBAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":[]}
@@ -134,7 +134,7 @@ function TokenCard({
134
134
  },
135
135
  children: [
136
136
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Tag, { className: "h-3.5 w-3.5 mr-1.5" }),
137
- "List"
137
+ "Sell"
138
138
  ]
139
139
  }
140
140
  ) : null : listingOrder && onBuy ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -181,7 +181,7 @@ function TokenCard({
181
181
  onClick: () => onList(token),
182
182
  children: [
183
183
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Tag, { className: "h-3.5 w-3.5" }),
184
- "List"
184
+ "Sell"
185
185
  ]
186
186
  }
187
187
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","buildCollectionHref","Link"]}
1
+ {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n Sell\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n Sell\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","buildCollectionHref","Link"]}
@@ -119,7 +119,7 @@ function TokenCard({
119
119
  },
120
120
  children: [
121
121
  /* @__PURE__ */ jsx(Tag, { className: "h-3.5 w-3.5 mr-1.5" }),
122
- "List"
122
+ "Sell"
123
123
  ]
124
124
  }
125
125
  ) : null : listingOrder && onBuy ? /* @__PURE__ */ jsx("div", { className: "btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8", children: /* @__PURE__ */ jsxs(
@@ -166,7 +166,7 @@ function TokenCard({
166
166
  onClick: () => onList(token),
167
167
  children: [
168
168
  /* @__PURE__ */ jsx(Tag, { className: "h-3.5 w-3.5" }),
169
- "List"
169
+ "Sell"
170
170
  ]
171
171
  }
172
172
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n Sell\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n Sell\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.312.0",
3
+ "version": "0.313.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "sideEffects": false,