Skip to content

Commit

Permalink
Merge pull request kodadot#8171 from kodadot/feat/unify-metadata
Browse files Browse the repository at this point in the history
feat: unify metadata on gallery detail page
  • Loading branch information
yangwao authored Jan 5, 2024
2 parents 5cb3c32 + 0faad68 commit 1625e6c
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 59 deletions.
36 changes: 28 additions & 8 deletions components/gallery/GalleryItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@
</div>
<BaseMediaItem
v-else
:key="nftImage"
:key="image"
ref="mediaItemRef"
class="gallery-item-media is-relative"
:src="getMediaSrc(nftImage)"
:src="getMediaSrc(image)"
:animation-src="nftAnimation"
:mime-type="nftMimeType"
:mime-type="nftAnimationMimeType || nftMimeType"
:title="nftMetadata?.name"
:is-fullscreen="isFullscreen"
is-detail
:is-lewd="galleryDescriptionRef?.isLewd"
:placeholder="placeholder"
:image-component="NuxtImg"
:sizes="sizes"
:audio-player-cover="nftImage" />
:audio-player-cover="image" />
</div>
</div>
Expand All @@ -80,7 +80,7 @@
<div class="flex justify-between">
<div class="name-container">
<h1 class="title" data-testid="item-title">
{{ nftMetadata?.name }}
{{ title }}
<span v-if="nft?.burned" class="has-text-danger">「🔥」</span>
</h1>
<h2 class="subtitle" data-testid="item-collection">
Expand Down Expand Up @@ -218,8 +218,15 @@ const preferencesStore = usePreferencesStore()
const pageViewCount = usePageViews()
const galleryItem = useGalleryItem()
const { nft, nftMetadata, nftImage, nftAnimation, nftMimeType, nftResources } =
galleryItem
const {
nft,
nftMetadata,
nftImage,
nftAnimation,
nftAnimationMimeType,
nftMimeType,
nftResources,
} = galleryItem
const collection = computed(() => nft.value?.collection)
const triggerBuySuccess = computed(() => preferencesStore.triggerBuySuccess)
Expand Down Expand Up @@ -256,6 +263,14 @@ const onNFTBought = () => {
activeTab.value = tabs.activity
}
const image = computed(() => {
if (!nftImage.value) {
return sanitizeIpfsUrl(nft.value?.meta?.image)
}
return nftImage.value
})
const getMediaSrc = (src: string | undefined) =>
src && isFullscreen.value ? toOriginalContentUrl(src) : src
Expand All @@ -277,7 +292,12 @@ onMounted(() => {
const { isUnlockable, unlockLink } = useUnlockable(collection)
const title = computed(() => nftMetadata.value?.name || '')
const title = computed(() =>
addSnSuffixName(
nft.value?.name || nftMetadata.value?.name || '',
nft.value?.sn,
),
)
const seoDescription = computed(
() => convertMarkdownToText(nftMetadata.value?.description) || '',
)
Expand Down
40 changes: 22 additions & 18 deletions components/gallery/GalleryItemDescription.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@
</div>
<hr class="my-2" />
<div v-if="nftImage" class="flex justify-between">
<div v-if="mediaUrl" class="flex justify-between">
<p>{{ $t('tabs.tabDetails.media') }}</p>
<div>
<a
v-safe-href="nftImage"
v-safe-href="mediaUrl"
class="has-text-link"
data-testid="media-link"
target="_blank"
Expand All @@ -139,15 +139,15 @@
</a>
</div>
</div>
<div v-if="nftAnimation" class="flex justify-between">
<div v-if="animatedMediaUrl" class="flex justify-between">
<p>{{ $t('tabs.tabDetails.animatedMedia') }}</p>
<div>
<a
v-safe-href="nftAnimation"
v-safe-href="animatedMediaUrl"
class="has-text-link"
target="_blank"
rel="nofollow noopener noreferrer">
{{ animationMediaMimeType }}
{{ nftAnimationMimeType }}
</a>
</div>
</div>
Expand Down Expand Up @@ -204,7 +204,7 @@ import { sanitizeIpfsUrl } from '@/utils/ipfs'
import { GalleryItem, useGalleryItem } from './useGalleryItem'
import { MediaType } from '@/components/rmrk/types'
import { getMimeType, resolveMedia } from '@/utils/gallery/media'
import { resolveMedia } from '@/utils/gallery/media'
import { replaceSingularCollectionUrlByText } from '@/utils/url'
const { urlPrefix } = usePrefix()
Expand All @@ -220,6 +220,7 @@ const nftMetadata = getValue('nftMetadata')
const nftMimeType = getValue('nftMimeType')
const nftImage = getValue('nftImage')
const nftAnimation = getValue('nftAnimation')
const nftAnimationMimeType = getValue('nftAnimationMimeType')
const activeTab = ref('0')
const { version } = useRmrkVersion()
Expand Down Expand Up @@ -264,7 +265,7 @@ const parentNftUrl = computed(() => {
const properties = computed(() => {
const attributes = (nftMetadata.value?.attributes ||
nftMetadata.value?.meta.attributes ||
nftMetadata.value?.meta?.attributes ||
[]) as Array<{ trait_type: string; value: string; key?: string }>
return attributes.map(({ trait_type, key, value }) => ({
Expand All @@ -281,22 +282,25 @@ const propertiesTabDisabled = computed(() => {
return !properties.value?.length
})
const metadataMimeType = ref('application/json')
const metadataURL = ref('')
const animationMediaMimeType = ref('')
const metadataMimeType = 'application/json'
const metadataURL = computed(() => sanitizeIpfsUrl(nft.value?.metadata))
watchEffect(async () => {
if (nft.value?.metadata) {
const sanitizeMetadata = sanitizeIpfsUrl(nft.value?.metadata)
const mimeType = await getMimeType(sanitizeMetadata)
const isCloudflareStream = (url: string) => url.includes('cloudflarestream.com')
metadataMimeType.value = mimeType || 'application/json'
metadataURL.value = sanitizeMetadata
const mediaUrl = computed(() => {
if (isCloudflareStream(nftImage.value)) {
return sanitizeIpfsUrl(nft.value.meta?.image)
}
if (nftAnimation.value) {
animationMediaMimeType.value = await getMimeType(nftAnimation.value)
return nftImage.value
})
const animatedMediaUrl = computed(() => {
if (isCloudflareStream(nftAnimation.value)) {
return sanitizeIpfsUrl(nft.value.meta?.animation_url)
}
return nftAnimation.value
})
</script>
Expand Down
63 changes: 36 additions & 27 deletions components/gallery/useGalleryItem.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { sanitizeIpfsUrl } from '@/utils/ipfs'
import { getMimeType } from '@/utils/gallery/media'
import { useHistoryStore } from '@/stores/history'
import { NftResources, getNftMetadata } from '@/composables/useNft'
import useSubscriptionGraphql from '@/composables/useSubscriptionGraphql'
import { getCloudflareMp4 } from '@/services/imageWorker'
import type { NFT } from '@/components/rmrk/service/scheme'
import type { NFTWithMetadata } from '@/composables/useNft'
import type { Ref } from 'vue'
Expand All @@ -16,37 +16,18 @@ export interface GalleryItem {
nftMimeType: Ref<string>
nftMetadata: Ref<NFTWithMetadata | undefined>
nftAnimation: Ref<string>
nftAnimationMimeType: Ref<string>
nftImage: Ref<string>
nftResources: Ref<NftResources[] | undefined>
}

const whichMimeType = async (data) => {
if (data?.type) {
return data?.type
}
if (data?.animationUrl) {
return await getMimeType(sanitizeIpfsUrl(data.animationUrl))
}
if (data?.image || data?.mediaUri) {
return await getMimeType(sanitizeIpfsUrl(data?.image || data?.mediaUri))
}

return ''
}

const whichAsset = (data) => {
return {
animation_url: sanitizeIpfsUrl(data.animationUrl || ''),
image: sanitizeIpfsUrl(data.image || data.mediaUri || '', 'image'),
}
}

export const useGalleryItem = (nftId?: string): GalleryItem => {
const { $consola } = useNuxtApp()
const historyStore = useHistoryStore()
const nft = ref<NFT>()
const nftImage = ref('')
const nftAnimation = ref('')
const nftAnimationMimeType = ref('')
const nftMimeType = ref('')
const nftMetadata = ref<NFTWithMetadata>()
const nftResources = ref<NftResources[]>()
Expand All @@ -61,6 +42,7 @@ export const useGalleryItem = (nftId?: string): GalleryItem => {
ahk: 'chain-ahk',
}

const { isIos, isSafari } = useDevice()
const { urlPrefix } = usePrefix()
const { data, refetch } = useGraphql({
queryName: 'nftById',
Expand Down Expand Up @@ -113,13 +95,39 @@ export const useGalleryItem = (nftId?: string): GalleryItem => {
}
})

nftMetadata.value = await getNftMetadata(nftEntity, urlPrefix.value)
nftMimeType.value = await whichMimeType(nftMetadata.value)
const metadata = await getNftMetadata(nftEntity, urlPrefix.value, true)
nftMetadata.value = metadata
nftResources.value = resources

const asset = whichAsset(nftMetadata.value)
nftImage.value = asset.image
nftAnimation.value = asset.animation_url
nftImage.value = metadata.image || ''
nftMimeType.value = metadata.imageMimeType || ''
nftAnimation.value = metadata.animationUrl || ''
nftAnimationMimeType.value = metadata.animationUrlMimeType || ''

// use cf-video & replace the video thumbnail
if (
nftAnimationMimeType.value.includes('video') ||
nftMimeType.value.includes('video')
) {
// fallback to cloudflare-ipfs for ios & safari while video is still processing to cf-stream
if (isIos || isSafari) {
nftImage.value = sanitizeIpfsUrl(nft.value.meta?.image, 'cloudflare')
nftAnimation.value = sanitizeIpfsUrl(
nft.value.meta?.animation_url,
'cloudflare',
)
}

// serve video from cloudflare stream
const streams = await getCloudflareMp4(
metadata.animationUrl || metadata.image,
)

if (streams.uid && streams.video?.default?.percentComplete === 100) {
nftAnimation.value = streams.video.default.url
nftImage.value = streams.detail?.thumbnail || ''
}
}

historyStore.addHistoryItem({
id: nft.value.id,
Expand All @@ -139,6 +147,7 @@ export const useGalleryItem = (nftId?: string): GalleryItem => {
nft,
nftImage,
nftAnimation,
nftAnimationMimeType,
nftMimeType,
nftMetadata,
nftResources,
Expand Down
1 change: 1 addition & 0 deletions components/rmrk/service/scheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface NFTMetadata extends Metadata, ItemResources {
image_ar?: string
properties?: Record<string, { value: string; type: string }>
unlockable?: boolean
sn?: string
}

export type CollectionMetadata = Metadata
Expand Down
24 changes: 18 additions & 6 deletions composables/useNft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { processSingleMetadata } from '@/utils/cachingStrategy'
import { getMimeType, isAudio as isAudioMimeType } from '@/utils/gallery/media'
import unionBy from 'lodash/unionBy'
import type { Ref } from 'vue'
import { getMetadata } from '@/services/imageWorker'

export type NftResources = {
id: string
Expand All @@ -24,8 +25,14 @@ export type ItemResources = {
resources?: NftResources[]
}

type baseMimeType = {
imageMimeType?: string
animationUrlMimeType?: string
}

export type NFTWithMetadata = NFT &
NFTMetadata & { meta: BaseNFTMeta } & ItemResources
NFTMetadata & { meta: BaseNFTMeta } & ItemResources &
baseMimeType

export type MinimalNFT = {
id: string
Expand Down Expand Up @@ -75,7 +82,7 @@ function getAttributes(nft, metadata) {
: attr
}

function getGeneralMetadata<T extends MinimalNFT>(nft: T) {
function getGeneralMetadata<T extends NFTWithMetadata>(nft: T) {
return {
...nft,
name: addSnSuffixName(nft.name || nft.meta.name, nft.sn) || nft.id,
Expand Down Expand Up @@ -136,7 +143,7 @@ export async function useNftMimeType<
}
}

async function getRmrk2Resources<T extends MinimalNFT>(nft: T) {
async function getRmrk2Resources<T extends NFTWithMetadata>(nft: T) {
const thumb = nft.resources?.[0]?.thumb
const src = nft.resources?.[0]?.src
const image = sanitizeIpfsUrl(thumb || src || '')
Expand All @@ -149,7 +156,7 @@ async function getRmrk2Resources<T extends MinimalNFT>(nft: T) {
}
}

async function getProcessMetadata<T extends MinimalNFT>(nft: T) {
async function getProcessMetadata<T extends NFTWithMetadata>(nft: T) {
const metadata = await processSingleMetadata<NFTMetadata>(nft.metadata)
const image = sanitizeIpfsUrl(
metadata.image || metadata.mediaUri || metadata.thumbnailUri || '',
Expand All @@ -169,10 +176,15 @@ async function getProcessMetadata<T extends MinimalNFT>(nft: T) {
}
}

export async function getNftMetadata<T extends MinimalNFT>(
export async function getNftMetadata<T extends NFTWithMetadata>(
nft: T,
prefix: string,
unify = false,
) {
if (unify) {
return await getMetadata(sanitizeIpfsUrl(nft.meta.id))
}

// if subsquid already give us the metadata, we don't need to fetch it again
if (nft.meta?.image) {
return getGeneralMetadata(nft)
Expand All @@ -186,7 +198,7 @@ export async function getNftMetadata<T extends MinimalNFT>(
return await getProcessMetadata(nft)
}

export default function useNftMetadata<T extends MinimalNFT>(nft: T) {
export default function useNftMetadata<T extends NFTWithMetadata>(nft: T) {
const item = ref<
T & {
name: string
Expand Down
1 change: 1 addition & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export default defineNuxtConfig({
'@nuxt/content',
'nuxt-simple-sitemap',
'@nuxtjs/google-fonts',
'@nuxtjs/device',
],

image: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"@nuxt/content": "^2.10.0",
"@nuxt/types": "^2.17.2",
"@nuxtjs/color-mode": "^3.3.2",
"@nuxtjs/device": "^3.1.1",
"@nuxtjs/google-fonts": "^3.1.3",
"@playwright/test": "^1.40.1",
"@rollup/plugin-graphql": "^1.1.0",
Expand Down
Loading

0 comments on commit 1625e6c

Please sign in to comment.