Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions app/components/collection/CollectionHeader.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { SupportedChain } from '~/plugins/sdk.client'
import type { AssetHubChain } from '~/plugins/sdk.client'
import type { OnchainCollection } from '~/services/oda'
import { getSubscanAccountUrl } from '~/utils/format/address'
import { sanitizeIpfsUrl, toOriginalContentUrl } from '~/utils/ipfs'
Expand All @@ -9,7 +9,7 @@ const props = withDefaults(
defineProps<{
collection: OnchainCollection | null
collectionId: string
chain: SupportedChain
chain: AssetHubChain
/** Optional override for owner (e.g. drops creator). Falls back to collection.owner */
creator?: string
/** Optional drop alias for "View Drop" button */
Expand Down Expand Up @@ -38,9 +38,34 @@ const bannerUrl = computed(() => {
return raw ? toOriginalContentUrl(sanitizeIpfsUrl(raw)) : ''
})

const ownerAddress = computed(
const ownerFromProps = computed(
() => props.creator || props.collection?.owner,
)

// Fallback: when ODA/drops fail, fetch collection owner from chain so creator + Subscan always show
const ownerFromChain = ref<string | null>(null)
onMounted(async () => {
if (ownerFromProps.value || !props.collectionId)
return
const collectionId = Number(props.collectionId)
if (Number.isNaN(collectionId))
return
try {
const { $sdk } = useNuxtApp()
const api = $sdk(props.chain).api
const collection = await api.query.Nfts.Collection.getValue(collectionId)
if (collection?.owner) {
ownerFromChain.value = collection.owner.toString()
}
}
catch {
// ignore
}
})

const ownerAddress = computed(
() => ownerFromProps.value || ownerFromChain.value,
)
</script>

<template>
Expand Down
8 changes: 4 additions & 4 deletions app/components/gallery/GalleryDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,16 @@ const actionItems = computed<DropdownMenuItem[]>(() => {
<template>
<div class="space-y-6">
<div>
<!-- Collection Information -->
<div v-if="collection" class="flex items-center gap-2">
<!-- Collection Information (always show; use fallback when ODA collection fails) -->
<div class="flex items-center gap-2">
<NuxtLink
:to="`/${chain}/collection/${collectionId || ''}`"
class="font-medium transition-colors flex items-center gap-2 text-blue-500 dark:text-blue-400 hover:text-blue-600 dark:hover:text-blue-500"
>
<!-- Collection Image -->
<div class="size-6 rounded-full overflow-hidden bg-muted flex shrink-0 items-center">
<img
v-if="collection.metadata?.image"
v-if="collection?.metadata?.image"
:src="sanitizeIpfsUrl(collection.metadata.image)"
:alt="collection.metadata?.name || 'Collection'"
class="w-full h-full object-cover"
Expand All @@ -153,7 +153,7 @@ const actionItems = computed<DropdownMenuItem[]>(() => {
class="size-4 text-muted-foreground m-auto"
/>
</div>
{{ collection.metadata?.name || `Collection ${collectionId || ''}` }}
{{ collection?.metadata?.name || `Collection ${collectionId || ''}` }}
</NuxtLink>
</div>

Expand Down
17 changes: 17 additions & 0 deletions app/composables/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ export function useToken(props: {
collectionCreator.value = collection.value?.owner ?? null
queryPrice.value = token.value?.price ?? null

// When ODA collection fails, fall back to genart then on-chain for collection creator
if (!collectionCreator.value) {
const [genartOk, _, genartRes] = await t($fetch<{ data: Array<{ creator?: string }> }>('/api/genart/list', {
query: { collection: props.collectionId.toString() },
}))
if (genartOk && genartRes?.data?.[0]?.creator) {
collectionCreator.value = genartRes.data[0].creator
}
}
if (!collectionCreator.value) {
const { api } = $sdk(props.chain)
const collectionOnChain = await api.query.Nfts.Collection.getValue(props.collectionId).catch(() => null)
if (collectionOnChain?.owner) {
collectionCreator.value = collectionOnChain.owner.toString()
}
}

const tokenRarityData = rarityData?.data.token
const rarityTotalItems = normalizeRarityTotalItems(collectionData?.supply)

Expand Down
10 changes: 5 additions & 5 deletions app/pages/[chain]/collection/[collection_id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import type { LocationQueryRaw } from 'vue-router'
import type { SelectedTrait } from '@/components/trait/types'
import type { AssetHubChain } from '~/plugins/sdk.client'
import { CHAINS } from '@kodadot1/static'
import { TradeTypes } from '@/components/trade/types'
import { fetchOdaCollection } from '~/services/oda'
import { normalizeRarityTotalItems } from '~/types/rarity'
import { chainSpec } from '~/utils/chain'

const availableTabs = ['items', 'offers', 'swaps', 'traits', 'analytics'] as const
type CollectionTab = typeof availableTabs[number]
Expand Down Expand Up @@ -72,12 +72,12 @@ const chain = computed(() => chainPrefix as AssetHubChain)
const { data } = await useLazyAsyncData(
`collection:${chain.value}:${collection_id}`,
async () => {
const [collection, drops] = await Promise.all([
fetchOdaCollection(chain.value, collection_id?.toString() ?? ''),
const [collectionResult, drops] = await Promise.all([
fetchOdaCollection(chain.value, collection_id?.toString() ?? '').catch(() => null),
$fetch('/api/genart/list', { query: { collection: collection_id?.toString() ?? '' } }),
])

return { collection, drops }
return { collection: collectionResult ?? null, drops }
},
)

Expand Down Expand Up @@ -173,7 +173,7 @@ function handleSelectedTraitsUpdate(traits: SelectedTrait[]) {
definePageMeta({
validate: async (route) => {
const { chain } = route.params
return typeof chain === 'string' && chain in CHAINS
return typeof chain === 'string' && chain in chainSpec
},
})

Expand Down
Loading