@medialane/ui 0.288.0 → 0.289.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.
- package/dist/components/asset-page.cjs +3 -2
- package/dist/components/asset-page.cjs.map +1 -1
- package/dist/components/asset-page.js +3 -2
- package/dist/components/asset-page.js.map +1 -1
- package/dist/components/badge.d.cts +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.cts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/collection-page.cjs +3 -2
- package/dist/components/collection-page.cjs.map +1 -1
- package/dist/components/collection-page.js +3 -2
- package/dist/components/collection-page.js.map +1 -1
- package/dist/components/comments-section.cjs +3 -3
- package/dist/components/comments-section.cjs.map +1 -1
- package/dist/components/comments-section.js +2 -2
- package/dist/components/comments-section.js.map +1 -1
- package/dist/components/creator-page.cjs +6 -5
- package/dist/components/creator-page.cjs.map +1 -1
- package/dist/components/creator-page.js +5 -4
- package/dist/components/creator-page.js.map +1 -1
- package/dist/components/launchpad/edition-mint-schema.d.cts +2 -2
- package/dist/components/launchpad/edition-mint-schema.d.ts +2 -2
- package/dist/components/listing-card-menu.cjs +10 -13
- package/dist/components/listing-card-menu.cjs.map +1 -1
- package/dist/components/listing-card-menu.js +10 -13
- package/dist/components/listing-card-menu.js.map +1 -1
- package/dist/components/medialane-app.cjs.map +1 -1
- package/dist/components/medialane-app.d.cts +2 -8
- package/dist/components/medialane-app.d.ts +2 -8
- package/dist/components/medialane-app.js.map +1 -1
- package/dist/components/portfolio-assets.cjs +4 -5
- package/dist/components/portfolio-assets.cjs.map +1 -1
- package/dist/components/portfolio-assets.js +3 -4
- package/dist/components/portfolio-assets.js.map +1 -1
- package/dist/components/report-dialog.cjs +191 -0
- package/dist/components/report-dialog.cjs.map +1 -0
- package/dist/components/report-dialog.d.cts +17 -0
- package/dist/components/report-dialog.d.ts +17 -0
- package/dist/components/report-dialog.js +167 -0
- package/dist/components/report-dialog.js.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/collection-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\nimport { Inbox, Lock, Sparkles, Ticket } from \"lucide-react\";\nimport { assetHref, getService, type ApiCollection, type ApiOrder, type ApiToken, type Chain, type CollectionTokensSort } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { AssetCard, AssetCardSkeleton } from \"./asset-card.js\";\nimport { CollectionHeader } from \"./collection-header.js\";\nimport { CollectionFiltersDialog, CollectionActiveFilters } from \"./collection-filters-dialog.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { CollectionActivityTab } from \"./collection-activity-tab.js\";\nimport { CollectionTraitsTab } from \"./collection-traits-tab.js\";\nimport { ClubOwnerActions } from \"./club-owner-actions.js\";\nimport { OwnerSetupPanel } from \"./owner-setup-panel.js\";\nimport { TransferCollectionOwnership } from \"./transfer-collection-ownership.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { MakeOfferPicker } from \"./make-offer-picker.js\";\nimport { OrderSortControl, sortOrders, type OrderSort } from \"./order-sort-control.js\";\nimport { GatedContentHero, GatedContentPanel, type GatedContentState } from \"./gated-content.js\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { useCollection, useCollectionTokens } from \"../utils/use-collections.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollectionProfile } from \"../utils/use-profiles.js\";\nimport { useUsdPrices, usdPriceFor } from \"../utils/use-usd-prices.js\";\nimport { formatUsd } from \"../utils/format.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\n\nexport interface CollectionMarketActions {\n buy: (order: ApiOrder) => void;\n offer: (token: ApiToken) => void;\n dialogs: ReactNode;\n}\n\nexport interface CollectionPageSlots {\n useMarketActions: () => CollectionMarketActions;\n useGatedContent: (contract: string | undefined) => GatedContentState;\n ServiceAction: ComponentType<{ service: string | null | undefined; contractAddress: string }>;\n}\n\nconst PAGE_SIZE = 24;\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 sm:gap-4\";\n\nconst MINT_HREFS: Partial<Record<string, (contract: string) => string>> = {\n \"mip-erc721\": () => \"/launchpad/single-editions\",\n \"mip-erc1155\": (c) => `/launchpad/nfteditions/${c}/mint`,\n \"ip-tickets\": (c) => `/launchpad/tickets/${c}/mint`,\n \"ip-club\": (c) => `/launchpad/club/${c}/mint`,\n};\n\n/** Ownership transfer exists from the audited v3 collection registry (2026-05-14) on. */\nexport function canTransferCollectionOwnership(collection: ApiCollection): boolean {\n return !!collection.collectionId && collection.standard === \"ERC721\" && collection.createdAt >= \"2026-05-14\";\n}\n\nfunction usdFor(order: ApiOrder | undefined, prices: ReturnType<typeof useUsdPrices>): string | null {\n const currency = order?.price?.currency?.toUpperCase();\n const amount = order?.price?.formatted ? parseFloat(order.price.formatted) : NaN;\n const rate = currency ? usdPriceFor(prices, currency) : undefined;\n return rate == null || isNaN(amount) ? null : formatUsd(amount * rate);\n}\n\nfunction EmptyState({ title, body, action }: { title: string; body: string; action?: { href: string; label: string } }) {\n return (\n <div className=\"py-20 flex flex-col items-center gap-3 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/40\" />\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n <p className=\"text-xs text-muted-foreground/70 max-w-xs\">{body}</p>\n {action ? (\n <Link href={action.href} className=\"mt-2 inline-flex items-center gap-1.5 h-9 px-4 rounded-lg text-sm font-semibold bg-foreground text-background hover:opacity-90 transition\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {action.label}\n </Link>\n ) : null}\n </div>\n );\n}\n\nfunction useCollectionItems(contract: string) {\n const [page, setPage] = useState(1);\n const [sort, setSort] = useState<CollectionTokensSort>(\"recent\");\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});\n const { tokens, meta, isLoading } = useCollectionTokens(contract, page, PAGE_SIZE, sort);\n\n const handleSortChange = (next: CollectionTokensSort) => {\n setSort(next);\n setPage(1);\n setAllTokens([]);\n };\n\n useEffect(() => {\n if (tokens.length === 0) return;\n setAllTokens((prev) => {\n const ids = new Set(prev.map((t) => `${t.contractAddress}-${t.tokenId}`));\n return page === 1 ? tokens : [...prev, ...tokens.filter((t) => !ids.has(`${t.contractAddress}-${t.tokenId}`))];\n });\n }, [tokens, page]);\n\n return { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters };\n}\n\ntype CollectionItemsState = ReturnType<typeof useCollectionItems>;\n\nfunction CollectionItems({ items, activeListings, mintHref }: { items: CollectionItemsState; activeListings: ApiOrder[]; mintHref: string | null }) {\n const { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters } = items;\n const usdPrices = useUsdPrices();\n\n const listingByTokenId = useMemo(() => new Map(activeListings.filter((o) => o.nftTokenId).map((o) => [o.nftTokenId!, o])), [activeListings]);\n\n const visibleTokens = useMemo(() => {\n const filters = Object.entries(selectedFilters);\n return allTokens.filter((token) => {\n if (filters.length === 0) return true;\n const attrs = Array.isArray(token.metadata?.attributes) ? (token.metadata.attributes as { trait_type?: string; value?: string }[]) : [];\n return filters.every(([traitType, values]) => attrs.some((a) => a.trait_type === traitType && values.includes(String(a.value))));\n });\n }, [allTokens, selectedFilters]);\n\n if (isLoading && allTokens.length === 0) {\n return <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <AssetCardSkeleton key={i} />)}</div>;\n }\n\n if (allTokens.length === 0) {\n return mintHref ? (\n <EmptyState title=\"Ready for your first work\" body=\"Mint into this collection and it appears here.\" action={{ href: mintHref, label: \"Mint your first work\" }} />\n ) : (\n <EmptyState title=\"No items yet\" body=\"Tokens in this collection will appear here once indexed.\" />\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <CollectionActiveFilters tokens={allTokens} selected={selectedFilters} onChange={setSelectedFilters} sort={sort} onSortChange={handleSortChange} />\n {visibleTokens.length === 0 ? (\n <EmptyState title=\"No items match these filters\" body=\"Try removing some filters to see more results.\" />\n ) : (\n <div className={GRID}>\n {visibleTokens.map((t) => {\n const listing =\n t.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") ?? listingByTokenId.get(t.tokenId);\n return (\n <AssetCard\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(t.chain as Chain, t.contractAddress, t.tokenId)}\n name={t.metadata?.name || `Token #${t.tokenId}`}\n image={t.metadata?.image}\n animationUrl={t.metadata?.animationUrl}\n live={isLivingRenderCollection(t.chain as Chain, t.contractAddress)}\n ipType={t.metadata?.ipType}\n price={listing ? { ...listing.price, usdValue: usdFor(listing, usdPrices) } : null}\n fallbackId={t.tokenId}\n indexing={t.metadataStatus === \"PENDING\" || t.metadataStatus === \"FETCHING\"}\n />\n );\n })}\n </div>\n )}\n <LoadMoreSentinel hasMore={meta ? allTokens.length < (meta.total ?? 0) : false} isLoading={isLoading} onLoadMore={() => setPage((p) => p + 1)} />\n </div>\n );\n}\n\nfunction OwnerActions({ collection }: { collection: ApiCollection }) {\n const service = getService(collection.service)?.id;\n const contract = collection.contractAddress;\n if (service === \"ip-tickets\") {\n return (\n <Link href={`/launchpad/tickets/${contract}/mint`} className=\"flex w-fit items-center gap-1.5 h-8 px-3 rounded-lg text-xs font-semibold text-white bg-brand-blue hover:brightness-110 active:scale-[0.98] transition\">\n <Ticket className=\"h-3.5 w-3.5\" />\n Mint tickets\n </Link>\n );\n }\n if (service === \"ip-club\") return <ClubOwnerActions contractAddress={contract} isOwner />;\n return null;\n}\n\nexport interface CollectionPageProps {\n contract: string;\n slots: CollectionPageSlots;\n}\n\nexport function CollectionPage({ contract, slots }: CollectionPageProps) {\n const { walletAddress, ReportDialog } = useMedialaneApp();\n const { collection, isLoading: collectionLoading } = useCollection(contract);\n const { profile } = useCollectionProfile(contract);\n const gatedState = slots.useGatedContent(profile?.hasGatedContent ? contract : undefined);\n const market = slots.useMarketActions();\n const { orders, isLoading: ordersLoading } = useOrders({ collection: contract, status: \"ACTIVE\", sort: \"recent\", limit: 100 });\n\n const [reportOpen, setReportOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"assets\");\n const [marketTab, setMarketTab] = useState<\"listings\" | \"offers\">(\"listings\");\n const [provenanceTab, setProvenanceTab] = useState<\"activity\" | \"traits\">(\"activity\");\n const [listingsSort, setListingsSort] = useState<OrderSort>(\"recent\");\n const [offersSort, setOffersSort] = useState<OrderSort>(\"recent\");\n const usdPrices = useUsdPrices();\n const items = useCollectionItems(contract);\n\n const activeListings = orders.filter((o) => o.status === \"ACTIVE\" && (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"));\n const activeBids = orders.filter((o) => o.status === \"ACTIVE\" && o.offer.itemType === \"ERC20\");\n const isOwner = !!collection?.owner && !!walletAddress && isSameAddress(collection.owner, walletAddress);\n const mintHref = isOwner && collection ? MINT_HREFS[getService(collection.service)?.id ?? \"\"]?.(contract) ?? null : null;\n const { ServiceAction } = slots;\n\n const listingGrid = (items: ApiOrder[], sort: OrderSort, buyable: boolean) => (\n <div className={GRID}>\n {sortOrders(items, sort).map((o) => (\n <ListingCard\n key={o.orderHash}\n order={o}\n onBuy={buyable && !isSameAddress(o.offerer, walletAddress) ? market.buy : undefined}\n usdValue={usdFor(o, usdPrices)}\n overflowMenu={<ListingCardMenu order={o} />}\n />\n ))}\n </div>\n );\n const loadingGrid = <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>;\n\n return (\n <div className=\"relative z-0 min-h-screen\">\n {(collection as { isHidden?: boolean } | null | undefined)?.isHidden ? <HiddenContentBanner /> : null}\n\n <div className=\"px-4 sm:px-6 pt-20 pb-6 space-y-4\">\n <CollectionHeader collection={collection} loading={collectionLoading}>\n {collection && isOwner ? <OwnerActions collection={collection} /> : null}\n {collection ? <ServiceAction service={collection.service} contractAddress={collection.contractAddress} /> : null}\n </CollectionHeader>\n {collection && profile ? <GatedContentHero profile={profile} state={gatedState} onViewExclusive={() => setActiveTab(\"exclusive\")} /> : null}\n </div>\n\n <div className=\"px-4 sm:px-6 pb-12\">\n <Tabs value={activeTab} onValueChange={setActiveTab}>\n <div className=\"sticky top-0 z-10 flex items-center gap-2 pt-3 pb-1\">\n <TabsList className=\"min-w-0 flex-1 justify-start overflow-x-auto sm:flex-none\">\n <TabsTrigger value=\"assets\" className=\"flex-1 sm:flex-none\">\n Assets{collection?.totalSupply ? ` (${collection.totalSupply.toLocaleString()})` : \"\"}\n </TabsTrigger>\n <TabsTrigger value=\"market\" className=\"flex-1 sm:flex-none\">\n Market{!ordersLoading && activeListings.length + activeBids.length > 0 && ` (${activeListings.length + activeBids.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"provenance\" className=\"flex-1 sm:flex-none\">Provenance</TabsTrigger>\n {profile?.hasGatedContent ? (\n <TabsTrigger value=\"exclusive\" className=\"flex-1 sm:flex-none gap-1.5\">\n <Lock className=\"h-3.5 w-3.5\" />\n Exclusive\n </TabsTrigger>\n ) : null}\n </TabsList>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {activeTab === \"assets\" && items.allTokens.length > 0 ? (\n <CollectionFiltersDialog tokens={items.allTokens} selected={items.selectedFilters} onChange={items.setSelectedFilters} sort={items.sort} onSortChange={items.handleSortChange} chips={false} />\n ) : null}\n {collection ? (\n <MoreMenu\n explorerHref={`${EXPLORER_URL}/contract/${collection.contractAddress}`}\n shareTitle={collection.name ?? \"Collection\"}\n onReport={() => setReportOpen(true)}\n />\n ) : null}\n </div>\n </div>\n\n <TabsContent value=\"assets\" className=\"mt-4\">\n {collection ? <CollectionItems items={items} activeListings={activeListings} mintHref={mintHref} /> : null}\n </TabsContent>\n\n <TabsContent value=\"market\" className=\"mt-4\">\n <Tabs value={marketTab} onValueChange={(v) => setMarketTab(v as \"listings\" | \"offers\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"listings\" className=\"text-xs px-3 py-1\">\n Listings{!ordersLoading && activeListings.length > 0 && ` (${activeListings.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"offers\" className=\"text-xs px-3 py-1\">\n Offers{!ordersLoading && activeBids.length > 0 && ` (${activeBids.length})`}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"listings\" className=\"mt-4\">\n {ordersLoading ? loadingGrid : activeListings.length === 0 ? (\n <EmptyState title=\"No active listings\" body=\"When items in this collection are listed for sale, they'll appear here.\" />\n ) : (\n <div className=\"space-y-3\">\n <div className=\"flex justify-end\">\n <OrderSortControl value={listingsSort} onChange={setListingsSort} />\n </div>\n {listingGrid(activeListings, listingsSort, true)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"offers\" className=\"mt-4\">\n <div className=\"flex justify-end items-center gap-2 mb-3\">\n {!ordersLoading && activeBids.length > 0 ? <OrderSortControl value={offersSort} onChange={setOffersSort} /> : null}\n <MakeOfferPicker contract={contract} onPick={market.offer} />\n </div>\n {ordersLoading ? loadingGrid : activeBids.length === 0 ? (\n <EmptyState title=\"No active offers\" body=\"Make the first offer, or check back when collectors start bidding.\" />\n ) : (\n listingGrid(activeBids, offersSort, false)\n )}\n </TabsContent>\n </Tabs>\n </TabsContent>\n\n <TabsContent value=\"provenance\" className=\"mt-4\">\n {collection?.standard && (collection.totalSupply ?? 0) > 1 ? (\n <Tabs value={provenanceTab} onValueChange={(v) => setProvenanceTab(v as \"activity\" | \"traits\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"activity\" className=\"text-xs px-3 py-1\">Activity</TabsTrigger>\n <TabsTrigger value=\"traits\" className=\"text-xs px-3 py-1\">Traits</TabsTrigger>\n </TabsList>\n <TabsContent value=\"activity\" className=\"mt-4\">\n <CollectionActivityTab contract={contract} />\n </TabsContent>\n <TabsContent value=\"traits\" className=\"mt-4\">\n <CollectionTraitsTab contract={contract} />\n </TabsContent>\n </Tabs>\n ) : (\n <CollectionActivityTab contract={contract} />\n )}\n </TabsContent>\n\n {profile?.hasGatedContent ? (\n <TabsContent value=\"exclusive\" className=\"mt-4\">\n <GatedContentPanel state={gatedState} onBrowseListings={() => { setMarketTab(\"listings\"); setActiveTab(\"market\"); }} />\n </TabsContent>\n ) : null}\n </Tabs>\n </div>\n\n {collection && isOwner ? (\n <div className=\"px-4 sm:px-6 pb-8 space-y-4\">\n <OwnerSetupPanel contract={contract} profile={profile ?? null} />\n {canTransferCollectionOwnership(collection) ? (\n <div className=\"flex justify-end\">\n <TransferCollectionOwnership collectionId={collection.collectionId!} currentOwner={collection.owner!} collectionName={collection.name} />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {collection && ReportDialog ? (\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"COLLECTION\", contract: collection.contractAddress, name: collection.name ?? undefined }} />\n ) : null}\n {market.dialogs}\n </div>\n );\n}\n"],"mappings":"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","names":["Link","items"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/collection-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\nimport { Inbox, Lock, Sparkles, Ticket } from \"lucide-react\";\nimport { assetHref, getService, type ApiCollection, type ApiOrder, type ApiToken, type Chain, type CollectionTokensSort } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { AssetCard, AssetCardSkeleton } from \"./asset-card.js\";\nimport { CollectionHeader } from \"./collection-header.js\";\nimport { CollectionFiltersDialog, CollectionActiveFilters } from \"./collection-filters-dialog.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { CollectionActivityTab } from \"./collection-activity-tab.js\";\nimport { CollectionTraitsTab } from \"./collection-traits-tab.js\";\nimport { ClubOwnerActions } from \"./club-owner-actions.js\";\nimport { OwnerSetupPanel } from \"./owner-setup-panel.js\";\nimport { TransferCollectionOwnership } from \"./transfer-collection-ownership.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { MakeOfferPicker } from \"./make-offer-picker.js\";\nimport { OrderSortControl, sortOrders, type OrderSort } from \"./order-sort-control.js\";\nimport { GatedContentHero, GatedContentPanel, type GatedContentState } from \"./gated-content.js\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport { useCollection, useCollectionTokens } from \"../utils/use-collections.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollectionProfile } from \"../utils/use-profiles.js\";\nimport { useUsdPrices, usdPriceFor } from \"../utils/use-usd-prices.js\";\nimport { formatUsd } from \"../utils/format.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\n\nexport interface CollectionMarketActions {\n buy: (order: ApiOrder) => void;\n offer: (token: ApiToken) => void;\n dialogs: ReactNode;\n}\n\nexport interface CollectionPageSlots {\n useMarketActions: () => CollectionMarketActions;\n useGatedContent: (contract: string | undefined) => GatedContentState;\n ServiceAction: ComponentType<{ service: string | null | undefined; contractAddress: string }>;\n}\n\nconst PAGE_SIZE = 24;\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 sm:gap-4\";\n\nconst MINT_HREFS: Partial<Record<string, (contract: string) => string>> = {\n \"mip-erc721\": () => \"/launchpad/single-editions\",\n \"mip-erc1155\": (c) => `/launchpad/nfteditions/${c}/mint`,\n \"ip-tickets\": (c) => `/launchpad/tickets/${c}/mint`,\n \"ip-club\": (c) => `/launchpad/club/${c}/mint`,\n};\n\n/** Ownership transfer exists from the audited v3 collection registry (2026-05-14) on. */\nexport function canTransferCollectionOwnership(collection: ApiCollection): boolean {\n return !!collection.collectionId && collection.standard === \"ERC721\" && collection.createdAt >= \"2026-05-14\";\n}\n\nfunction usdFor(order: ApiOrder | undefined, prices: ReturnType<typeof useUsdPrices>): string | null {\n const currency = order?.price?.currency?.toUpperCase();\n const amount = order?.price?.formatted ? parseFloat(order.price.formatted) : NaN;\n const rate = currency ? usdPriceFor(prices, currency) : undefined;\n return rate == null || isNaN(amount) ? null : formatUsd(amount * rate);\n}\n\nfunction EmptyState({ title, body, action }: { title: string; body: string; action?: { href: string; label: string } }) {\n return (\n <div className=\"py-20 flex flex-col items-center gap-3 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/40\" />\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n <p className=\"text-xs text-muted-foreground/70 max-w-xs\">{body}</p>\n {action ? (\n <Link href={action.href} className=\"mt-2 inline-flex items-center gap-1.5 h-9 px-4 rounded-lg text-sm font-semibold bg-foreground text-background hover:opacity-90 transition\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {action.label}\n </Link>\n ) : null}\n </div>\n );\n}\n\nfunction useCollectionItems(contract: string) {\n const [page, setPage] = useState(1);\n const [sort, setSort] = useState<CollectionTokensSort>(\"recent\");\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});\n const { tokens, meta, isLoading } = useCollectionTokens(contract, page, PAGE_SIZE, sort);\n\n const handleSortChange = (next: CollectionTokensSort) => {\n setSort(next);\n setPage(1);\n setAllTokens([]);\n };\n\n useEffect(() => {\n if (tokens.length === 0) return;\n setAllTokens((prev) => {\n const ids = new Set(prev.map((t) => `${t.contractAddress}-${t.tokenId}`));\n return page === 1 ? tokens : [...prev, ...tokens.filter((t) => !ids.has(`${t.contractAddress}-${t.tokenId}`))];\n });\n }, [tokens, page]);\n\n return { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters };\n}\n\ntype CollectionItemsState = ReturnType<typeof useCollectionItems>;\n\nfunction CollectionItems({ items, activeListings, mintHref }: { items: CollectionItemsState; activeListings: ApiOrder[]; mintHref: string | null }) {\n const { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters } = items;\n const usdPrices = useUsdPrices();\n\n const listingByTokenId = useMemo(() => new Map(activeListings.filter((o) => o.nftTokenId).map((o) => [o.nftTokenId!, o])), [activeListings]);\n\n const visibleTokens = useMemo(() => {\n const filters = Object.entries(selectedFilters);\n return allTokens.filter((token) => {\n if (filters.length === 0) return true;\n const attrs = Array.isArray(token.metadata?.attributes) ? (token.metadata.attributes as { trait_type?: string; value?: string }[]) : [];\n return filters.every(([traitType, values]) => attrs.some((a) => a.trait_type === traitType && values.includes(String(a.value))));\n });\n }, [allTokens, selectedFilters]);\n\n if (isLoading && allTokens.length === 0) {\n return <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <AssetCardSkeleton key={i} />)}</div>;\n }\n\n if (allTokens.length === 0) {\n return mintHref ? (\n <EmptyState title=\"Ready for your first work\" body=\"Mint into this collection and it appears here.\" action={{ href: mintHref, label: \"Mint your first work\" }} />\n ) : (\n <EmptyState title=\"No items yet\" body=\"Tokens in this collection will appear here once indexed.\" />\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <CollectionActiveFilters tokens={allTokens} selected={selectedFilters} onChange={setSelectedFilters} sort={sort} onSortChange={handleSortChange} />\n {visibleTokens.length === 0 ? (\n <EmptyState title=\"No items match these filters\" body=\"Try removing some filters to see more results.\" />\n ) : (\n <div className={GRID}>\n {visibleTokens.map((t) => {\n const listing =\n t.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") ?? listingByTokenId.get(t.tokenId);\n return (\n <AssetCard\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(t.chain as Chain, t.contractAddress, t.tokenId)}\n name={t.metadata?.name || `Token #${t.tokenId}`}\n image={t.metadata?.image}\n animationUrl={t.metadata?.animationUrl}\n live={isLivingRenderCollection(t.chain as Chain, t.contractAddress)}\n ipType={t.metadata?.ipType}\n price={listing ? { ...listing.price, usdValue: usdFor(listing, usdPrices) } : null}\n fallbackId={t.tokenId}\n indexing={t.metadataStatus === \"PENDING\" || t.metadataStatus === \"FETCHING\"}\n />\n );\n })}\n </div>\n )}\n <LoadMoreSentinel hasMore={meta ? allTokens.length < (meta.total ?? 0) : false} isLoading={isLoading} onLoadMore={() => setPage((p) => p + 1)} />\n </div>\n );\n}\n\nfunction OwnerActions({ collection }: { collection: ApiCollection }) {\n const service = getService(collection.service)?.id;\n const contract = collection.contractAddress;\n if (service === \"ip-tickets\") {\n return (\n <Link href={`/launchpad/tickets/${contract}/mint`} className=\"flex w-fit items-center gap-1.5 h-8 px-3 rounded-lg text-xs font-semibold text-white bg-brand-blue hover:brightness-110 active:scale-[0.98] transition\">\n <Ticket className=\"h-3.5 w-3.5\" />\n Mint tickets\n </Link>\n );\n }\n if (service === \"ip-club\") return <ClubOwnerActions contractAddress={contract} isOwner />;\n return null;\n}\n\nexport interface CollectionPageProps {\n contract: string;\n slots: CollectionPageSlots;\n}\n\nexport function CollectionPage({ contract, slots }: CollectionPageProps) {\n const { walletAddress } = useMedialaneApp();\n const { collection, isLoading: collectionLoading } = useCollection(contract);\n const { profile } = useCollectionProfile(contract);\n const gatedState = slots.useGatedContent(profile?.hasGatedContent ? contract : undefined);\n const market = slots.useMarketActions();\n const { orders, isLoading: ordersLoading } = useOrders({ collection: contract, status: \"ACTIVE\", sort: \"recent\", limit: 100 });\n\n const [reportOpen, setReportOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"assets\");\n const [marketTab, setMarketTab] = useState<\"listings\" | \"offers\">(\"listings\");\n const [provenanceTab, setProvenanceTab] = useState<\"activity\" | \"traits\">(\"activity\");\n const [listingsSort, setListingsSort] = useState<OrderSort>(\"recent\");\n const [offersSort, setOffersSort] = useState<OrderSort>(\"recent\");\n const usdPrices = useUsdPrices();\n const items = useCollectionItems(contract);\n\n const activeListings = orders.filter((o) => o.status === \"ACTIVE\" && (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"));\n const activeBids = orders.filter((o) => o.status === \"ACTIVE\" && o.offer.itemType === \"ERC20\");\n const isOwner = !!collection?.owner && !!walletAddress && isSameAddress(collection.owner, walletAddress);\n const mintHref = isOwner && collection ? MINT_HREFS[getService(collection.service)?.id ?? \"\"]?.(contract) ?? null : null;\n const { ServiceAction } = slots;\n\n const listingGrid = (items: ApiOrder[], sort: OrderSort, buyable: boolean) => (\n <div className={GRID}>\n {sortOrders(items, sort).map((o) => (\n <ListingCard\n key={o.orderHash}\n order={o}\n onBuy={buyable && !isSameAddress(o.offerer, walletAddress) ? market.buy : undefined}\n usdValue={usdFor(o, usdPrices)}\n overflowMenu={<ListingCardMenu order={o} />}\n />\n ))}\n </div>\n );\n const loadingGrid = <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>;\n\n return (\n <div className=\"relative z-0 min-h-screen\">\n {(collection as { isHidden?: boolean } | null | undefined)?.isHidden ? <HiddenContentBanner /> : null}\n\n <div className=\"px-4 sm:px-6 pt-20 pb-6 space-y-4\">\n <CollectionHeader collection={collection} loading={collectionLoading}>\n {collection && isOwner ? <OwnerActions collection={collection} /> : null}\n {collection ? <ServiceAction service={collection.service} contractAddress={collection.contractAddress} /> : null}\n </CollectionHeader>\n {collection && profile ? <GatedContentHero profile={profile} state={gatedState} onViewExclusive={() => setActiveTab(\"exclusive\")} /> : null}\n </div>\n\n <div className=\"px-4 sm:px-6 pb-12\">\n <Tabs value={activeTab} onValueChange={setActiveTab}>\n <div className=\"sticky top-0 z-10 flex items-center gap-2 pt-3 pb-1\">\n <TabsList className=\"min-w-0 flex-1 justify-start overflow-x-auto sm:flex-none\">\n <TabsTrigger value=\"assets\" className=\"flex-1 sm:flex-none\">\n Assets{collection?.totalSupply ? ` (${collection.totalSupply.toLocaleString()})` : \"\"}\n </TabsTrigger>\n <TabsTrigger value=\"market\" className=\"flex-1 sm:flex-none\">\n Market{!ordersLoading && activeListings.length + activeBids.length > 0 && ` (${activeListings.length + activeBids.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"provenance\" className=\"flex-1 sm:flex-none\">Provenance</TabsTrigger>\n {profile?.hasGatedContent ? (\n <TabsTrigger value=\"exclusive\" className=\"flex-1 sm:flex-none gap-1.5\">\n <Lock className=\"h-3.5 w-3.5\" />\n Exclusive\n </TabsTrigger>\n ) : null}\n </TabsList>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {activeTab === \"assets\" && items.allTokens.length > 0 ? (\n <CollectionFiltersDialog tokens={items.allTokens} selected={items.selectedFilters} onChange={items.setSelectedFilters} sort={items.sort} onSortChange={items.handleSortChange} chips={false} />\n ) : null}\n {collection ? (\n <MoreMenu\n explorerHref={`${EXPLORER_URL}/contract/${collection.contractAddress}`}\n shareTitle={collection.name ?? \"Collection\"}\n onReport={() => setReportOpen(true)}\n />\n ) : null}\n </div>\n </div>\n\n <TabsContent value=\"assets\" className=\"mt-4\">\n {collection ? <CollectionItems items={items} activeListings={activeListings} mintHref={mintHref} /> : null}\n </TabsContent>\n\n <TabsContent value=\"market\" className=\"mt-4\">\n <Tabs value={marketTab} onValueChange={(v) => setMarketTab(v as \"listings\" | \"offers\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"listings\" className=\"text-xs px-3 py-1\">\n Listings{!ordersLoading && activeListings.length > 0 && ` (${activeListings.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"offers\" className=\"text-xs px-3 py-1\">\n Offers{!ordersLoading && activeBids.length > 0 && ` (${activeBids.length})`}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"listings\" className=\"mt-4\">\n {ordersLoading ? loadingGrid : activeListings.length === 0 ? (\n <EmptyState title=\"No active listings\" body=\"When items in this collection are listed for sale, they'll appear here.\" />\n ) : (\n <div className=\"space-y-3\">\n <div className=\"flex justify-end\">\n <OrderSortControl value={listingsSort} onChange={setListingsSort} />\n </div>\n {listingGrid(activeListings, listingsSort, true)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"offers\" className=\"mt-4\">\n <div className=\"flex justify-end items-center gap-2 mb-3\">\n {!ordersLoading && activeBids.length > 0 ? <OrderSortControl value={offersSort} onChange={setOffersSort} /> : null}\n <MakeOfferPicker contract={contract} onPick={market.offer} />\n </div>\n {ordersLoading ? loadingGrid : activeBids.length === 0 ? (\n <EmptyState title=\"No active offers\" body=\"Make the first offer, or check back when collectors start bidding.\" />\n ) : (\n listingGrid(activeBids, offersSort, false)\n )}\n </TabsContent>\n </Tabs>\n </TabsContent>\n\n <TabsContent value=\"provenance\" className=\"mt-4\">\n {collection?.standard && (collection.totalSupply ?? 0) > 1 ? (\n <Tabs value={provenanceTab} onValueChange={(v) => setProvenanceTab(v as \"activity\" | \"traits\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"activity\" className=\"text-xs px-3 py-1\">Activity</TabsTrigger>\n <TabsTrigger value=\"traits\" className=\"text-xs px-3 py-1\">Traits</TabsTrigger>\n </TabsList>\n <TabsContent value=\"activity\" className=\"mt-4\">\n <CollectionActivityTab contract={contract} />\n </TabsContent>\n <TabsContent value=\"traits\" className=\"mt-4\">\n <CollectionTraitsTab contract={contract} />\n </TabsContent>\n </Tabs>\n ) : (\n <CollectionActivityTab contract={contract} />\n )}\n </TabsContent>\n\n {profile?.hasGatedContent ? (\n <TabsContent value=\"exclusive\" className=\"mt-4\">\n <GatedContentPanel state={gatedState} onBrowseListings={() => { setMarketTab(\"listings\"); setActiveTab(\"market\"); }} />\n </TabsContent>\n ) : null}\n </Tabs>\n </div>\n\n {collection && isOwner ? (\n <div className=\"px-4 sm:px-6 pb-8 space-y-4\">\n <OwnerSetupPanel contract={contract} profile={profile ?? null} />\n {canTransferCollectionOwnership(collection) ? (\n <div className=\"flex justify-end\">\n <TransferCollectionOwnership collectionId={collection.collectionId!} currentOwner={collection.owner!} collectionName={collection.name} />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {collection ? (\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"COLLECTION\", contract: collection.contractAddress, name: collection.name ?? undefined }} />\n ) : null}\n {market.dialogs}\n </div>\n );\n}\n"],"mappings":"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","names":["Link","items"]}
|
|
@@ -23,6 +23,7 @@ import { OrderSortControl, sortOrders } from "./order-sort-control.js";
|
|
|
23
23
|
import { GatedContentHero, GatedContentPanel } from "./gated-content.js";
|
|
24
24
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
|
|
25
25
|
import { useMedialaneApp } from "./medialane-app.js";
|
|
26
|
+
import { ReportDialog } from "./report-dialog.js";
|
|
26
27
|
import { useCollection, useCollectionTokens } from "../utils/use-collections.js";
|
|
27
28
|
import { useOrders } from "../utils/use-orders.js";
|
|
28
29
|
import { useCollectionProfile } from "../utils/use-profiles.js";
|
|
@@ -132,7 +133,7 @@ function OwnerActions({ collection }) {
|
|
|
132
133
|
return null;
|
|
133
134
|
}
|
|
134
135
|
function CollectionPage({ contract, slots }) {
|
|
135
|
-
const { walletAddress
|
|
136
|
+
const { walletAddress } = useMedialaneApp();
|
|
136
137
|
const { collection, isLoading: collectionLoading } = useCollection(contract);
|
|
137
138
|
const { profile } = useCollectionProfile(contract);
|
|
138
139
|
const gatedState = slots.useGatedContent(profile?.hasGatedContent ? contract : void 0);
|
|
@@ -241,7 +242,7 @@ function CollectionPage({ contract, slots }) {
|
|
|
241
242
|
/* @__PURE__ */ jsx(OwnerSetupPanel, { contract, profile: profile ?? null }),
|
|
242
243
|
canTransferCollectionOwnership(collection) ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(TransferCollectionOwnership, { collectionId: collection.collectionId, currentOwner: collection.owner, collectionName: collection.name }) }) : null
|
|
243
244
|
] }) : null,
|
|
244
|
-
collection
|
|
245
|
+
collection ? /* @__PURE__ */ jsx(ReportDialog, { open: reportOpen, onOpenChange: setReportOpen, target: { type: "COLLECTION", contract: collection.contractAddress, name: collection.name ?? void 0 } }) : null,
|
|
245
246
|
market.dialogs
|
|
246
247
|
] });
|
|
247
248
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/collection-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\nimport { Inbox, Lock, Sparkles, Ticket } from \"lucide-react\";\nimport { assetHref, getService, type ApiCollection, type ApiOrder, type ApiToken, type Chain, type CollectionTokensSort } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { AssetCard, AssetCardSkeleton } from \"./asset-card.js\";\nimport { CollectionHeader } from \"./collection-header.js\";\nimport { CollectionFiltersDialog, CollectionActiveFilters } from \"./collection-filters-dialog.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { CollectionActivityTab } from \"./collection-activity-tab.js\";\nimport { CollectionTraitsTab } from \"./collection-traits-tab.js\";\nimport { ClubOwnerActions } from \"./club-owner-actions.js\";\nimport { OwnerSetupPanel } from \"./owner-setup-panel.js\";\nimport { TransferCollectionOwnership } from \"./transfer-collection-ownership.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { MakeOfferPicker } from \"./make-offer-picker.js\";\nimport { OrderSortControl, sortOrders, type OrderSort } from \"./order-sort-control.js\";\nimport { GatedContentHero, GatedContentPanel, type GatedContentState } from \"./gated-content.js\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { useCollection, useCollectionTokens } from \"../utils/use-collections.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollectionProfile } from \"../utils/use-profiles.js\";\nimport { useUsdPrices, usdPriceFor } from \"../utils/use-usd-prices.js\";\nimport { formatUsd } from \"../utils/format.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\n\nexport interface CollectionMarketActions {\n buy: (order: ApiOrder) => void;\n offer: (token: ApiToken) => void;\n dialogs: ReactNode;\n}\n\nexport interface CollectionPageSlots {\n useMarketActions: () => CollectionMarketActions;\n useGatedContent: (contract: string | undefined) => GatedContentState;\n ServiceAction: ComponentType<{ service: string | null | undefined; contractAddress: string }>;\n}\n\nconst PAGE_SIZE = 24;\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 sm:gap-4\";\n\nconst MINT_HREFS: Partial<Record<string, (contract: string) => string>> = {\n \"mip-erc721\": () => \"/launchpad/single-editions\",\n \"mip-erc1155\": (c) => `/launchpad/nfteditions/${c}/mint`,\n \"ip-tickets\": (c) => `/launchpad/tickets/${c}/mint`,\n \"ip-club\": (c) => `/launchpad/club/${c}/mint`,\n};\n\n/** Ownership transfer exists from the audited v3 collection registry (2026-05-14) on. */\nexport function canTransferCollectionOwnership(collection: ApiCollection): boolean {\n return !!collection.collectionId && collection.standard === \"ERC721\" && collection.createdAt >= \"2026-05-14\";\n}\n\nfunction usdFor(order: ApiOrder | undefined, prices: ReturnType<typeof useUsdPrices>): string | null {\n const currency = order?.price?.currency?.toUpperCase();\n const amount = order?.price?.formatted ? parseFloat(order.price.formatted) : NaN;\n const rate = currency ? usdPriceFor(prices, currency) : undefined;\n return rate == null || isNaN(amount) ? null : formatUsd(amount * rate);\n}\n\nfunction EmptyState({ title, body, action }: { title: string; body: string; action?: { href: string; label: string } }) {\n return (\n <div className=\"py-20 flex flex-col items-center gap-3 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/40\" />\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n <p className=\"text-xs text-muted-foreground/70 max-w-xs\">{body}</p>\n {action ? (\n <Link href={action.href} className=\"mt-2 inline-flex items-center gap-1.5 h-9 px-4 rounded-lg text-sm font-semibold bg-foreground text-background hover:opacity-90 transition\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {action.label}\n </Link>\n ) : null}\n </div>\n );\n}\n\nfunction useCollectionItems(contract: string) {\n const [page, setPage] = useState(1);\n const [sort, setSort] = useState<CollectionTokensSort>(\"recent\");\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});\n const { tokens, meta, isLoading } = useCollectionTokens(contract, page, PAGE_SIZE, sort);\n\n const handleSortChange = (next: CollectionTokensSort) => {\n setSort(next);\n setPage(1);\n setAllTokens([]);\n };\n\n useEffect(() => {\n if (tokens.length === 0) return;\n setAllTokens((prev) => {\n const ids = new Set(prev.map((t) => `${t.contractAddress}-${t.tokenId}`));\n return page === 1 ? tokens : [...prev, ...tokens.filter((t) => !ids.has(`${t.contractAddress}-${t.tokenId}`))];\n });\n }, [tokens, page]);\n\n return { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters };\n}\n\ntype CollectionItemsState = ReturnType<typeof useCollectionItems>;\n\nfunction CollectionItems({ items, activeListings, mintHref }: { items: CollectionItemsState; activeListings: ApiOrder[]; mintHref: string | null }) {\n const { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters } = items;\n const usdPrices = useUsdPrices();\n\n const listingByTokenId = useMemo(() => new Map(activeListings.filter((o) => o.nftTokenId).map((o) => [o.nftTokenId!, o])), [activeListings]);\n\n const visibleTokens = useMemo(() => {\n const filters = Object.entries(selectedFilters);\n return allTokens.filter((token) => {\n if (filters.length === 0) return true;\n const attrs = Array.isArray(token.metadata?.attributes) ? (token.metadata.attributes as { trait_type?: string; value?: string }[]) : [];\n return filters.every(([traitType, values]) => attrs.some((a) => a.trait_type === traitType && values.includes(String(a.value))));\n });\n }, [allTokens, selectedFilters]);\n\n if (isLoading && allTokens.length === 0) {\n return <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <AssetCardSkeleton key={i} />)}</div>;\n }\n\n if (allTokens.length === 0) {\n return mintHref ? (\n <EmptyState title=\"Ready for your first work\" body=\"Mint into this collection and it appears here.\" action={{ href: mintHref, label: \"Mint your first work\" }} />\n ) : (\n <EmptyState title=\"No items yet\" body=\"Tokens in this collection will appear here once indexed.\" />\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <CollectionActiveFilters tokens={allTokens} selected={selectedFilters} onChange={setSelectedFilters} sort={sort} onSortChange={handleSortChange} />\n {visibleTokens.length === 0 ? (\n <EmptyState title=\"No items match these filters\" body=\"Try removing some filters to see more results.\" />\n ) : (\n <div className={GRID}>\n {visibleTokens.map((t) => {\n const listing =\n t.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") ?? listingByTokenId.get(t.tokenId);\n return (\n <AssetCard\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(t.chain as Chain, t.contractAddress, t.tokenId)}\n name={t.metadata?.name || `Token #${t.tokenId}`}\n image={t.metadata?.image}\n animationUrl={t.metadata?.animationUrl}\n live={isLivingRenderCollection(t.chain as Chain, t.contractAddress)}\n ipType={t.metadata?.ipType}\n price={listing ? { ...listing.price, usdValue: usdFor(listing, usdPrices) } : null}\n fallbackId={t.tokenId}\n indexing={t.metadataStatus === \"PENDING\" || t.metadataStatus === \"FETCHING\"}\n />\n );\n })}\n </div>\n )}\n <LoadMoreSentinel hasMore={meta ? allTokens.length < (meta.total ?? 0) : false} isLoading={isLoading} onLoadMore={() => setPage((p) => p + 1)} />\n </div>\n );\n}\n\nfunction OwnerActions({ collection }: { collection: ApiCollection }) {\n const service = getService(collection.service)?.id;\n const contract = collection.contractAddress;\n if (service === \"ip-tickets\") {\n return (\n <Link href={`/launchpad/tickets/${contract}/mint`} className=\"flex w-fit items-center gap-1.5 h-8 px-3 rounded-lg text-xs font-semibold text-white bg-brand-blue hover:brightness-110 active:scale-[0.98] transition\">\n <Ticket className=\"h-3.5 w-3.5\" />\n Mint tickets\n </Link>\n );\n }\n if (service === \"ip-club\") return <ClubOwnerActions contractAddress={contract} isOwner />;\n return null;\n}\n\nexport interface CollectionPageProps {\n contract: string;\n slots: CollectionPageSlots;\n}\n\nexport function CollectionPage({ contract, slots }: CollectionPageProps) {\n const { walletAddress, ReportDialog } = useMedialaneApp();\n const { collection, isLoading: collectionLoading } = useCollection(contract);\n const { profile } = useCollectionProfile(contract);\n const gatedState = slots.useGatedContent(profile?.hasGatedContent ? contract : undefined);\n const market = slots.useMarketActions();\n const { orders, isLoading: ordersLoading } = useOrders({ collection: contract, status: \"ACTIVE\", sort: \"recent\", limit: 100 });\n\n const [reportOpen, setReportOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"assets\");\n const [marketTab, setMarketTab] = useState<\"listings\" | \"offers\">(\"listings\");\n const [provenanceTab, setProvenanceTab] = useState<\"activity\" | \"traits\">(\"activity\");\n const [listingsSort, setListingsSort] = useState<OrderSort>(\"recent\");\n const [offersSort, setOffersSort] = useState<OrderSort>(\"recent\");\n const usdPrices = useUsdPrices();\n const items = useCollectionItems(contract);\n\n const activeListings = orders.filter((o) => o.status === \"ACTIVE\" && (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"));\n const activeBids = orders.filter((o) => o.status === \"ACTIVE\" && o.offer.itemType === \"ERC20\");\n const isOwner = !!collection?.owner && !!walletAddress && isSameAddress(collection.owner, walletAddress);\n const mintHref = isOwner && collection ? MINT_HREFS[getService(collection.service)?.id ?? \"\"]?.(contract) ?? null : null;\n const { ServiceAction } = slots;\n\n const listingGrid = (items: ApiOrder[], sort: OrderSort, buyable: boolean) => (\n <div className={GRID}>\n {sortOrders(items, sort).map((o) => (\n <ListingCard\n key={o.orderHash}\n order={o}\n onBuy={buyable && !isSameAddress(o.offerer, walletAddress) ? market.buy : undefined}\n usdValue={usdFor(o, usdPrices)}\n overflowMenu={<ListingCardMenu order={o} />}\n />\n ))}\n </div>\n );\n const loadingGrid = <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>;\n\n return (\n <div className=\"relative z-0 min-h-screen\">\n {(collection as { isHidden?: boolean } | null | undefined)?.isHidden ? <HiddenContentBanner /> : null}\n\n <div className=\"px-4 sm:px-6 pt-20 pb-6 space-y-4\">\n <CollectionHeader collection={collection} loading={collectionLoading}>\n {collection && isOwner ? <OwnerActions collection={collection} /> : null}\n {collection ? <ServiceAction service={collection.service} contractAddress={collection.contractAddress} /> : null}\n </CollectionHeader>\n {collection && profile ? <GatedContentHero profile={profile} state={gatedState} onViewExclusive={() => setActiveTab(\"exclusive\")} /> : null}\n </div>\n\n <div className=\"px-4 sm:px-6 pb-12\">\n <Tabs value={activeTab} onValueChange={setActiveTab}>\n <div className=\"sticky top-0 z-10 flex items-center gap-2 pt-3 pb-1\">\n <TabsList className=\"min-w-0 flex-1 justify-start overflow-x-auto sm:flex-none\">\n <TabsTrigger value=\"assets\" className=\"flex-1 sm:flex-none\">\n Assets{collection?.totalSupply ? ` (${collection.totalSupply.toLocaleString()})` : \"\"}\n </TabsTrigger>\n <TabsTrigger value=\"market\" className=\"flex-1 sm:flex-none\">\n Market{!ordersLoading && activeListings.length + activeBids.length > 0 && ` (${activeListings.length + activeBids.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"provenance\" className=\"flex-1 sm:flex-none\">Provenance</TabsTrigger>\n {profile?.hasGatedContent ? (\n <TabsTrigger value=\"exclusive\" className=\"flex-1 sm:flex-none gap-1.5\">\n <Lock className=\"h-3.5 w-3.5\" />\n Exclusive\n </TabsTrigger>\n ) : null}\n </TabsList>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {activeTab === \"assets\" && items.allTokens.length > 0 ? (\n <CollectionFiltersDialog tokens={items.allTokens} selected={items.selectedFilters} onChange={items.setSelectedFilters} sort={items.sort} onSortChange={items.handleSortChange} chips={false} />\n ) : null}\n {collection ? (\n <MoreMenu\n explorerHref={`${EXPLORER_URL}/contract/${collection.contractAddress}`}\n shareTitle={collection.name ?? \"Collection\"}\n onReport={() => setReportOpen(true)}\n />\n ) : null}\n </div>\n </div>\n\n <TabsContent value=\"assets\" className=\"mt-4\">\n {collection ? <CollectionItems items={items} activeListings={activeListings} mintHref={mintHref} /> : null}\n </TabsContent>\n\n <TabsContent value=\"market\" className=\"mt-4\">\n <Tabs value={marketTab} onValueChange={(v) => setMarketTab(v as \"listings\" | \"offers\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"listings\" className=\"text-xs px-3 py-1\">\n Listings{!ordersLoading && activeListings.length > 0 && ` (${activeListings.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"offers\" className=\"text-xs px-3 py-1\">\n Offers{!ordersLoading && activeBids.length > 0 && ` (${activeBids.length})`}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"listings\" className=\"mt-4\">\n {ordersLoading ? loadingGrid : activeListings.length === 0 ? (\n <EmptyState title=\"No active listings\" body=\"When items in this collection are listed for sale, they'll appear here.\" />\n ) : (\n <div className=\"space-y-3\">\n <div className=\"flex justify-end\">\n <OrderSortControl value={listingsSort} onChange={setListingsSort} />\n </div>\n {listingGrid(activeListings, listingsSort, true)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"offers\" className=\"mt-4\">\n <div className=\"flex justify-end items-center gap-2 mb-3\">\n {!ordersLoading && activeBids.length > 0 ? <OrderSortControl value={offersSort} onChange={setOffersSort} /> : null}\n <MakeOfferPicker contract={contract} onPick={market.offer} />\n </div>\n {ordersLoading ? loadingGrid : activeBids.length === 0 ? (\n <EmptyState title=\"No active offers\" body=\"Make the first offer, or check back when collectors start bidding.\" />\n ) : (\n listingGrid(activeBids, offersSort, false)\n )}\n </TabsContent>\n </Tabs>\n </TabsContent>\n\n <TabsContent value=\"provenance\" className=\"mt-4\">\n {collection?.standard && (collection.totalSupply ?? 0) > 1 ? (\n <Tabs value={provenanceTab} onValueChange={(v) => setProvenanceTab(v as \"activity\" | \"traits\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"activity\" className=\"text-xs px-3 py-1\">Activity</TabsTrigger>\n <TabsTrigger value=\"traits\" className=\"text-xs px-3 py-1\">Traits</TabsTrigger>\n </TabsList>\n <TabsContent value=\"activity\" className=\"mt-4\">\n <CollectionActivityTab contract={contract} />\n </TabsContent>\n <TabsContent value=\"traits\" className=\"mt-4\">\n <CollectionTraitsTab contract={contract} />\n </TabsContent>\n </Tabs>\n ) : (\n <CollectionActivityTab contract={contract} />\n )}\n </TabsContent>\n\n {profile?.hasGatedContent ? (\n <TabsContent value=\"exclusive\" className=\"mt-4\">\n <GatedContentPanel state={gatedState} onBrowseListings={() => { setMarketTab(\"listings\"); setActiveTab(\"market\"); }} />\n </TabsContent>\n ) : null}\n </Tabs>\n </div>\n\n {collection && isOwner ? (\n <div className=\"px-4 sm:px-6 pb-8 space-y-4\">\n <OwnerSetupPanel contract={contract} profile={profile ?? null} />\n {canTransferCollectionOwnership(collection) ? (\n <div className=\"flex justify-end\">\n <TransferCollectionOwnership collectionId={collection.collectionId!} currentOwner={collection.owner!} collectionName={collection.name} />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {collection && ReportDialog ? (\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"COLLECTION\", contract: collection.contractAddress, name: collection.name ?? undefined }} />\n ) : null}\n {market.dialogs}\n </div>\n );\n}\n"],"mappings":"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","names":["items"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/collection-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\nimport { Inbox, Lock, Sparkles, Ticket } from \"lucide-react\";\nimport { assetHref, getService, type ApiCollection, type ApiOrder, type ApiToken, type Chain, type CollectionTokensSort } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { AssetCard, AssetCardSkeleton } from \"./asset-card.js\";\nimport { CollectionHeader } from \"./collection-header.js\";\nimport { CollectionFiltersDialog, CollectionActiveFilters } from \"./collection-filters-dialog.js\";\nimport { MoreMenu } from \"./more-menu.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { CollectionActivityTab } from \"./collection-activity-tab.js\";\nimport { CollectionTraitsTab } from \"./collection-traits-tab.js\";\nimport { ClubOwnerActions } from \"./club-owner-actions.js\";\nimport { OwnerSetupPanel } from \"./owner-setup-panel.js\";\nimport { TransferCollectionOwnership } from \"./transfer-collection-ownership.js\";\nimport { HiddenContentBanner } from \"./hidden-content-banner.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { MakeOfferPicker } from \"./make-offer-picker.js\";\nimport { OrderSortControl, sortOrders, type OrderSort } from \"./order-sort-control.js\";\nimport { GatedContentHero, GatedContentPanel, type GatedContentState } from \"./gated-content.js\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport { useCollection, useCollectionTokens } from \"../utils/use-collections.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollectionProfile } from \"../utils/use-profiles.js\";\nimport { useUsdPrices, usdPriceFor } from \"../utils/use-usd-prices.js\";\nimport { formatUsd } from \"../utils/format.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\n\nexport interface CollectionMarketActions {\n buy: (order: ApiOrder) => void;\n offer: (token: ApiToken) => void;\n dialogs: ReactNode;\n}\n\nexport interface CollectionPageSlots {\n useMarketActions: () => CollectionMarketActions;\n useGatedContent: (contract: string | undefined) => GatedContentState;\n ServiceAction: ComponentType<{ service: string | null | undefined; contractAddress: string }>;\n}\n\nconst PAGE_SIZE = 24;\nconst GRID = \"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 sm:gap-4\";\n\nconst MINT_HREFS: Partial<Record<string, (contract: string) => string>> = {\n \"mip-erc721\": () => \"/launchpad/single-editions\",\n \"mip-erc1155\": (c) => `/launchpad/nfteditions/${c}/mint`,\n \"ip-tickets\": (c) => `/launchpad/tickets/${c}/mint`,\n \"ip-club\": (c) => `/launchpad/club/${c}/mint`,\n};\n\n/** Ownership transfer exists from the audited v3 collection registry (2026-05-14) on. */\nexport function canTransferCollectionOwnership(collection: ApiCollection): boolean {\n return !!collection.collectionId && collection.standard === \"ERC721\" && collection.createdAt >= \"2026-05-14\";\n}\n\nfunction usdFor(order: ApiOrder | undefined, prices: ReturnType<typeof useUsdPrices>): string | null {\n const currency = order?.price?.currency?.toUpperCase();\n const amount = order?.price?.formatted ? parseFloat(order.price.formatted) : NaN;\n const rate = currency ? usdPriceFor(prices, currency) : undefined;\n return rate == null || isNaN(amount) ? null : formatUsd(amount * rate);\n}\n\nfunction EmptyState({ title, body, action }: { title: string; body: string; action?: { href: string; label: string } }) {\n return (\n <div className=\"py-20 flex flex-col items-center gap-3 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/40\" />\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n <p className=\"text-xs text-muted-foreground/70 max-w-xs\">{body}</p>\n {action ? (\n <Link href={action.href} className=\"mt-2 inline-flex items-center gap-1.5 h-9 px-4 rounded-lg text-sm font-semibold bg-foreground text-background hover:opacity-90 transition\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {action.label}\n </Link>\n ) : null}\n </div>\n );\n}\n\nfunction useCollectionItems(contract: string) {\n const [page, setPage] = useState(1);\n const [sort, setSort] = useState<CollectionTokensSort>(\"recent\");\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});\n const { tokens, meta, isLoading } = useCollectionTokens(contract, page, PAGE_SIZE, sort);\n\n const handleSortChange = (next: CollectionTokensSort) => {\n setSort(next);\n setPage(1);\n setAllTokens([]);\n };\n\n useEffect(() => {\n if (tokens.length === 0) return;\n setAllTokens((prev) => {\n const ids = new Set(prev.map((t) => `${t.contractAddress}-${t.tokenId}`));\n return page === 1 ? tokens : [...prev, ...tokens.filter((t) => !ids.has(`${t.contractAddress}-${t.tokenId}`))];\n });\n }, [tokens, page]);\n\n return { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters };\n}\n\ntype CollectionItemsState = ReturnType<typeof useCollectionItems>;\n\nfunction CollectionItems({ items, activeListings, mintHref }: { items: CollectionItemsState; activeListings: ApiOrder[]; mintHref: string | null }) {\n const { meta, isLoading, setPage, sort, handleSortChange, allTokens, selectedFilters, setSelectedFilters } = items;\n const usdPrices = useUsdPrices();\n\n const listingByTokenId = useMemo(() => new Map(activeListings.filter((o) => o.nftTokenId).map((o) => [o.nftTokenId!, o])), [activeListings]);\n\n const visibleTokens = useMemo(() => {\n const filters = Object.entries(selectedFilters);\n return allTokens.filter((token) => {\n if (filters.length === 0) return true;\n const attrs = Array.isArray(token.metadata?.attributes) ? (token.metadata.attributes as { trait_type?: string; value?: string }[]) : [];\n return filters.every(([traitType, values]) => attrs.some((a) => a.trait_type === traitType && values.includes(String(a.value))));\n });\n }, [allTokens, selectedFilters]);\n\n if (isLoading && allTokens.length === 0) {\n return <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <AssetCardSkeleton key={i} />)}</div>;\n }\n\n if (allTokens.length === 0) {\n return mintHref ? (\n <EmptyState title=\"Ready for your first work\" body=\"Mint into this collection and it appears here.\" action={{ href: mintHref, label: \"Mint your first work\" }} />\n ) : (\n <EmptyState title=\"No items yet\" body=\"Tokens in this collection will appear here once indexed.\" />\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <CollectionActiveFilters tokens={allTokens} selected={selectedFilters} onChange={setSelectedFilters} sort={sort} onSortChange={handleSortChange} />\n {visibleTokens.length === 0 ? (\n <EmptyState title=\"No items match these filters\" body=\"Try removing some filters to see more results.\" />\n ) : (\n <div className={GRID}>\n {visibleTokens.map((t) => {\n const listing =\n t.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") ?? listingByTokenId.get(t.tokenId);\n return (\n <AssetCard\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(t.chain as Chain, t.contractAddress, t.tokenId)}\n name={t.metadata?.name || `Token #${t.tokenId}`}\n image={t.metadata?.image}\n animationUrl={t.metadata?.animationUrl}\n live={isLivingRenderCollection(t.chain as Chain, t.contractAddress)}\n ipType={t.metadata?.ipType}\n price={listing ? { ...listing.price, usdValue: usdFor(listing, usdPrices) } : null}\n fallbackId={t.tokenId}\n indexing={t.metadataStatus === \"PENDING\" || t.metadataStatus === \"FETCHING\"}\n />\n );\n })}\n </div>\n )}\n <LoadMoreSentinel hasMore={meta ? allTokens.length < (meta.total ?? 0) : false} isLoading={isLoading} onLoadMore={() => setPage((p) => p + 1)} />\n </div>\n );\n}\n\nfunction OwnerActions({ collection }: { collection: ApiCollection }) {\n const service = getService(collection.service)?.id;\n const contract = collection.contractAddress;\n if (service === \"ip-tickets\") {\n return (\n <Link href={`/launchpad/tickets/${contract}/mint`} className=\"flex w-fit items-center gap-1.5 h-8 px-3 rounded-lg text-xs font-semibold text-white bg-brand-blue hover:brightness-110 active:scale-[0.98] transition\">\n <Ticket className=\"h-3.5 w-3.5\" />\n Mint tickets\n </Link>\n );\n }\n if (service === \"ip-club\") return <ClubOwnerActions contractAddress={contract} isOwner />;\n return null;\n}\n\nexport interface CollectionPageProps {\n contract: string;\n slots: CollectionPageSlots;\n}\n\nexport function CollectionPage({ contract, slots }: CollectionPageProps) {\n const { walletAddress } = useMedialaneApp();\n const { collection, isLoading: collectionLoading } = useCollection(contract);\n const { profile } = useCollectionProfile(contract);\n const gatedState = slots.useGatedContent(profile?.hasGatedContent ? contract : undefined);\n const market = slots.useMarketActions();\n const { orders, isLoading: ordersLoading } = useOrders({ collection: contract, status: \"ACTIVE\", sort: \"recent\", limit: 100 });\n\n const [reportOpen, setReportOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"assets\");\n const [marketTab, setMarketTab] = useState<\"listings\" | \"offers\">(\"listings\");\n const [provenanceTab, setProvenanceTab] = useState<\"activity\" | \"traits\">(\"activity\");\n const [listingsSort, setListingsSort] = useState<OrderSort>(\"recent\");\n const [offersSort, setOffersSort] = useState<OrderSort>(\"recent\");\n const usdPrices = useUsdPrices();\n const items = useCollectionItems(contract);\n\n const activeListings = orders.filter((o) => o.status === \"ACTIVE\" && (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"));\n const activeBids = orders.filter((o) => o.status === \"ACTIVE\" && o.offer.itemType === \"ERC20\");\n const isOwner = !!collection?.owner && !!walletAddress && isSameAddress(collection.owner, walletAddress);\n const mintHref = isOwner && collection ? MINT_HREFS[getService(collection.service)?.id ?? \"\"]?.(contract) ?? null : null;\n const { ServiceAction } = slots;\n\n const listingGrid = (items: ApiOrder[], sort: OrderSort, buyable: boolean) => (\n <div className={GRID}>\n {sortOrders(items, sort).map((o) => (\n <ListingCard\n key={o.orderHash}\n order={o}\n onBuy={buyable && !isSameAddress(o.offerer, walletAddress) ? market.buy : undefined}\n usdValue={usdFor(o, usdPrices)}\n overflowMenu={<ListingCardMenu order={o} />}\n />\n ))}\n </div>\n );\n const loadingGrid = <div className={GRID}>{Array.from({ length: 8 }).map((_, i) => <ListingCardSkeleton key={i} />)}</div>;\n\n return (\n <div className=\"relative z-0 min-h-screen\">\n {(collection as { isHidden?: boolean } | null | undefined)?.isHidden ? <HiddenContentBanner /> : null}\n\n <div className=\"px-4 sm:px-6 pt-20 pb-6 space-y-4\">\n <CollectionHeader collection={collection} loading={collectionLoading}>\n {collection && isOwner ? <OwnerActions collection={collection} /> : null}\n {collection ? <ServiceAction service={collection.service} contractAddress={collection.contractAddress} /> : null}\n </CollectionHeader>\n {collection && profile ? <GatedContentHero profile={profile} state={gatedState} onViewExclusive={() => setActiveTab(\"exclusive\")} /> : null}\n </div>\n\n <div className=\"px-4 sm:px-6 pb-12\">\n <Tabs value={activeTab} onValueChange={setActiveTab}>\n <div className=\"sticky top-0 z-10 flex items-center gap-2 pt-3 pb-1\">\n <TabsList className=\"min-w-0 flex-1 justify-start overflow-x-auto sm:flex-none\">\n <TabsTrigger value=\"assets\" className=\"flex-1 sm:flex-none\">\n Assets{collection?.totalSupply ? ` (${collection.totalSupply.toLocaleString()})` : \"\"}\n </TabsTrigger>\n <TabsTrigger value=\"market\" className=\"flex-1 sm:flex-none\">\n Market{!ordersLoading && activeListings.length + activeBids.length > 0 && ` (${activeListings.length + activeBids.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"provenance\" className=\"flex-1 sm:flex-none\">Provenance</TabsTrigger>\n {profile?.hasGatedContent ? (\n <TabsTrigger value=\"exclusive\" className=\"flex-1 sm:flex-none gap-1.5\">\n <Lock className=\"h-3.5 w-3.5\" />\n Exclusive\n </TabsTrigger>\n ) : null}\n </TabsList>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {activeTab === \"assets\" && items.allTokens.length > 0 ? (\n <CollectionFiltersDialog tokens={items.allTokens} selected={items.selectedFilters} onChange={items.setSelectedFilters} sort={items.sort} onSortChange={items.handleSortChange} chips={false} />\n ) : null}\n {collection ? (\n <MoreMenu\n explorerHref={`${EXPLORER_URL}/contract/${collection.contractAddress}`}\n shareTitle={collection.name ?? \"Collection\"}\n onReport={() => setReportOpen(true)}\n />\n ) : null}\n </div>\n </div>\n\n <TabsContent value=\"assets\" className=\"mt-4\">\n {collection ? <CollectionItems items={items} activeListings={activeListings} mintHref={mintHref} /> : null}\n </TabsContent>\n\n <TabsContent value=\"market\" className=\"mt-4\">\n <Tabs value={marketTab} onValueChange={(v) => setMarketTab(v as \"listings\" | \"offers\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"listings\" className=\"text-xs px-3 py-1\">\n Listings{!ordersLoading && activeListings.length > 0 && ` (${activeListings.length})`}\n </TabsTrigger>\n <TabsTrigger value=\"offers\" className=\"text-xs px-3 py-1\">\n Offers{!ordersLoading && activeBids.length > 0 && ` (${activeBids.length})`}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"listings\" className=\"mt-4\">\n {ordersLoading ? loadingGrid : activeListings.length === 0 ? (\n <EmptyState title=\"No active listings\" body=\"When items in this collection are listed for sale, they'll appear here.\" />\n ) : (\n <div className=\"space-y-3\">\n <div className=\"flex justify-end\">\n <OrderSortControl value={listingsSort} onChange={setListingsSort} />\n </div>\n {listingGrid(activeListings, listingsSort, true)}\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"offers\" className=\"mt-4\">\n <div className=\"flex justify-end items-center gap-2 mb-3\">\n {!ordersLoading && activeBids.length > 0 ? <OrderSortControl value={offersSort} onChange={setOffersSort} /> : null}\n <MakeOfferPicker contract={contract} onPick={market.offer} />\n </div>\n {ordersLoading ? loadingGrid : activeBids.length === 0 ? (\n <EmptyState title=\"No active offers\" body=\"Make the first offer, or check back when collectors start bidding.\" />\n ) : (\n listingGrid(activeBids, offersSort, false)\n )}\n </TabsContent>\n </Tabs>\n </TabsContent>\n\n <TabsContent value=\"provenance\" className=\"mt-4\">\n {collection?.standard && (collection.totalSupply ?? 0) > 1 ? (\n <Tabs value={provenanceTab} onValueChange={(v) => setProvenanceTab(v as \"activity\" | \"traits\")}>\n <TabsList className=\"h-9\">\n <TabsTrigger value=\"activity\" className=\"text-xs px-3 py-1\">Activity</TabsTrigger>\n <TabsTrigger value=\"traits\" className=\"text-xs px-3 py-1\">Traits</TabsTrigger>\n </TabsList>\n <TabsContent value=\"activity\" className=\"mt-4\">\n <CollectionActivityTab contract={contract} />\n </TabsContent>\n <TabsContent value=\"traits\" className=\"mt-4\">\n <CollectionTraitsTab contract={contract} />\n </TabsContent>\n </Tabs>\n ) : (\n <CollectionActivityTab contract={contract} />\n )}\n </TabsContent>\n\n {profile?.hasGatedContent ? (\n <TabsContent value=\"exclusive\" className=\"mt-4\">\n <GatedContentPanel state={gatedState} onBrowseListings={() => { setMarketTab(\"listings\"); setActiveTab(\"market\"); }} />\n </TabsContent>\n ) : null}\n </Tabs>\n </div>\n\n {collection && isOwner ? (\n <div className=\"px-4 sm:px-6 pb-8 space-y-4\">\n <OwnerSetupPanel contract={contract} profile={profile ?? null} />\n {canTransferCollectionOwnership(collection) ? (\n <div className=\"flex justify-end\">\n <TransferCollectionOwnership collectionId={collection.collectionId!} currentOwner={collection.owner!} collectionName={collection.name} />\n </div>\n ) : null}\n </div>\n ) : null}\n\n {collection ? (\n <ReportDialog open={reportOpen} onOpenChange={setReportOpen} target={{ type: \"COLLECTION\", contract: collection.contractAddress, name: collection.name ?? undefined }} />\n ) : null}\n {market.dialogs}\n </div>\n );\n}\n"],"mappings":"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","names":["items"]}
|
|
@@ -40,6 +40,7 @@ var import_address_display = require("./address-display.js");
|
|
|
40
40
|
var import_button = require("./button.js");
|
|
41
41
|
var import_link = __toESM(require("./link.js"), 1);
|
|
42
42
|
var import_medialane_app = require("./medialane-app.js");
|
|
43
|
+
var import_report_dialog = require("./report-dialog.js");
|
|
43
44
|
var import_reward_earned = require("./rewards/reward-earned.js");
|
|
44
45
|
var import_skeleton = require("./skeleton.js");
|
|
45
46
|
var import_textarea = require("./textarea.js");
|
|
@@ -57,7 +58,6 @@ function CommentsSection({ contract, tokenId, className }) {
|
|
|
57
58
|
const app = (0, import_medialane_app.useMedialaneApp)();
|
|
58
59
|
const walletAddress = app.walletAddress;
|
|
59
60
|
const hasWallet = !!walletAddress;
|
|
60
|
-
const { ReportDialog } = app;
|
|
61
61
|
const { comments, isLoading, mutate } = (0, import_use_comments.useComments)(contract, tokenId);
|
|
62
62
|
const tx = (0, import_use_transaction.useTransaction)();
|
|
63
63
|
const [text, setText] = (0, import_react.useState)("");
|
|
@@ -276,8 +276,8 @@ function CommentsSection({ contract, tokenId, className }) {
|
|
|
276
276
|
}
|
|
277
277
|
)
|
|
278
278
|
] }) }),
|
|
279
|
-
reportTarget &&
|
|
280
|
-
ReportDialog,
|
|
279
|
+
reportTarget && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
280
|
+
import_report_dialog.ReportDialog,
|
|
281
281
|
{
|
|
282
282
|
target: reportTarget,
|
|
283
283
|
open: !!reportTarget,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/comments-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { ExternalLink, Flag, Loader2, MessageCircle, Send, Zap } from \"lucide-react\";\nimport { normalizeAddress, profileHref, STARKNET_NFTCOMMENTS_CONTRACT } from \"@medialane/sdk\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { Button } from \"./button.js\";\nimport Link from \"./link.js\";\nimport { useMedialaneApp, type ReportTarget } from \"./medialane-app.js\";\nimport { RewardEarned } from \"./rewards/reward-earned.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Textarea } from \"./textarea.js\";\nimport { TransactionDialog } from \"./transaction-dialog.js\";\nimport { TransactionSuccess } from \"./transaction-success.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { runIntentCalls } from \"../utils/run-intent-calls.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { successMessages } from \"../utils/success-messages.js\";\nimport { useComments } from \"../utils/use-comments.js\";\nimport { useTransaction } from \"../utils/use-transaction.js\";\n\nconst MAX_LEN = 1000;\n\nexport interface CommentsSectionProps {\n contract: string;\n tokenId: string;\n className?: string;\n}\n\nexport function CommentsSection({ contract, tokenId, className }: CommentsSectionProps) {\n const app = useMedialaneApp();\n const walletAddress = app.walletAddress;\n const hasWallet = !!walletAddress;\n const { ReportDialog } = app;\n const { comments, isLoading, mutate } = useComments(contract, tokenId);\n const tx = useTransaction();\n\n const [text, setText] = useState(\"\");\n const [reportTarget, setReportTarget] = useState<ReportTarget | null>(null);\n\n const messagesRef = useRef<HTMLDivElement>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const composeRef = useRef<HTMLTextAreaElement>(null);\n\n const byteLen = new TextEncoder().encode(text).length;\n const canSubmit = text.trim().length > 0 && byteLen <= MAX_LEN && !!STARKNET_NFTCOMMENTS_CONTRACT;\n const isProcessing = tx.phase === \"preparing\" || tx.phase === \"submitting\" || tx.phase === \"confirming\";\n\n const isOwn = (author: string) =>\n !!walletAddress && normalizeAddress(\"STARKNET\", author) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const isNearBottom = () => {\n const el = messagesRef.current;\n if (!el) return true;\n return el.scrollHeight - el.scrollTop - el.clientHeight < 100;\n };\n\n useEffect(() => {\n if (isNearBottom()) {\n bottomRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }\n\n }, [comments.length]);\n\n const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && canSubmit && !isProcessing) {\n e.preventDefault();\n post();\n }\n };\n\n const post = () => {\n const body = text.trim();\n void tx\n .run(async (signer) => {\n await runIntentCalls(signer, () =>\n app.getClient().api.intents.create(\"comment\", { author: signer.address, nftContract: contract, tokenId, text: body }),\n );\n })\n .then((ok) => {\n if (!ok) return;\n setText(\"\");\n if (composeRef.current) composeRef.current.style.height = \"auto\";\n void mutate();\n });\n };\n\n const handleStartConversation = () => {\n if (hasWallet) composeRef.current?.focus();\n };\n\n return (\n <div className={cn(\"flex flex-col h-[480px] overflow-hidden\", className)}>\n\n <div ref={messagesRef} className=\"flex-1 overflow-y-auto px-4 py-4\">\n {isLoading ? (\n <div className=\"space-y-4\">\n {([\n { own: false, w: \"w-40\" },\n { own: true, w: \"w-56\" },\n { own: false, w: \"w-48\" },\n { own: true, w: \"w-32\" },\n ] as const).map((item, i) => (\n <div key={i} className={`flex ${item.own ? \"justify-end\" : \"justify-start\"} gap-2`}>\n {!item.own && <Skeleton className=\"h-8 w-8 rounded-full shrink-0\" />}\n <Skeleton\n className={`h-10 ${item.w} rounded-2xl ${item.own ? \"rounded-tr-sm\" : \"rounded-tl-sm\"}`}\n />\n </div>\n ))}\n </div>\n ) : comments.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full\" style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue) / 0.15), hsl(var(--brand-purple) / 0.15))\" }}>\n <MessageCircle className=\"h-7 w-7\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n </div>\n <div>\n <p className=\"text-sm font-semibold\">Nothing here yet</p>\n <p className=\"text-xs text-muted-foreground mt-1 leading-relaxed max-w-[220px]\">\n Your comment will be minted onchain — attached to this NFT forever.\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={handleStartConversation}\n className=\"rounded-full text-white px-5\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n Write the first comment\n </Button>\n </div>\n ) : (\n <div className=\"space-y-5\">\n {comments.map((comment) => {\n const own = isOwn(comment.author);\n const avatarSeed = parseInt(comment.author.slice(2, 8), 16) % 360;\n const avatarBg = own\n ? \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\"\n : `linear-gradient(135deg, hsl(${avatarSeed}, 65%, 45%), hsl(${(avatarSeed + 60) % 360}, 65%, 55%))`;\n\n return (\n <div\n key={comment.id}\n className={`group flex gap-2.5 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}\n >\n\n <Link href={profileHref({ address: comment.author })} className=\"shrink-0 mt-0.5\">\n <div\n className=\"h-9 w-9 rounded-full flex items-center justify-center text-2xs font-bold text-white select-none ring-2 ring-background hover:ring-primary/40 transition-all\"\n style={{ background: avatarBg }}\n >\n {comment.author.slice(2, 5).toUpperCase()}\n </div>\n </Link>\n\n <div className={`flex flex-col max-w-[75%] gap-1 ${own ? \"items-end\" : \"items-start\"}`}>\n\n <div className={`flex items-center gap-2 px-1 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}>\n <Link\n href={profileHref({ address: comment.author })}\n className=\"text-xs font-semibold text-foreground hover:underline underline-offset-2\"\n >\n {own ? \"You\" : <AddressDisplay address={comment.author} chars={5} showCopy={false} />}\n </Link>\n <span className=\"text-2xs text-muted-foreground\" title={comment.postedAt}>\n {timeAgo(comment.postedAt)}\n </span>\n </div>\n\n <div className=\"relative\">\n {own ? (\n <div\n className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words rounded-2xl rounded-tr-sm text-white\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {comment.content}\n </div>\n ) : (\n <div className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words bg-muted rounded-2xl rounded-tl-sm border border-border/50\">\n {comment.content}\n </div>\n )}\n {!own && hasWallet && (\n <button\n className=\"absolute -top-1 -right-5 opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground/40 hover:text-destructive/70\"\n title=\"Report comment\"\n aria-label=\"Report comment\"\n onClick={() => setReportTarget({ type: \"COMMENT\", commentId: comment.id })}\n >\n <Flag className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n\n {comment.txHash && (\n <a\n href={`${EXPLORER_URL}/tx/${comment.txHash}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 px-1 text-2xs font-medium transition-colors\"\n style={{ color: \"hsl(var(--brand-blue) / 0.55)\" }}\n onMouseEnter={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue))\")}\n onMouseLeave={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue) / 0.55)\")}\n >\n <span>⛓</span>\n <span>onchain proof</span>\n <ExternalLink className=\"h-2.5 w-2.5\" />\n </a>\n )}\n </div>\n </div>\n );\n })}\n <div ref={bottomRef} />\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border/60 shrink-0\">\n {!hasWallet ? (\n <div className=\"flex items-center justify-center px-4 h-16\">\n <button type=\"button\" onClick={app.onRequireWallet} className=\"text-sm text-primary hover:underline\">\n {app.connectLabel ?? \"Connect\"} to comment\n </button>\n </div>\n ) : (\n <div className=\"px-3 pt-2 pb-3 space-y-2\">\n\n <div className=\"flex items-center gap-1.5\">\n <Zap className=\"h-3 w-3\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n <span className=\"text-2xs font-semibold\" style={{ color: \"hsl(var(--brand-blue))\" }}>\n Mint your message onchain\n </span>\n </div>\n\n <div\n className=\"rounded-xl border bg-background/60 transition-all focus-within:ring-2\"\n style={{\n borderColor: \"hsl(var(--border))\",\n }}\n onFocusCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--brand-blue) / 0.6)\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"0 0 0 3px hsl(var(--brand-blue) / 0.12)\";\n }}\n onBlurCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--border))\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"\";\n }}\n >\n <Textarea\n ref={composeRef}\n placeholder=\"Say something onchain… it's permanent.\"\n value={text}\n onChange={handleTextInput}\n onKeyDown={handleKeyDown}\n rows={2}\n className=\"resize-none min-h-[52px] max-h-[120px] w-full border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 pt-2.5 pb-1 text-sm rounded-xl\"\n disabled={isProcessing}\n />\n <div className=\"flex items-center justify-between px-3 pb-2.5\">\n <span className=\"text-2xs text-muted-foreground/40 flex items-center gap-1\">\n Enter ↵ to post\n </span>\n <div className=\"flex items-center gap-2\">\n {byteLen > 800 && (\n <span className={`text-2xs ${byteLen > MAX_LEN ? \"text-destructive\" : \"text-muted-foreground\"}`}>\n {byteLen}/{MAX_LEN}\n </span>\n )}\n <button\n onClick={post}\n disabled={!canSubmit || isProcessing}\n className=\"flex items-center gap-1.5 h-7 px-3 text-xs font-semibold rounded-full text-white transition-all hover:opacity-90 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {isProcessing ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <Send className=\"h-3 w-3\" />}\n Post onchain\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {reportTarget && ReportDialog && (\n <ReportDialog\n target={reportTarget}\n open={!!reportTarget}\n onOpenChange={(open) => { if (!open) setReportTarget(null); }}\n />\n )}\n\n <TransactionDialog tx={tx} title=\"Comment\" workingLabel=\"Posting your comment…\" firstStepLabel=\"Prepare comment\">\n <TransactionSuccess\n name=\"Comment\"\n {...successMessages.commented()}\n txHash={tx.txHash}\n actions={[{ label: \"Done\", onClick: tx.reset }]}\n >\n <RewardEarned actionType=\"comment\" />\n </TransactionSuccess>\n </TransactionDialog>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8Gc;AA5Gd,mBAA4C;AAC5C,0BAAsE;AACtE,iBAA6E;AAC7E,6BAA+B;AAC/B,oBAAuB;AACvB,kBAAiB;AACjB,2BAAmD;AACnD,2BAA6B;AAC7B,sBAAyB;AACzB,sBAAyB;AACzB,gCAAkC;AAClC,iCAAmC;AACnC,gBAAmB;AACnB,0BAA6B;AAC7B,8BAA+B;AAC/B,kBAAwB;AACxB,8BAAgC;AAChC,0BAA4B;AAC5B,6BAA+B;AAE/B,MAAM,UAAU;AAQT,SAAS,gBAAgB,EAAE,UAAU,SAAS,UAAU,GAAyB;AACtF,QAAM,UAAM,sCAAgB;AAC5B,QAAM,gBAAgB,IAAI;AAC1B,QAAM,YAAY,CAAC,CAAC;AACpB,QAAM,EAAE,aAAa,IAAI;AACzB,QAAM,EAAE,UAAU,WAAW,OAAO,QAAI,iCAAY,UAAU,OAAO;AACrE,QAAM,SAAK,uCAAe;AAE1B,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,EAAE;AACnC,QAAM,CAAC,cAAc,eAAe,QAAI,uBAA8B,IAAI;AAE1E,QAAM,kBAAc,qBAAuB,IAAI;AAC/C,QAAM,gBAAY,qBAAuB,IAAI;AAC7C,QAAM,iBAAa,qBAA4B,IAAI;AAEnD,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE;AAC/C,QAAM,YAAY,KAAK,KAAK,EAAE,SAAS,KAAK,WAAW,WAAW,CAAC,CAAC;AACpE,QAAM,eAAe,GAAG,UAAU,eAAe,GAAG,UAAU,gBAAgB,GAAG,UAAU;AAE3F,QAAM,QAAQ,CAAC,WACb,CAAC,CAAC,qBAAiB,6BAAiB,YAAY,MAAM,UAAM,6BAAiB,YAAY,aAAa;AAExG,QAAM,eAAe,MAAM;AACzB,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI,QAAO;AAChB,WAAO,GAAG,eAAe,GAAG,YAAY,GAAG,eAAe;AAAA,EAC5D;AAEA,8BAAU,MAAM;AACd,QAAI,aAAa,GAAG;AAClB,gBAAU,SAAS,eAAe,EAAE,UAAU,SAAS,CAAC;AAAA,IAC1D;AAAA,EAEF,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,QAAM,kBAAkB,CAAC,MAA8C;AACrE,YAAQ,EAAE,OAAO,KAAK;AACtB,MAAE,OAAO,MAAM,SAAS;AACxB,MAAE,OAAO,MAAM,SAAS,KAAK,IAAI,EAAE,OAAO,cAAc,EAAE,IAAI;AAAA,EAChE;AAEA,QAAM,gBAAgB,CAAC,MAAgD;AACrE,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,YAAY,aAAa,CAAC,cAAc;AAClE,QAAE,eAAe;AACjB,WAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,OAAO,MAAM;AACjB,UAAM,OAAO,KAAK,KAAK;AACvB,SAAK,GACF,IAAI,OAAO,WAAW;AACrB,gBAAM;AAAA,QAAe;AAAA,QAAQ,MAC3B,IAAI,UAAU,EAAE,IAAI,QAAQ,OAAO,WAAW,EAAE,QAAQ,OAAO,SAAS,aAAa,UAAU,SAAS,MAAM,KAAK,CAAC;AAAA,MACtH;AAAA,IACF,CAAC,EACA,KAAK,CAAC,OAAO;AACZ,UAAI,CAAC,GAAI;AACT,cAAQ,EAAE;AACV,UAAI,WAAW,QAAS,YAAW,QAAQ,MAAM,SAAS;AAC1D,WAAK,OAAO;AAAA,IACd,CAAC;AAAA,EACL;AAEA,QAAM,0BAA0B,MAAM;AACpC,QAAI,UAAW,YAAW,SAAS,MAAM;AAAA,EAC3C;AAEA,SACE,6CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GAErE;AAAA,gDAAC,SAAI,KAAK,aAAa,WAAU,oCAC9B,sBACC,4CAAC,SAAI,WAAU,aACX;AAAA,MACA,EAAE,KAAK,OAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,MAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,OAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,MAAO,GAAG,OAAO;AAAA,IAC1B,EAAY,IAAI,CAAC,MAAM,MACrB,6CAAC,SAAY,WAAW,QAAQ,KAAK,MAAM,gBAAgB,eAAe,UACvE;AAAA,OAAC,KAAK,OAAO,4CAAC,4BAAS,WAAU,iCAAgC;AAAA,MAClE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,QAAQ,KAAK,CAAC,gBAAgB,KAAK,MAAM,kBAAkB,eAAe;AAAA;AAAA,MACvF;AAAA,SAJQ,CAKV,CACD,GACH,IACE,SAAS,WAAW,IACtB,6CAAC,SAAI,WAAU,2EACb;AAAA,kDAAC,SAAI,WAAU,2DAA0D,OAAO,EAAE,YAAY,0FAA0F,GACtL,sDAAC,qCAAc,WAAU,WAAU,OAAO,EAAE,OAAO,yBAAyB,GAAG,GACjF;AAAA,MACA,6CAAC,SACC;AAAA,oDAAC,OAAE,WAAU,yBAAwB,8BAAgB;AAAA,QACrD,4CAAC,OAAE,WAAU,oEAAmE,sFAEhF;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,UAClG;AAAA;AAAA,MAED;AAAA,OACF,IAEA,6CAAC,SAAI,WAAU,aACZ;AAAA,eAAS,IAAI,CAAC,YAAY;AACzB,cAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,cAAM,aAAa,SAAS,QAAQ,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;AAC9D,cAAM,WAAW,MACb,8EACA,+BAA+B,UAAU,qBAAqB,aAAa,MAAM,GAAG;AAExF,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW,sBAAsB,MAAM,qBAAqB,UAAU;AAAA,YAGtE;AAAA,0DAAC,YAAAA,SAAA,EAAK,UAAM,wBAAY,EAAE,SAAS,QAAQ,OAAO,CAAC,GAAG,WAAU,mBAC9D;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,EAAE,YAAY,SAAS;AAAA,kBAE7B,kBAAQ,OAAO,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA;AAAA,cAC1C,GACF;AAAA,cAEA,6CAAC,SAAI,WAAW,mCAAmC,MAAM,cAAc,aAAa,IAElF;AAAA,6DAAC,SAAI,WAAW,gCAAgC,MAAM,qBAAqB,UAAU,IACnF;AAAA;AAAA,oBAAC,YAAAA;AAAA,oBAAA;AAAA,sBACC,UAAM,wBAAY,EAAE,SAAS,QAAQ,OAAO,CAAC;AAAA,sBAC7C,WAAU;AAAA,sBAET,gBAAM,QAAQ,4CAAC,yCAAe,SAAS,QAAQ,QAAQ,OAAO,GAAG,UAAU,OAAO;AAAA;AAAA,kBACrF;AAAA,kBACA,4CAAC,UAAK,WAAU,kCAAiC,OAAO,QAAQ,UAC7D,mCAAQ,QAAQ,QAAQ,GAC3B;AAAA,mBACF;AAAA,gBAEA,6CAAC,SAAI,WAAU,YACZ;AAAA,wBACC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,sBAEhG,kBAAQ;AAAA;AAAA,kBACX,IAEA,4CAAC,SAAI,WAAU,gHACZ,kBAAQ,SACX;AAAA,kBAED,CAAC,OAAO,aACP;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,OAAM;AAAA,sBACN,cAAW;AAAA,sBACX,SAAS,MAAM,gBAAgB,EAAE,MAAM,WAAW,WAAW,QAAQ,GAAG,CAAC;AAAA,sBAEzE,sDAAC,4BAAK,WAAU,WAAU;AAAA;AAAA,kBAC5B;AAAA,mBAEJ;AAAA,gBAEC,QAAQ,UACP;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAM,GAAG,gCAAY,OAAO,QAAQ,MAAM;AAAA,oBAC1C,QAAO;AAAA,oBACP,KAAI;AAAA,oBACJ,WAAU;AAAA,oBACV,OAAO,EAAE,OAAO,gCAAgC;AAAA,oBAChD,cAAc,CAAC,MAAO,EAAE,cAAc,MAAM,QAAQ;AAAA,oBACpD,cAAc,CAAC,MAAO,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAEpD;AAAA,kEAAC,UAAK,oBAAC;AAAA,sBACP,4CAAC,UAAK,2BAAa;AAAA,sBACnB,4CAAC,oCAAa,WAAU,eAAc;AAAA;AAAA;AAAA,gBACxC;AAAA,iBAEJ;AAAA;AAAA;AAAA,UAnEK,QAAQ;AAAA,QAoEf;AAAA,MAEJ,CAAC;AAAA,MACD,4CAAC,SAAI,KAAK,WAAW;AAAA,OACvB,GAEJ;AAAA,IAEA,4CAAC,SAAI,WAAU,sCACZ,WAAC,YACA,4CAAC,SAAI,WAAU,8CACb,uDAAC,YAAO,MAAK,UAAS,SAAS,IAAI,iBAAiB,WAAU,wCAC3D;AAAA,UAAI,gBAAgB;AAAA,MAAU;AAAA,OACjC,GACF,IAEA,6CAAC,SAAI,WAAU,4BAEb;AAAA,mDAAC,SAAI,WAAU,6BACb;AAAA,oDAAC,2BAAI,WAAU,WAAU,OAAO,EAAE,OAAO,yBAAyB,GAAG;AAAA,QACrE,4CAAC,UAAK,WAAU,0BAAyB,OAAO,EAAE,OAAO,yBAAyB,GAAG,uCAErF;AAAA,SACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO;AAAA,YACL,aAAa;AAAA,UACf;AAAA,UACA,gBAAgB,CAAC,MAAM;AACrB,YAAC,EAAE,cAAiC,MAAM,cAAc;AACxD,YAAC,EAAE,cAAiC,MAAM,YAAY;AAAA,UACxD;AAAA,UACA,eAAe,CAAC,MAAM;AACpB,YAAC,EAAE,cAAiC,MAAM,cAAc;AACxD,YAAC,EAAE,cAAiC,MAAM,YAAY;AAAA,UACxD;AAAA,UAEA;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,OAAO;AAAA,gBACP,UAAU;AAAA,gBACV,WAAW;AAAA,gBACX,MAAM;AAAA,gBACN,WAAU;AAAA,gBACV,UAAU;AAAA;AAAA,YACZ;AAAA,YACA,6CAAC,SAAI,WAAU,iDACb;AAAA,0DAAC,UAAK,WAAU,6DAA4D,kCAE5E;AAAA,cACA,6CAAC,SAAI,WAAU,2BACZ;AAAA,0BAAU,OACT,6CAAC,UAAK,WAAW,YAAY,UAAU,UAAU,qBAAqB,uBAAuB,IAC1F;AAAA;AAAA,kBAAQ;AAAA,kBAAE;AAAA,mBACb;AAAA,gBAEF;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS;AAAA,oBACT,UAAU,CAAC,aAAa;AAAA,oBACxB,WAAU;AAAA,oBACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,oBAEhG;AAAA,qCAAe,4CAAC,+BAAQ,WAAU,wBAAuB,IAAK,4CAAC,4BAAK,WAAU,WAAU;AAAA,sBAAG;AAAA;AAAA;AAAA,gBAE9F;AAAA,iBACF;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA,OACF,GAEJ;AAAA,IAEC,gBAAgB,gBACf;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,MAAM,CAAC,CAAC;AAAA,QACR,cAAc,CAAC,SAAS;AAAE,cAAI,CAAC,KAAM,iBAAgB,IAAI;AAAA,QAAG;AAAA;AAAA,IAC9D;AAAA,IAGF,4CAAC,+CAAkB,IAAQ,OAAM,WAAU,cAAa,8BAAwB,gBAAe,mBAC7F;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACJ,GAAG,wCAAgB,UAAU;AAAA,QAC9B,QAAQ,GAAG;AAAA,QACX,SAAS,CAAC,EAAE,OAAO,QAAQ,SAAS,GAAG,MAAM,CAAC;AAAA,QAE9C,sDAAC,qCAAa,YAAW,WAAU;AAAA;AAAA,IACrC,GACF;AAAA,KACF;AAEJ;","names":["Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/comments-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { ExternalLink, Flag, Loader2, MessageCircle, Send, Zap } from \"lucide-react\";\nimport { normalizeAddress, profileHref, STARKNET_NFTCOMMENTS_CONTRACT } from \"@medialane/sdk\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { Button } from \"./button.js\";\nimport Link from \"./link.js\";\nimport { useMedialaneApp, type ReportTarget } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport { RewardEarned } from \"./rewards/reward-earned.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Textarea } from \"./textarea.js\";\nimport { TransactionDialog } from \"./transaction-dialog.js\";\nimport { TransactionSuccess } from \"./transaction-success.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { runIntentCalls } from \"../utils/run-intent-calls.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { successMessages } from \"../utils/success-messages.js\";\nimport { useComments } from \"../utils/use-comments.js\";\nimport { useTransaction } from \"../utils/use-transaction.js\";\n\nconst MAX_LEN = 1000;\n\nexport interface CommentsSectionProps {\n contract: string;\n tokenId: string;\n className?: string;\n}\n\nexport function CommentsSection({ contract, tokenId, className }: CommentsSectionProps) {\n const app = useMedialaneApp();\n const walletAddress = app.walletAddress;\n const hasWallet = !!walletAddress;\n const { comments, isLoading, mutate } = useComments(contract, tokenId);\n const tx = useTransaction();\n\n const [text, setText] = useState(\"\");\n const [reportTarget, setReportTarget] = useState<ReportTarget | null>(null);\n\n const messagesRef = useRef<HTMLDivElement>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const composeRef = useRef<HTMLTextAreaElement>(null);\n\n const byteLen = new TextEncoder().encode(text).length;\n const canSubmit = text.trim().length > 0 && byteLen <= MAX_LEN && !!STARKNET_NFTCOMMENTS_CONTRACT;\n const isProcessing = tx.phase === \"preparing\" || tx.phase === \"submitting\" || tx.phase === \"confirming\";\n\n const isOwn = (author: string) =>\n !!walletAddress && normalizeAddress(\"STARKNET\", author) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const isNearBottom = () => {\n const el = messagesRef.current;\n if (!el) return true;\n return el.scrollHeight - el.scrollTop - el.clientHeight < 100;\n };\n\n useEffect(() => {\n if (isNearBottom()) {\n bottomRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }\n\n }, [comments.length]);\n\n const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && canSubmit && !isProcessing) {\n e.preventDefault();\n post();\n }\n };\n\n const post = () => {\n const body = text.trim();\n void tx\n .run(async (signer) => {\n await runIntentCalls(signer, () =>\n app.getClient().api.intents.create(\"comment\", { author: signer.address, nftContract: contract, tokenId, text: body }),\n );\n })\n .then((ok) => {\n if (!ok) return;\n setText(\"\");\n if (composeRef.current) composeRef.current.style.height = \"auto\";\n void mutate();\n });\n };\n\n const handleStartConversation = () => {\n if (hasWallet) composeRef.current?.focus();\n };\n\n return (\n <div className={cn(\"flex flex-col h-[480px] overflow-hidden\", className)}>\n\n <div ref={messagesRef} className=\"flex-1 overflow-y-auto px-4 py-4\">\n {isLoading ? (\n <div className=\"space-y-4\">\n {([\n { own: false, w: \"w-40\" },\n { own: true, w: \"w-56\" },\n { own: false, w: \"w-48\" },\n { own: true, w: \"w-32\" },\n ] as const).map((item, i) => (\n <div key={i} className={`flex ${item.own ? \"justify-end\" : \"justify-start\"} gap-2`}>\n {!item.own && <Skeleton className=\"h-8 w-8 rounded-full shrink-0\" />}\n <Skeleton\n className={`h-10 ${item.w} rounded-2xl ${item.own ? \"rounded-tr-sm\" : \"rounded-tl-sm\"}`}\n />\n </div>\n ))}\n </div>\n ) : comments.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full\" style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue) / 0.15), hsl(var(--brand-purple) / 0.15))\" }}>\n <MessageCircle className=\"h-7 w-7\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n </div>\n <div>\n <p className=\"text-sm font-semibold\">Nothing here yet</p>\n <p className=\"text-xs text-muted-foreground mt-1 leading-relaxed max-w-[220px]\">\n Your comment will be minted onchain — attached to this NFT forever.\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={handleStartConversation}\n className=\"rounded-full text-white px-5\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n Write the first comment\n </Button>\n </div>\n ) : (\n <div className=\"space-y-5\">\n {comments.map((comment) => {\n const own = isOwn(comment.author);\n const avatarSeed = parseInt(comment.author.slice(2, 8), 16) % 360;\n const avatarBg = own\n ? \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\"\n : `linear-gradient(135deg, hsl(${avatarSeed}, 65%, 45%), hsl(${(avatarSeed + 60) % 360}, 65%, 55%))`;\n\n return (\n <div\n key={comment.id}\n className={`group flex gap-2.5 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}\n >\n\n <Link href={profileHref({ address: comment.author })} className=\"shrink-0 mt-0.5\">\n <div\n className=\"h-9 w-9 rounded-full flex items-center justify-center text-2xs font-bold text-white select-none ring-2 ring-background hover:ring-primary/40 transition-all\"\n style={{ background: avatarBg }}\n >\n {comment.author.slice(2, 5).toUpperCase()}\n </div>\n </Link>\n\n <div className={`flex flex-col max-w-[75%] gap-1 ${own ? \"items-end\" : \"items-start\"}`}>\n\n <div className={`flex items-center gap-2 px-1 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}>\n <Link\n href={profileHref({ address: comment.author })}\n className=\"text-xs font-semibold text-foreground hover:underline underline-offset-2\"\n >\n {own ? \"You\" : <AddressDisplay address={comment.author} chars={5} showCopy={false} />}\n </Link>\n <span className=\"text-2xs text-muted-foreground\" title={comment.postedAt}>\n {timeAgo(comment.postedAt)}\n </span>\n </div>\n\n <div className=\"relative\">\n {own ? (\n <div\n className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words rounded-2xl rounded-tr-sm text-white\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {comment.content}\n </div>\n ) : (\n <div className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words bg-muted rounded-2xl rounded-tl-sm border border-border/50\">\n {comment.content}\n </div>\n )}\n {!own && hasWallet && (\n <button\n className=\"absolute -top-1 -right-5 opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground/40 hover:text-destructive/70\"\n title=\"Report comment\"\n aria-label=\"Report comment\"\n onClick={() => setReportTarget({ type: \"COMMENT\", commentId: comment.id })}\n >\n <Flag className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n\n {comment.txHash && (\n <a\n href={`${EXPLORER_URL}/tx/${comment.txHash}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 px-1 text-2xs font-medium transition-colors\"\n style={{ color: \"hsl(var(--brand-blue) / 0.55)\" }}\n onMouseEnter={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue))\")}\n onMouseLeave={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue) / 0.55)\")}\n >\n <span>⛓</span>\n <span>onchain proof</span>\n <ExternalLink className=\"h-2.5 w-2.5\" />\n </a>\n )}\n </div>\n </div>\n );\n })}\n <div ref={bottomRef} />\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border/60 shrink-0\">\n {!hasWallet ? (\n <div className=\"flex items-center justify-center px-4 h-16\">\n <button type=\"button\" onClick={app.onRequireWallet} className=\"text-sm text-primary hover:underline\">\n {app.connectLabel ?? \"Connect\"} to comment\n </button>\n </div>\n ) : (\n <div className=\"px-3 pt-2 pb-3 space-y-2\">\n\n <div className=\"flex items-center gap-1.5\">\n <Zap className=\"h-3 w-3\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n <span className=\"text-2xs font-semibold\" style={{ color: \"hsl(var(--brand-blue))\" }}>\n Mint your message onchain\n </span>\n </div>\n\n <div\n className=\"rounded-xl border bg-background/60 transition-all focus-within:ring-2\"\n style={{\n borderColor: \"hsl(var(--border))\",\n }}\n onFocusCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--brand-blue) / 0.6)\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"0 0 0 3px hsl(var(--brand-blue) / 0.12)\";\n }}\n onBlurCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--border))\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"\";\n }}\n >\n <Textarea\n ref={composeRef}\n placeholder=\"Say something onchain… it's permanent.\"\n value={text}\n onChange={handleTextInput}\n onKeyDown={handleKeyDown}\n rows={2}\n className=\"resize-none min-h-[52px] max-h-[120px] w-full border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 pt-2.5 pb-1 text-sm rounded-xl\"\n disabled={isProcessing}\n />\n <div className=\"flex items-center justify-between px-3 pb-2.5\">\n <span className=\"text-2xs text-muted-foreground/40 flex items-center gap-1\">\n Enter ↵ to post\n </span>\n <div className=\"flex items-center gap-2\">\n {byteLen > 800 && (\n <span className={`text-2xs ${byteLen > MAX_LEN ? \"text-destructive\" : \"text-muted-foreground\"}`}>\n {byteLen}/{MAX_LEN}\n </span>\n )}\n <button\n onClick={post}\n disabled={!canSubmit || isProcessing}\n className=\"flex items-center gap-1.5 h-7 px-3 text-xs font-semibold rounded-full text-white transition-all hover:opacity-90 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {isProcessing ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <Send className=\"h-3 w-3\" />}\n Post onchain\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {reportTarget && (\n <ReportDialog\n target={reportTarget}\n open={!!reportTarget}\n onOpenChange={(open) => { if (!open) setReportTarget(null); }}\n />\n )}\n\n <TransactionDialog tx={tx} title=\"Comment\" workingLabel=\"Posting your comment…\" firstStepLabel=\"Prepare comment\">\n <TransactionSuccess\n name=\"Comment\"\n {...successMessages.commented()}\n txHash={tx.txHash}\n actions={[{ label: \"Done\", onClick: tx.reset }]}\n >\n <RewardEarned actionType=\"comment\" />\n </TransactionSuccess>\n </TransactionDialog>\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -7,6 +7,7 @@ import { AddressDisplay } from "./address-display.js";
|
|
|
7
7
|
import { Button } from "./button.js";
|
|
8
8
|
import Link from "./link.js";
|
|
9
9
|
import { useMedialaneApp } from "./medialane-app.js";
|
|
10
|
+
import { ReportDialog } from "./report-dialog.js";
|
|
10
11
|
import { RewardEarned } from "./rewards/reward-earned.js";
|
|
11
12
|
import { Skeleton } from "./skeleton.js";
|
|
12
13
|
import { Textarea } from "./textarea.js";
|
|
@@ -24,7 +25,6 @@ function CommentsSection({ contract, tokenId, className }) {
|
|
|
24
25
|
const app = useMedialaneApp();
|
|
25
26
|
const walletAddress = app.walletAddress;
|
|
26
27
|
const hasWallet = !!walletAddress;
|
|
27
|
-
const { ReportDialog } = app;
|
|
28
28
|
const { comments, isLoading, mutate } = useComments(contract, tokenId);
|
|
29
29
|
const tx = useTransaction();
|
|
30
30
|
const [text, setText] = useState("");
|
|
@@ -243,7 +243,7 @@ function CommentsSection({ contract, tokenId, className }) {
|
|
|
243
243
|
}
|
|
244
244
|
)
|
|
245
245
|
] }) }),
|
|
246
|
-
reportTarget &&
|
|
246
|
+
reportTarget && /* @__PURE__ */ jsx(
|
|
247
247
|
ReportDialog,
|
|
248
248
|
{
|
|
249
249
|
target: reportTarget,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/comments-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { ExternalLink, Flag, Loader2, MessageCircle, Send, Zap } from \"lucide-react\";\nimport { normalizeAddress, profileHref, STARKNET_NFTCOMMENTS_CONTRACT } from \"@medialane/sdk\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { Button } from \"./button.js\";\nimport Link from \"./link.js\";\nimport { useMedialaneApp, type ReportTarget } from \"./medialane-app.js\";\nimport { RewardEarned } from \"./rewards/reward-earned.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Textarea } from \"./textarea.js\";\nimport { TransactionDialog } from \"./transaction-dialog.js\";\nimport { TransactionSuccess } from \"./transaction-success.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { runIntentCalls } from \"../utils/run-intent-calls.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { successMessages } from \"../utils/success-messages.js\";\nimport { useComments } from \"../utils/use-comments.js\";\nimport { useTransaction } from \"../utils/use-transaction.js\";\n\nconst MAX_LEN = 1000;\n\nexport interface CommentsSectionProps {\n contract: string;\n tokenId: string;\n className?: string;\n}\n\nexport function CommentsSection({ contract, tokenId, className }: CommentsSectionProps) {\n const app = useMedialaneApp();\n const walletAddress = app.walletAddress;\n const hasWallet = !!walletAddress;\n const { ReportDialog } = app;\n const { comments, isLoading, mutate } = useComments(contract, tokenId);\n const tx = useTransaction();\n\n const [text, setText] = useState(\"\");\n const [reportTarget, setReportTarget] = useState<ReportTarget | null>(null);\n\n const messagesRef = useRef<HTMLDivElement>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const composeRef = useRef<HTMLTextAreaElement>(null);\n\n const byteLen = new TextEncoder().encode(text).length;\n const canSubmit = text.trim().length > 0 && byteLen <= MAX_LEN && !!STARKNET_NFTCOMMENTS_CONTRACT;\n const isProcessing = tx.phase === \"preparing\" || tx.phase === \"submitting\" || tx.phase === \"confirming\";\n\n const isOwn = (author: string) =>\n !!walletAddress && normalizeAddress(\"STARKNET\", author) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const isNearBottom = () => {\n const el = messagesRef.current;\n if (!el) return true;\n return el.scrollHeight - el.scrollTop - el.clientHeight < 100;\n };\n\n useEffect(() => {\n if (isNearBottom()) {\n bottomRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }\n\n }, [comments.length]);\n\n const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && canSubmit && !isProcessing) {\n e.preventDefault();\n post();\n }\n };\n\n const post = () => {\n const body = text.trim();\n void tx\n .run(async (signer) => {\n await runIntentCalls(signer, () =>\n app.getClient().api.intents.create(\"comment\", { author: signer.address, nftContract: contract, tokenId, text: body }),\n );\n })\n .then((ok) => {\n if (!ok) return;\n setText(\"\");\n if (composeRef.current) composeRef.current.style.height = \"auto\";\n void mutate();\n });\n };\n\n const handleStartConversation = () => {\n if (hasWallet) composeRef.current?.focus();\n };\n\n return (\n <div className={cn(\"flex flex-col h-[480px] overflow-hidden\", className)}>\n\n <div ref={messagesRef} className=\"flex-1 overflow-y-auto px-4 py-4\">\n {isLoading ? (\n <div className=\"space-y-4\">\n {([\n { own: false, w: \"w-40\" },\n { own: true, w: \"w-56\" },\n { own: false, w: \"w-48\" },\n { own: true, w: \"w-32\" },\n ] as const).map((item, i) => (\n <div key={i} className={`flex ${item.own ? \"justify-end\" : \"justify-start\"} gap-2`}>\n {!item.own && <Skeleton className=\"h-8 w-8 rounded-full shrink-0\" />}\n <Skeleton\n className={`h-10 ${item.w} rounded-2xl ${item.own ? \"rounded-tr-sm\" : \"rounded-tl-sm\"}`}\n />\n </div>\n ))}\n </div>\n ) : comments.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full\" style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue) / 0.15), hsl(var(--brand-purple) / 0.15))\" }}>\n <MessageCircle className=\"h-7 w-7\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n </div>\n <div>\n <p className=\"text-sm font-semibold\">Nothing here yet</p>\n <p className=\"text-xs text-muted-foreground mt-1 leading-relaxed max-w-[220px]\">\n Your comment will be minted onchain — attached to this NFT forever.\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={handleStartConversation}\n className=\"rounded-full text-white px-5\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n Write the first comment\n </Button>\n </div>\n ) : (\n <div className=\"space-y-5\">\n {comments.map((comment) => {\n const own = isOwn(comment.author);\n const avatarSeed = parseInt(comment.author.slice(2, 8), 16) % 360;\n const avatarBg = own\n ? \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\"\n : `linear-gradient(135deg, hsl(${avatarSeed}, 65%, 45%), hsl(${(avatarSeed + 60) % 360}, 65%, 55%))`;\n\n return (\n <div\n key={comment.id}\n className={`group flex gap-2.5 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}\n >\n\n <Link href={profileHref({ address: comment.author })} className=\"shrink-0 mt-0.5\">\n <div\n className=\"h-9 w-9 rounded-full flex items-center justify-center text-2xs font-bold text-white select-none ring-2 ring-background hover:ring-primary/40 transition-all\"\n style={{ background: avatarBg }}\n >\n {comment.author.slice(2, 5).toUpperCase()}\n </div>\n </Link>\n\n <div className={`flex flex-col max-w-[75%] gap-1 ${own ? \"items-end\" : \"items-start\"}`}>\n\n <div className={`flex items-center gap-2 px-1 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}>\n <Link\n href={profileHref({ address: comment.author })}\n className=\"text-xs font-semibold text-foreground hover:underline underline-offset-2\"\n >\n {own ? \"You\" : <AddressDisplay address={comment.author} chars={5} showCopy={false} />}\n </Link>\n <span className=\"text-2xs text-muted-foreground\" title={comment.postedAt}>\n {timeAgo(comment.postedAt)}\n </span>\n </div>\n\n <div className=\"relative\">\n {own ? (\n <div\n className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words rounded-2xl rounded-tr-sm text-white\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {comment.content}\n </div>\n ) : (\n <div className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words bg-muted rounded-2xl rounded-tl-sm border border-border/50\">\n {comment.content}\n </div>\n )}\n {!own && hasWallet && (\n <button\n className=\"absolute -top-1 -right-5 opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground/40 hover:text-destructive/70\"\n title=\"Report comment\"\n aria-label=\"Report comment\"\n onClick={() => setReportTarget({ type: \"COMMENT\", commentId: comment.id })}\n >\n <Flag className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n\n {comment.txHash && (\n <a\n href={`${EXPLORER_URL}/tx/${comment.txHash}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 px-1 text-2xs font-medium transition-colors\"\n style={{ color: \"hsl(var(--brand-blue) / 0.55)\" }}\n onMouseEnter={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue))\")}\n onMouseLeave={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue) / 0.55)\")}\n >\n <span>⛓</span>\n <span>onchain proof</span>\n <ExternalLink className=\"h-2.5 w-2.5\" />\n </a>\n )}\n </div>\n </div>\n );\n })}\n <div ref={bottomRef} />\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border/60 shrink-0\">\n {!hasWallet ? (\n <div className=\"flex items-center justify-center px-4 h-16\">\n <button type=\"button\" onClick={app.onRequireWallet} className=\"text-sm text-primary hover:underline\">\n {app.connectLabel ?? \"Connect\"} to comment\n </button>\n </div>\n ) : (\n <div className=\"px-3 pt-2 pb-3 space-y-2\">\n\n <div className=\"flex items-center gap-1.5\">\n <Zap className=\"h-3 w-3\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n <span className=\"text-2xs font-semibold\" style={{ color: \"hsl(var(--brand-blue))\" }}>\n Mint your message onchain\n </span>\n </div>\n\n <div\n className=\"rounded-xl border bg-background/60 transition-all focus-within:ring-2\"\n style={{\n borderColor: \"hsl(var(--border))\",\n }}\n onFocusCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--brand-blue) / 0.6)\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"0 0 0 3px hsl(var(--brand-blue) / 0.12)\";\n }}\n onBlurCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--border))\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"\";\n }}\n >\n <Textarea\n ref={composeRef}\n placeholder=\"Say something onchain… it's permanent.\"\n value={text}\n onChange={handleTextInput}\n onKeyDown={handleKeyDown}\n rows={2}\n className=\"resize-none min-h-[52px] max-h-[120px] w-full border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 pt-2.5 pb-1 text-sm rounded-xl\"\n disabled={isProcessing}\n />\n <div className=\"flex items-center justify-between px-3 pb-2.5\">\n <span className=\"text-2xs text-muted-foreground/40 flex items-center gap-1\">\n Enter ↵ to post\n </span>\n <div className=\"flex items-center gap-2\">\n {byteLen > 800 && (\n <span className={`text-2xs ${byteLen > MAX_LEN ? \"text-destructive\" : \"text-muted-foreground\"}`}>\n {byteLen}/{MAX_LEN}\n </span>\n )}\n <button\n onClick={post}\n disabled={!canSubmit || isProcessing}\n className=\"flex items-center gap-1.5 h-7 px-3 text-xs font-semibold rounded-full text-white transition-all hover:opacity-90 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {isProcessing ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <Send className=\"h-3 w-3\" />}\n Post onchain\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {reportTarget && ReportDialog && (\n <ReportDialog\n target={reportTarget}\n open={!!reportTarget}\n onOpenChange={(open) => { if (!open) setReportTarget(null); }}\n />\n )}\n\n <TransactionDialog tx={tx} title=\"Comment\" workingLabel=\"Posting your comment…\" firstStepLabel=\"Prepare comment\">\n <TransactionSuccess\n name=\"Comment\"\n {...successMessages.commented()}\n txHash={tx.txHash}\n actions={[{ label: \"Done\", onClick: tx.reset }]}\n >\n <RewardEarned actionType=\"comment\" />\n </TransactionSuccess>\n </TransactionDialog>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/comments-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { ExternalLink, Flag, Loader2, MessageCircle, Send, Zap } from \"lucide-react\";\nimport { normalizeAddress, profileHref, STARKNET_NFTCOMMENTS_CONTRACT } from \"@medialane/sdk\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { Button } from \"./button.js\";\nimport Link from \"./link.js\";\nimport { useMedialaneApp, type ReportTarget } from \"./medialane-app.js\";\nimport { ReportDialog } from \"./report-dialog.js\";\nimport { RewardEarned } from \"./rewards/reward-earned.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Textarea } from \"./textarea.js\";\nimport { TransactionDialog } from \"./transaction-dialog.js\";\nimport { TransactionSuccess } from \"./transaction-success.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { runIntentCalls } from \"../utils/run-intent-calls.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { successMessages } from \"../utils/success-messages.js\";\nimport { useComments } from \"../utils/use-comments.js\";\nimport { useTransaction } from \"../utils/use-transaction.js\";\n\nconst MAX_LEN = 1000;\n\nexport interface CommentsSectionProps {\n contract: string;\n tokenId: string;\n className?: string;\n}\n\nexport function CommentsSection({ contract, tokenId, className }: CommentsSectionProps) {\n const app = useMedialaneApp();\n const walletAddress = app.walletAddress;\n const hasWallet = !!walletAddress;\n const { comments, isLoading, mutate } = useComments(contract, tokenId);\n const tx = useTransaction();\n\n const [text, setText] = useState(\"\");\n const [reportTarget, setReportTarget] = useState<ReportTarget | null>(null);\n\n const messagesRef = useRef<HTMLDivElement>(null);\n const bottomRef = useRef<HTMLDivElement>(null);\n const composeRef = useRef<HTMLTextAreaElement>(null);\n\n const byteLen = new TextEncoder().encode(text).length;\n const canSubmit = text.trim().length > 0 && byteLen <= MAX_LEN && !!STARKNET_NFTCOMMENTS_CONTRACT;\n const isProcessing = tx.phase === \"preparing\" || tx.phase === \"submitting\" || tx.phase === \"confirming\";\n\n const isOwn = (author: string) =>\n !!walletAddress && normalizeAddress(\"STARKNET\", author) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const isNearBottom = () => {\n const el = messagesRef.current;\n if (!el) return true;\n return el.scrollHeight - el.scrollTop - el.clientHeight < 100;\n };\n\n useEffect(() => {\n if (isNearBottom()) {\n bottomRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }\n\n }, [comments.length]);\n\n const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && canSubmit && !isProcessing) {\n e.preventDefault();\n post();\n }\n };\n\n const post = () => {\n const body = text.trim();\n void tx\n .run(async (signer) => {\n await runIntentCalls(signer, () =>\n app.getClient().api.intents.create(\"comment\", { author: signer.address, nftContract: contract, tokenId, text: body }),\n );\n })\n .then((ok) => {\n if (!ok) return;\n setText(\"\");\n if (composeRef.current) composeRef.current.style.height = \"auto\";\n void mutate();\n });\n };\n\n const handleStartConversation = () => {\n if (hasWallet) composeRef.current?.focus();\n };\n\n return (\n <div className={cn(\"flex flex-col h-[480px] overflow-hidden\", className)}>\n\n <div ref={messagesRef} className=\"flex-1 overflow-y-auto px-4 py-4\">\n {isLoading ? (\n <div className=\"space-y-4\">\n {([\n { own: false, w: \"w-40\" },\n { own: true, w: \"w-56\" },\n { own: false, w: \"w-48\" },\n { own: true, w: \"w-32\" },\n ] as const).map((item, i) => (\n <div key={i} className={`flex ${item.own ? \"justify-end\" : \"justify-start\"} gap-2`}>\n {!item.own && <Skeleton className=\"h-8 w-8 rounded-full shrink-0\" />}\n <Skeleton\n className={`h-10 ${item.w} rounded-2xl ${item.own ? \"rounded-tr-sm\" : \"rounded-tl-sm\"}`}\n />\n </div>\n ))}\n </div>\n ) : comments.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full\" style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue) / 0.15), hsl(var(--brand-purple) / 0.15))\" }}>\n <MessageCircle className=\"h-7 w-7\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n </div>\n <div>\n <p className=\"text-sm font-semibold\">Nothing here yet</p>\n <p className=\"text-xs text-muted-foreground mt-1 leading-relaxed max-w-[220px]\">\n Your comment will be minted onchain — attached to this NFT forever.\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={handleStartConversation}\n className=\"rounded-full text-white px-5\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n Write the first comment\n </Button>\n </div>\n ) : (\n <div className=\"space-y-5\">\n {comments.map((comment) => {\n const own = isOwn(comment.author);\n const avatarSeed = parseInt(comment.author.slice(2, 8), 16) % 360;\n const avatarBg = own\n ? \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\"\n : `linear-gradient(135deg, hsl(${avatarSeed}, 65%, 45%), hsl(${(avatarSeed + 60) % 360}, 65%, 55%))`;\n\n return (\n <div\n key={comment.id}\n className={`group flex gap-2.5 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}\n >\n\n <Link href={profileHref({ address: comment.author })} className=\"shrink-0 mt-0.5\">\n <div\n className=\"h-9 w-9 rounded-full flex items-center justify-center text-2xs font-bold text-white select-none ring-2 ring-background hover:ring-primary/40 transition-all\"\n style={{ background: avatarBg }}\n >\n {comment.author.slice(2, 5).toUpperCase()}\n </div>\n </Link>\n\n <div className={`flex flex-col max-w-[75%] gap-1 ${own ? \"items-end\" : \"items-start\"}`}>\n\n <div className={`flex items-center gap-2 px-1 ${own ? \"flex-row-reverse\" : \"flex-row\"}`}>\n <Link\n href={profileHref({ address: comment.author })}\n className=\"text-xs font-semibold text-foreground hover:underline underline-offset-2\"\n >\n {own ? \"You\" : <AddressDisplay address={comment.author} chars={5} showCopy={false} />}\n </Link>\n <span className=\"text-2xs text-muted-foreground\" title={comment.postedAt}>\n {timeAgo(comment.postedAt)}\n </span>\n </div>\n\n <div className=\"relative\">\n {own ? (\n <div\n className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words rounded-2xl rounded-tr-sm text-white\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {comment.content}\n </div>\n ) : (\n <div className=\"px-3.5 py-2.5 text-sm leading-relaxed break-words bg-muted rounded-2xl rounded-tl-sm border border-border/50\">\n {comment.content}\n </div>\n )}\n {!own && hasWallet && (\n <button\n className=\"absolute -top-1 -right-5 opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground/40 hover:text-destructive/70\"\n title=\"Report comment\"\n aria-label=\"Report comment\"\n onClick={() => setReportTarget({ type: \"COMMENT\", commentId: comment.id })}\n >\n <Flag className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n\n {comment.txHash && (\n <a\n href={`${EXPLORER_URL}/tx/${comment.txHash}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 px-1 text-2xs font-medium transition-colors\"\n style={{ color: \"hsl(var(--brand-blue) / 0.55)\" }}\n onMouseEnter={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue))\")}\n onMouseLeave={(e) => (e.currentTarget.style.color = \"hsl(var(--brand-blue) / 0.55)\")}\n >\n <span>⛓</span>\n <span>onchain proof</span>\n <ExternalLink className=\"h-2.5 w-2.5\" />\n </a>\n )}\n </div>\n </div>\n );\n })}\n <div ref={bottomRef} />\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border/60 shrink-0\">\n {!hasWallet ? (\n <div className=\"flex items-center justify-center px-4 h-16\">\n <button type=\"button\" onClick={app.onRequireWallet} className=\"text-sm text-primary hover:underline\">\n {app.connectLabel ?? \"Connect\"} to comment\n </button>\n </div>\n ) : (\n <div className=\"px-3 pt-2 pb-3 space-y-2\">\n\n <div className=\"flex items-center gap-1.5\">\n <Zap className=\"h-3 w-3\" style={{ color: \"hsl(var(--brand-blue))\" }} />\n <span className=\"text-2xs font-semibold\" style={{ color: \"hsl(var(--brand-blue))\" }}>\n Mint your message onchain\n </span>\n </div>\n\n <div\n className=\"rounded-xl border bg-background/60 transition-all focus-within:ring-2\"\n style={{\n borderColor: \"hsl(var(--border))\",\n }}\n onFocusCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--brand-blue) / 0.6)\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"0 0 0 3px hsl(var(--brand-blue) / 0.12)\";\n }}\n onBlurCapture={(e) => {\n (e.currentTarget as HTMLDivElement).style.borderColor = \"hsl(var(--border))\";\n (e.currentTarget as HTMLDivElement).style.boxShadow = \"\";\n }}\n >\n <Textarea\n ref={composeRef}\n placeholder=\"Say something onchain… it's permanent.\"\n value={text}\n onChange={handleTextInput}\n onKeyDown={handleKeyDown}\n rows={2}\n className=\"resize-none min-h-[52px] max-h-[120px] w-full border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 pt-2.5 pb-1 text-sm rounded-xl\"\n disabled={isProcessing}\n />\n <div className=\"flex items-center justify-between px-3 pb-2.5\">\n <span className=\"text-2xs text-muted-foreground/40 flex items-center gap-1\">\n Enter ↵ to post\n </span>\n <div className=\"flex items-center gap-2\">\n {byteLen > 800 && (\n <span className={`text-2xs ${byteLen > MAX_LEN ? \"text-destructive\" : \"text-muted-foreground\"}`}>\n {byteLen}/{MAX_LEN}\n </span>\n )}\n <button\n onClick={post}\n disabled={!canSubmit || isProcessing}\n className=\"flex items-center gap-1.5 h-7 px-3 text-xs font-semibold rounded-full text-white transition-all hover:opacity-90 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed\"\n style={{ background: \"linear-gradient(135deg, hsl(var(--brand-blue)), hsl(var(--brand-purple)))\" }}\n >\n {isProcessing ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <Send className=\"h-3 w-3\" />}\n Post onchain\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {reportTarget && (\n <ReportDialog\n target={reportTarget}\n open={!!reportTarget}\n onOpenChange={(open) => { if (!open) setReportTarget(null); }}\n />\n )}\n\n <TransactionDialog tx={tx} title=\"Comment\" workingLabel=\"Posting your comment…\" firstStepLabel=\"Prepare comment\">\n <TransactionSuccess\n name=\"Comment\"\n {...successMessages.commented()}\n txHash={tx.txHash}\n actions={[{ label: \"Done\", onClick: tx.reset }]}\n >\n <RewardEarned actionType=\"comment\" />\n </TransactionSuccess>\n </TransactionDialog>\n </div>\n );\n}\n"],"mappings":";AA8Gc,SACgB,KADhB;AA5Gd,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,cAAc,MAAM,SAAS,eAAe,MAAM,WAAW;AACtE,SAAS,kBAAkB,aAAa,qCAAqC;AAC7E,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,OAAO,UAAU;AACjB,SAAS,uBAA0C;AACnD,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAE/B,MAAM,UAAU;AAQT,SAAS,gBAAgB,EAAE,UAAU,SAAS,UAAU,GAAyB;AACtF,QAAM,MAAM,gBAAgB;AAC5B,QAAM,gBAAgB,IAAI;AAC1B,QAAM,YAAY,CAAC,CAAC;AACpB,QAAM,EAAE,UAAU,WAAW,OAAO,IAAI,YAAY,UAAU,OAAO;AACrE,QAAM,KAAK,eAAe;AAE1B,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,cAAc,eAAe,IAAI,SAA8B,IAAI;AAE1E,QAAM,cAAc,OAAuB,IAAI;AAC/C,QAAM,YAAY,OAAuB,IAAI;AAC7C,QAAM,aAAa,OAA4B,IAAI;AAEnD,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE;AAC/C,QAAM,YAAY,KAAK,KAAK,EAAE,SAAS,KAAK,WAAW,WAAW,CAAC,CAAC;AACpE,QAAM,eAAe,GAAG,UAAU,eAAe,GAAG,UAAU,gBAAgB,GAAG,UAAU;AAE3F,QAAM,QAAQ,CAAC,WACb,CAAC,CAAC,iBAAiB,iBAAiB,YAAY,MAAM,MAAM,iBAAiB,YAAY,aAAa;AAExG,QAAM,eAAe,MAAM;AACzB,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI,QAAO;AAChB,WAAO,GAAG,eAAe,GAAG,YAAY,GAAG,eAAe;AAAA,EAC5D;AAEA,YAAU,MAAM;AACd,QAAI,aAAa,GAAG;AAClB,gBAAU,SAAS,eAAe,EAAE,UAAU,SAAS,CAAC;AAAA,IAC1D;AAAA,EAEF,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,QAAM,kBAAkB,CAAC,MAA8C;AACrE,YAAQ,EAAE,OAAO,KAAK;AACtB,MAAE,OAAO,MAAM,SAAS;AACxB,MAAE,OAAO,MAAM,SAAS,KAAK,IAAI,EAAE,OAAO,cAAc,EAAE,IAAI;AAAA,EAChE;AAEA,QAAM,gBAAgB,CAAC,MAAgD;AACrE,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,YAAY,aAAa,CAAC,cAAc;AAClE,QAAE,eAAe;AACjB,WAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,OAAO,MAAM;AACjB,UAAM,OAAO,KAAK,KAAK;AACvB,SAAK,GACF,IAAI,OAAO,WAAW;AACrB,YAAM;AAAA,QAAe;AAAA,QAAQ,MAC3B,IAAI,UAAU,EAAE,IAAI,QAAQ,OAAO,WAAW,EAAE,QAAQ,OAAO,SAAS,aAAa,UAAU,SAAS,MAAM,KAAK,CAAC;AAAA,MACtH;AAAA,IACF,CAAC,EACA,KAAK,CAAC,OAAO;AACZ,UAAI,CAAC,GAAI;AACT,cAAQ,EAAE;AACV,UAAI,WAAW,QAAS,YAAW,QAAQ,MAAM,SAAS;AAC1D,WAAK,OAAO;AAAA,IACd,CAAC;AAAA,EACL;AAEA,QAAM,0BAA0B,MAAM;AACpC,QAAI,UAAW,YAAW,SAAS,MAAM;AAAA,EAC3C;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GAErE;AAAA,wBAAC,SAAI,KAAK,aAAa,WAAU,oCAC9B,sBACC,oBAAC,SAAI,WAAU,aACX;AAAA,MACA,EAAE,KAAK,OAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,MAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,OAAO,GAAG,OAAO;AAAA,MACxB,EAAE,KAAK,MAAO,GAAG,OAAO;AAAA,IAC1B,EAAY,IAAI,CAAC,MAAM,MACrB,qBAAC,SAAY,WAAW,QAAQ,KAAK,MAAM,gBAAgB,eAAe,UACvE;AAAA,OAAC,KAAK,OAAO,oBAAC,YAAS,WAAU,iCAAgC;AAAA,MAClE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,QAAQ,KAAK,CAAC,gBAAgB,KAAK,MAAM,kBAAkB,eAAe;AAAA;AAAA,MACvF;AAAA,SAJQ,CAKV,CACD,GACH,IACE,SAAS,WAAW,IACtB,qBAAC,SAAI,WAAU,2EACb;AAAA,0BAAC,SAAI,WAAU,2DAA0D,OAAO,EAAE,YAAY,0FAA0F,GACtL,8BAAC,iBAAc,WAAU,WAAU,OAAO,EAAE,OAAO,yBAAyB,GAAG,GACjF;AAAA,MACA,qBAAC,SACC;AAAA,4BAAC,OAAE,WAAU,yBAAwB,8BAAgB;AAAA,QACrD,oBAAC,OAAE,WAAU,oEAAmE,sFAEhF;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,UAClG;AAAA;AAAA,MAED;AAAA,OACF,IAEA,qBAAC,SAAI,WAAU,aACZ;AAAA,eAAS,IAAI,CAAC,YAAY;AACzB,cAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,cAAM,aAAa,SAAS,QAAQ,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;AAC9D,cAAM,WAAW,MACb,8EACA,+BAA+B,UAAU,qBAAqB,aAAa,MAAM,GAAG;AAExF,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW,sBAAsB,MAAM,qBAAqB,UAAU;AAAA,YAGtE;AAAA,kCAAC,QAAK,MAAM,YAAY,EAAE,SAAS,QAAQ,OAAO,CAAC,GAAG,WAAU,mBAC9D;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,EAAE,YAAY,SAAS;AAAA,kBAE7B,kBAAQ,OAAO,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA;AAAA,cAC1C,GACF;AAAA,cAEA,qBAAC,SAAI,WAAW,mCAAmC,MAAM,cAAc,aAAa,IAElF;AAAA,qCAAC,SAAI,WAAW,gCAAgC,MAAM,qBAAqB,UAAU,IACnF;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM,YAAY,EAAE,SAAS,QAAQ,OAAO,CAAC;AAAA,sBAC7C,WAAU;AAAA,sBAET,gBAAM,QAAQ,oBAAC,kBAAe,SAAS,QAAQ,QAAQ,OAAO,GAAG,UAAU,OAAO;AAAA;AAAA,kBACrF;AAAA,kBACA,oBAAC,UAAK,WAAU,kCAAiC,OAAO,QAAQ,UAC7D,kBAAQ,QAAQ,QAAQ,GAC3B;AAAA,mBACF;AAAA,gBAEA,qBAAC,SAAI,WAAU,YACZ;AAAA,wBACC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,sBAEhG,kBAAQ;AAAA;AAAA,kBACX,IAEA,oBAAC,SAAI,WAAU,gHACZ,kBAAQ,SACX;AAAA,kBAED,CAAC,OAAO,aACP;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,OAAM;AAAA,sBACN,cAAW;AAAA,sBACX,SAAS,MAAM,gBAAgB,EAAE,MAAM,WAAW,WAAW,QAAQ,GAAG,CAAC;AAAA,sBAEzE,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,kBAC5B;AAAA,mBAEJ;AAAA,gBAEC,QAAQ,UACP;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAM,GAAG,YAAY,OAAO,QAAQ,MAAM;AAAA,oBAC1C,QAAO;AAAA,oBACP,KAAI;AAAA,oBACJ,WAAU;AAAA,oBACV,OAAO,EAAE,OAAO,gCAAgC;AAAA,oBAChD,cAAc,CAAC,MAAO,EAAE,cAAc,MAAM,QAAQ;AAAA,oBACpD,cAAc,CAAC,MAAO,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAEpD;AAAA,0CAAC,UAAK,oBAAC;AAAA,sBACP,oBAAC,UAAK,2BAAa;AAAA,sBACnB,oBAAC,gBAAa,WAAU,eAAc;AAAA;AAAA;AAAA,gBACxC;AAAA,iBAEJ;AAAA;AAAA;AAAA,UAnEK,QAAQ;AAAA,QAoEf;AAAA,MAEJ,CAAC;AAAA,MACD,oBAAC,SAAI,KAAK,WAAW;AAAA,OACvB,GAEJ;AAAA,IAEA,oBAAC,SAAI,WAAU,sCACZ,WAAC,YACA,oBAAC,SAAI,WAAU,8CACb,+BAAC,YAAO,MAAK,UAAS,SAAS,IAAI,iBAAiB,WAAU,wCAC3D;AAAA,UAAI,gBAAgB;AAAA,MAAU;AAAA,OACjC,GACF,IAEA,qBAAC,SAAI,WAAU,4BAEb;AAAA,2BAAC,SAAI,WAAU,6BACb;AAAA,4BAAC,OAAI,WAAU,WAAU,OAAO,EAAE,OAAO,yBAAyB,GAAG;AAAA,QACrE,oBAAC,UAAK,WAAU,0BAAyB,OAAO,EAAE,OAAO,yBAAyB,GAAG,uCAErF;AAAA,SACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO;AAAA,YACL,aAAa;AAAA,UACf;AAAA,UACA,gBAAgB,CAAC,MAAM;AACrB,YAAC,EAAE,cAAiC,MAAM,cAAc;AACxD,YAAC,EAAE,cAAiC,MAAM,YAAY;AAAA,UACxD;AAAA,UACA,eAAe,CAAC,MAAM;AACpB,YAAC,EAAE,cAAiC,MAAM,cAAc;AACxD,YAAC,EAAE,cAAiC,MAAM,YAAY;AAAA,UACxD;AAAA,UAEA;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,OAAO;AAAA,gBACP,UAAU;AAAA,gBACV,WAAW;AAAA,gBACX,MAAM;AAAA,gBACN,WAAU;AAAA,gBACV,UAAU;AAAA;AAAA,YACZ;AAAA,YACA,qBAAC,SAAI,WAAU,iDACb;AAAA,kCAAC,UAAK,WAAU,6DAA4D,kCAE5E;AAAA,cACA,qBAAC,SAAI,WAAU,2BACZ;AAAA,0BAAU,OACT,qBAAC,UAAK,WAAW,YAAY,UAAU,UAAU,qBAAqB,uBAAuB,IAC1F;AAAA;AAAA,kBAAQ;AAAA,kBAAE;AAAA,mBACb;AAAA,gBAEF;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS;AAAA,oBACT,UAAU,CAAC,aAAa;AAAA,oBACxB,WAAU;AAAA,oBACV,OAAO,EAAE,YAAY,4EAA4E;AAAA,oBAEhG;AAAA,qCAAe,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,QAAK,WAAU,WAAU;AAAA,sBAAG;AAAA;AAAA;AAAA,gBAE9F;AAAA,iBACF;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA,OACF,GAEJ;AAAA,IAEC,gBACC;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,MAAM,CAAC,CAAC;AAAA,QACR,cAAc,CAAC,SAAS;AAAE,cAAI,CAAC,KAAM,iBAAgB,IAAI;AAAA,QAAG;AAAA;AAAA,IAC9D;AAAA,IAGF,oBAAC,qBAAkB,IAAQ,OAAM,WAAU,cAAa,8BAAwB,gBAAe,mBAC7F;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACJ,GAAG,gBAAgB,UAAU;AAAA,QAC9B,QAAQ,GAAG;AAAA,QACX,SAAS,CAAC,EAAE,OAAO,QAAQ,SAAS,GAAG,MAAM,CAAC;AAAA,QAE9C,8BAAC,gBAAa,YAAW,WAAU;AAAA;AAAA,IACrC,GACF;AAAA,KACF;AAEJ;","names":[]}
|