From e2d3e3d550a03b2ccae146a698eaa1286ceb6313 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 12:52:33 +0700 Subject: [PATCH 01/74] refactor(gallery): streamline NFT metadata fetching --- components/gallery/useGalleryItem.ts | 101 ++++++++++----------------- 1 file changed, 36 insertions(+), 65 deletions(-) diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index 59bca6ff6c..c468366275 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -1,12 +1,13 @@ import type { Ref } from 'vue' -import { sanitizeIpfsUrl } from '@/utils/ipfs' -import { useHistoryStore } from '@/stores/history' -import { getNftMetadata } from '@/composables/useNft' +import { tokenIdToRoute } from '../unique/utils' +import type { NFT } from '@/components/rmrk/service/scheme' +import type { NFTWithMetadata } from '@/composables/useNft' import useSubscriptionGraphql from '@/composables/useSubscriptionGraphql' import { getCloudflareMp4 } from '@/services/imageWorker' -import type { NFT } from '@/components/rmrk/service/scheme' -import type { NFTWithMetadata, NftResources } from '@/composables/useNft' -import { getMimeType } from '@/utils/gallery/media' +import { useHistoryStore } from '@/stores/history' +import { sanitizeIpfsUrl } from '@/utils/ipfs' +import type { NFTMetadata } from '@/services/oda' +import { fetchMimeType, fetchOdaToken } from '@/services/oda' interface NFTData { nftEntity?: NFTWithMetadata @@ -15,11 +16,10 @@ interface NFTData { export interface GalleryItem { nft: Ref nftMimeType: Ref - nftMetadata: Ref + nftMetadata: Ref nftAnimation: Ref nftAnimationMimeType: Ref nftImage: Ref - nftResources: Ref } export const useGalleryItem = (nftId?: string): GalleryItem => { @@ -30,12 +30,11 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { const nftAnimation = ref('') const nftAnimationMimeType = ref('') const nftMimeType = ref('') - const nftMetadata = ref() - const nftResources = ref() + const nftMetadata = ref() const { params } = useRoute() const id = nftId || params.id - // const { id: collectionID, item: id } = tokenIdToRoute(params.id) + const { id: collectionId, item: tokenId } = tokenIdToRoute(id.toString()) const queryPath = { rmrk: 'chain-rmrk', @@ -75,43 +74,32 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { nft.value = nftEntity - const resources = nftEntity.resources?.map((resource, index) => { - const imageSrc - = resource.meta?.animationUrl - || resource.src - || resource.meta?.image - || resource.thumb - - let animationUrl = resource.meta?.animationUrl - - if (index === 0 && !animationUrl) { - animationUrl = nftEntity.meta.animation_url - } - - return { - ...resource, - src: sanitizeIpfsUrl(imageSrc), - thumb: sanitizeIpfsUrl(resource.thumb || resource.meta?.image), - animation: sanitizeIpfsUrl(animationUrl), - } + historyStore.addHistoryItem({ + id: nft.value.id, + name: nft.value.name, + image: nftImage.value, + collection: nft.value.collection.name, + date: new Date(), + description: nftMetadata.value?.description, + author: nft.value.currentOwner, + price: nft.value.price, + mimeType: nftMimeType.value, + prefix: urlPrefix, }) + }) - const metadata = await getNftMetadata(nftEntity, urlPrefix.value, true) + onBeforeMount(async () => { + const getMetadata = await fetchOdaToken(urlPrefix.value, collectionId, tokenId) + const metadata = getMetadata.metadata nftMetadata.value = metadata - nftResources.value = resources - - nftAnimation.value - = sanitizeIpfsUrl(metadata.animationUrl || metadata.animation_url) || '' - nftAnimationMimeType.value - = metadata.animationUrlMimeType - || (nftAnimation.value && (await getMimeType(nftAnimation.value))) - || '' - nftImage.value = sanitizeIpfsUrl(metadata.image) || '' - nftMimeType.value - = metadata.imageMimeType - || metadata.type - || (nftImage.value && (await getMimeType(nftImage.value))) - || '' + + const mimeType = metadata.image ? await fetchMimeType(metadata.image) : null + const mimeTypeAnimation = metadata.animation_url ? await fetchMimeType(metadata.animation_url) : null + + nftAnimation.value = sanitizeIpfsUrl(metadata.animation_url) + nftAnimationMimeType.value = mimeTypeAnimation?.mime_type || '' + nftImage.value = sanitizeIpfsUrl(metadata.image) + nftMimeType.value = mimeType?.mime_type || '' // use cf-video & replace the video thumbnail if ( @@ -120,16 +108,13 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { ) { // 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', - ) + nftImage.value = sanitizeIpfsUrl(metadata.image, 'cloudflare') + nftAnimation.value = sanitizeIpfsUrl(metadata.animation_url, 'cloudflare') } // serve video from cloudflare stream const streams = await getCloudflareMp4( - metadata.animationUrl || metadata.image, + metadata.animation_url || metadata.image, ) if (streams.uid && streams.video?.default?.percentComplete === 100) { @@ -137,19 +122,6 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { nftImage.value = streams.detail?.thumbnail || '' } } - - historyStore.addHistoryItem({ - id: nft.value.id, - name: nft.value.name, - image: nftImage.value, - collection: nft.value.collection.name, - date: new Date(), - description: nftMetadata.value?.description, - author: nft.value.currentOwner, - price: nft.value.price, - mimeType: nftMimeType.value, - prefix: urlPrefix, - }) }) return { @@ -159,6 +131,5 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { nftAnimationMimeType, nftMimeType, nftMetadata, - nftResources, } } From ac5497ccb41315f8508a8f7b2378f0c1015d4fa1 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 12:52:53 +0700 Subject: [PATCH 02/74] feat(store): create nft store with initial state --- stores/nft.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 stores/nft.ts diff --git a/stores/nft.ts b/stores/nft.ts new file mode 100644 index 0000000000..dc863a0424 --- /dev/null +++ b/stores/nft.ts @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' +import type { NFTMetadata } from '@/services/oda' +import type { NFT } from '@/components/rmrk/service/scheme' + +export const useNftStore = defineStore('nft', { + state: () => ({ + // data from indexer + nft: undefined as NFT | undefined, + + // data from oda-worker + nftMetadata: undefined as NFTMetadata | undefined, + nftImage: '', + nftMimeType: '', + nftAnimation: '', + nftAnimationMimeType: '', + }), +}) From fbd27d52d80edacf3224bc200ee8361828d2e9aa Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 12:53:07 +0700 Subject: [PATCH 03/74] feat(gallery): integrate NFT store with GalleryItem component --- pages/[prefix]/gallery/[id].vue | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pages/[prefix]/gallery/[id].vue b/pages/[prefix]/gallery/[id].vue index fca0bbf2f8..d4b4e8ea11 100644 --- a/pages/[prefix]/gallery/[id].vue +++ b/pages/[prefix]/gallery/[id].vue @@ -2,7 +2,30 @@ - From 196d238949acb9c1841a4cab0cb4fe687c657bd5 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 16:06:47 +0700 Subject: [PATCH 08/74] refactor(media): simplify metadata fetching logic and remove unused functionality --- components/carousel/module/CarouselMedia.vue | 16 -- .../common/ConnectWallet/WalletAssetNfts.vue | 2 +- components/items/ItemsGrid/ItemsGridImage.vue | 12 -- .../ItemsGrid/ItemsGridImageTokenEntity.vue | 16 +- components/profile/activityTab/HistoryRow.vue | 2 +- components/shared/nftCard/NftCard.vue | 6 +- components/shared/nftCard/types.ts | 6 +- composables/useNft.ts | 172 +++--------------- services/imageWorker.ts | 9 - utils/nft.ts | 6 +- 10 files changed, 38 insertions(+), 209 deletions(-) diff --git a/components/carousel/module/CarouselMedia.vue b/components/carousel/module/CarouselMedia.vue index e3573a2465..670770aa8f 100644 --- a/components/carousel/module/CarouselMedia.vue +++ b/components/carousel/module/CarouselMedia.vue @@ -4,12 +4,6 @@ :class="{ 'carousel-media-collection': isCollection }" >
- Card Icon props.item)) - -watch( - () => props.item.image, - async () => { - const nft = await getNftMetadata(props.item, urlPrefix.value) - imageSrc.value = nft.image - }, -) diff --git a/components/common/ConnectWallet/WalletAssetNfts.vue b/components/common/ConnectWallet/WalletAssetNfts.vue index a20e9d19b1..93624f9971 100644 --- a/components/common/ConnectWallet/WalletAssetNfts.vue +++ b/components/common/ConnectWallet/WalletAssetNfts.vue @@ -46,7 +46,7 @@ watchEffect(() => { const nftEntities = data.value.nFTEntities nftEntities.forEach(async (nft, index) => { - const nftMetadata = await getNftMetadata(nft, urlPrefix.value) + const nftMetadata = await getNftMetadata(nft) if (nftMetadata.meta.image) { const mimeType = await getMimeType( diff --git a/components/items/ItemsGrid/ItemsGridImage.vue b/components/items/ItemsGrid/ItemsGridImage.vue index 564f3c44d3..106f3d34f8 100644 --- a/components/items/ItemsGrid/ItemsGridImage.vue +++ b/components/items/ItemsGrid/ItemsGridImage.vue @@ -16,12 +16,9 @@ shoppingCartStore.isItemInCart(nft.id) || listingCartStore.isItemInCart(nft.id), }" - :card-icon="showCardIcon" - :card-icon-src="cardIcon" :show-action-on-hover="!showActionSection" :link="NuxtLink" bind-key="to" - :media-player-cover="mediaPlayerCover" :media-static-video="hideVideoControls" media-hover-on-cover-play > @@ -94,7 +91,6 @@ import { nftToListingCartItem, nftToShoppingCartItem, } from '@/components/common/shoppingCart/utils' -import useNftMetadata, { useNftCardIcon } from '@/composables/useNft' const { placeholder } = useTheme() const { isLogIn, isCurrentOwner } = useAuth() @@ -120,12 +116,6 @@ const props = defineProps<{ skeletonVariant: string }>() -const { showCardIcon, cardIcon } = useNftCardIcon(computed(() => props.nft)) - -const { nft: nftMetadata } = useNftMetadata(props.nft) - -const mediaPlayerCover = computed(() => nftMetadata.value?.image) - const showActionSection = computed(() => { return !isLogIn.value && shoppingCartStore.getItemToBuy?.id === props.nft.id }) @@ -147,8 +137,6 @@ const listLabel = computed(() => { return label + (listingCartStore.isItemInCart(props.nft.id) ? ' ✓' : '') }) -const { nft } = useNft(props.nft) - const isOwner = computed(() => isCurrentOwner(props.nft?.currentOwner)) const openCompletePurcahseModal = () => { diff --git a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue index 10a116e446..ab47a6c10b 100644 --- a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue +++ b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue @@ -14,12 +14,10 @@ shoppingCartStore.isItemInCart(nftForShoppingCart.id) || listingCartStore.isItemInCart(entity.id), }" - :card-icon="showCardIcon" - :card-icon-src="cardIcon" :show-action-on-hover="!showActionSection" :link="NuxtLink" bind-key="to" - :media-player-cover="mediaPlayerCover" + :media-player-cover="entity.image" :media-static-video="hideVideoControls" :lazy-loading="lazyLoading" media-hover-on-cover-play @@ -108,8 +106,6 @@ import { nftToShoppingCartItem, } from '@/components/common/shoppingCart/utils' -import useNftMetadata, { useNftCardIcon } from '@/composables/useNft' - const { urlPrefix } = usePrefix() const { placeholder } = useTheme() const { isLogIn } = useAuth() @@ -141,10 +137,6 @@ const { } = useNftActions(props.entity) const cheapestNFT = ref() -const { showCardIcon, cardIcon } = await useNftCardIcon( - computed(() => props.entity), -) - const linkTo = computed(() => isStack.value ? `/${urlPrefix.value}/collection/${props.entity.collection.id}` @@ -155,10 +147,6 @@ const variant = computed(() => isStack.value ? `stacked-${props.variant}` : props.variant, ) -const { nft: nftMetadata } = useNftMetadata(props.entity) - -const mediaPlayerCover = computed(() => nftMetadata.value?.image) - const showActionSection = computed(() => { return ( !isLogIn.value @@ -190,8 +178,6 @@ const listLabel = computed(() => { return isInCart ? label + ' ✓' : label }) -const { nft: entity } = useNft(props.entity) - const openCompletePurcahseModal = () => { preferencesStore.setCompletePurchaseModal({ isOpen: true, diff --git a/components/profile/activityTab/HistoryRow.vue b/components/profile/activityTab/HistoryRow.vue index 1cbfea70ea..473d6e7c70 100644 --- a/components/profile/activityTab/HistoryRow.vue +++ b/components/profile/activityTab/HistoryRow.vue @@ -231,7 +231,7 @@ const interactionName = computed( const getAvatar = async () => { if (props.event.Item) { - const meta = await getNftMetadata(props.event.Item, urlPrefix.value) + const meta = await getNftMetadata(props.event.Item) image.value = meta.image animationUrl.value = meta.animationUrl } diff --git a/components/shared/nftCard/NftCard.vue b/components/shared/nftCard/NftCard.vue index 14ce36374d..ebf3278fb8 100644 --- a/components/shared/nftCard/NftCard.vue +++ b/components/shared/nftCard/NftCard.vue @@ -19,14 +19,14 @@ :class="{ 'border border-stacked ml-5 mt-5 mr-2': isStacked }" > (nft: T) { - let name = nft.name || nft.meta.name - let description = nft.description || nft.meta.description - let image = sanitizeIpfsUrl(nft.meta.image) - let animationUrl = sanitizeIpfsUrl( - nft.meta.animation_url || nft.meta.animationUrl || '', - ) - let type = nft.meta.type - - if (nft.metadata !== nft.meta.id && nft.metadata) { - const metadataUrl = sanitizeIpfsUrl(nft.metadata) - const res = await getMetadata(metadataUrl) - - name = res.name - description = res.description - image = sanitizeIpfsUrl(res.image) - animationUrl = sanitizeIpfsUrl(res.animationUrl) - type = res.type - } +async function getMetadata(nft) { + const { urlPrefix } = usePrefix() + const getMetadata = await fetchOdaToken(urlPrefix.value, nft.collection.id, nft.sn) + const metadata = getMetadata.metadata return { ...nft, - name: nameWithIndex(name, nft.sn) || nft.id, - description, - image, - animationUrl, - type: type || '', - attributes: getAttributes(nft, nft.metadata || nft.meta), - } -} - -export function useNftCardIcon< - T extends { + image: metadata.image, + animationUrl: metadata.animation_url, meta: { - animationUrl?: string - } - }, ->(nft: Ref) { - const isAudio = ref(false) - const { unlockableIcon } = useIcon() - - const cardIcon = computed(() => { - if (isAudio) { - return '/sound.svg' - } - return unlockableIcon.value - }) - - watchEffect(async () => { - const { isAudio: audio } = await useNftMimeType(nft) - isAudio.value = audio - }) - - return { showCardIcon: isAudio, cardIcon } -} - -export async function useNftMimeType< - T extends { - meta: { - animationUrl?: string - } - }, ->(nft?: Ref) { - if (!nft?.value.meta?.animationUrl) { - return { - isAudio: false, - } - } - - const mimeType = await getMimeType( - sanitizeIpfsUrl(nft.value.meta.animationUrl), - ) - - return { - isAudio: isAudioMimeType(mimeType), + image: metadata.image, + animationUrl: metadata.animation_url, + }, } } -async function getRmrk2Resources(nft: T) { - const thumb = nft.resources?.[0]?.thumb - const src = nft.resources?.[0]?.src - const image = sanitizeIpfsUrl(thumb || src || '') - const type = await getMimeType(image) - - return { - ...(await getGeneralMetadata(nft)), - image, - type, +export async function getNftMetadata( + nft: T, +) { + if (!nft.meta?.image) { + // get metadata from onchain + return await getMetadata(nft) } -} -async function getProcessMetadata(nft: T) { - const metadata = await processSingleMetadata(nft.metadata) - const image = sanitizeIpfsUrl( - metadata.image || metadata.mediaUri || metadata.thumbnailUri || '', + // return metadata from indexer + const name = nft.name || nft.meta.name + const description = nft.description || nft.meta.description + const image = sanitizeIpfsUrl(nft.meta.image) + const animationUrl = sanitizeIpfsUrl( + nft.meta.animation_url || nft.meta.animationUrl || '', ) - const animationUrl = sanitizeIpfsUrl(metadata.animation_url || '') + const type = nft.meta.type return { ...nft, - name: - nameWithIndex(nft.name || metadata.name, nft.sn || metadata.sn) || nft.id, - description: nft.description || metadata.description || '', + name: nameWithIndex(name, nft.sn) || nft.id, + description, image, animationUrl, - type: metadata.type || '', - attributes: getAttributes(nft, metadata), - } -} - -export async function getNftMetadata( - nft: T, - prefix: string, - unify = false, -) { - const ignoreMetadata = [DYNAMIC_METADATA, 'dyndata'] - const checkMetadata = ignoreMetadata.some(item => - (nft.metadata || nft.meta?.id)?.includes(item), - ) - if (unify && !checkMetadata) { - return await getMetadata(sanitizeIpfsUrl(nft.metadata || nft.meta.id)) - } - - // if subsquid already give us the metadata, we don't need to fetch it again - if (nft.meta?.image) { - return await getGeneralMetadata(nft) - } - - // if it's rmrk2, we need to check `resources` field - if (prefix === 'ksm' && nft.resources?.length) { - return await getRmrk2Resources(nft) - } - - return await getProcessMetadata(nft) -} - -export default function useNftMetadata(nft: T) { - const item = ref< - T & { - name: string - description: string - image: string - animationUrl: string - type: string - attributes: unknown - } - >() - const { urlPrefix } = usePrefix() - - onMounted(async () => { - item.value = await getNftMetadata(nft, urlPrefix.value) - }) - - return { - nft: computed(() => item.value), + type: type || '', + attributes: getAttributes(nft, nft.metadata || nft.meta), } } diff --git a/services/imageWorker.ts b/services/imageWorker.ts index 266855e097..f2054efafa 100644 --- a/services/imageWorker.ts +++ b/services/imageWorker.ts @@ -31,12 +31,3 @@ export async function getCloudflareMp4(url): Promise { return video as StreamDownload } - -export async function getMetadata(url: string) { - workerUrl.pathname = '/metadata' - workerUrl.searchParams.set('url', url) - - const metadata = await $fetch(workerUrl.toString()) - - return metadata as unknown as NFTWithMetadata -} diff --git a/utils/nft.ts b/utils/nft.ts index cd5ff2e748..846bc87c6d 100644 --- a/utils/nft.ts +++ b/utils/nft.ts @@ -4,11 +4,7 @@ import type { NFTWithMetadata } from '@/composables/useNft' export const parseNftAvatar = async ( entity: EntityWithMeta, ): Promise => { - const { urlPrefix } = usePrefix() - const meta = await getNftMetadata( - entity as unknown as NFTWithMetadata, - urlPrefix.value, - ) + const meta = await getNftMetadata(entity as unknown as NFTWithMetadata) return meta.image } From a6704e88735e26d160313aa3b6f73c7f1c981119 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 16:15:44 +0700 Subject: [PATCH 09/74] refactor(nft): remove redundant nameWithIndex utility function --- components/collection/drop/ItemsGrid.vue | 1 - components/common/CartItemDetails.vue | 2 +- .../ConfirmPurchaseItemRow.vue | 3 +-- .../shoppingCart/ShoppingCartItemRow.vue | 4 ++-- components/common/shoppingCart/utils.ts | 9 ++++----- components/gallery/GalleryItem.vue | 5 +---- components/items/Items.vue | 1 - components/items/ItemsGrid/ItemsGrid.vue | 3 --- components/items/ItemsGrid/ItemsGridImage.vue | 2 -- .../ItemsGrid/ItemsGridImageTokenEntity.vue | 2 -- components/profile/ProfileDetail.vue | 1 - components/profile/activityTab/HistoryRow.vue | 5 ++--- components/shared/nftCard/NftCard.vue | 3 +-- components/shared/nftCard/NftMediaInfo.vue | 14 +------------ .../useCollectionActivity.ts | 3 +-- composables/useNft.ts | 2 +- utils/carousel.ts | 3 +-- utils/nft.ts | 20 ------------------- 18 files changed, 16 insertions(+), 67 deletions(-) diff --git a/components/collection/drop/ItemsGrid.vue b/components/collection/drop/ItemsGrid.vue index c2637fb424..998e5e2fad 100644 --- a/components/collection/drop/ItemsGrid.vue +++ b/components/collection/drop/ItemsGrid.vue @@ -26,7 +26,6 @@ - {{ nameWithIndex(nft.name, nft.sn) }} + {{ nft.name }}
- {{ nameWithIndex(name, sn) }} + {{ name }}
import CommonTokenMoney from '@/components/shared/CommonTokenMoney.vue' -import { nameWithIndex } from '@/utils/nft' defineProps<{ name: string diff --git a/components/common/shoppingCart/ShoppingCartItemRow.vue b/components/common/shoppingCart/ShoppingCartItemRow.vue index e070097f76..7fceb2ba42 100644 --- a/components/common/shoppingCart/ShoppingCartItemRow.vue +++ b/components/common/shoppingCart/ShoppingCartItemRow.vue @@ -23,7 +23,7 @@ class="font-bold text-text-color whitespace-nowrap is-clipped text-ellipsis" @click="emit('click-item')" > - {{ nameWithIndex(nft.name, nft.sn) }} + {{ nft.name }}
@@ -60,7 +60,7 @@ import { useElementHover } from '@vueuse/core' import { NeoButton } from '@kodadot1/brick' import type { ShoppingCartItem } from './types' import BasicImage from '@/components/shared/view/BasicImage.vue' -import { nameWithIndex, parseNftAvatar } from '@/utils/nft' +import { parseNftAvatar } from '@/utils/nft' import CommonTokenMoney from '@/components/shared/CommonTokenMoney.vue' const { urlPrefix } = usePrefix() diff --git a/components/common/shoppingCart/utils.ts b/components/common/shoppingCart/utils.ts index 0a26a181fe..a3dd503157 100644 --- a/components/common/shoppingCart/utils.ts +++ b/components/common/shoppingCart/utils.ts @@ -5,7 +5,6 @@ import { useFiatStore } from '@/stores/fiat' import { sum } from '@/utils/math' import type { NFT, TokenId } from '@/components/rmrk/service/scheme' import { chainPropListOf } from '@/utils/config/chain.config' -import { nameWithIndex } from '@/utils/nft' import type { MakingOfferItem } from '@/components/offer/types' export const prefixToToken = { @@ -51,7 +50,7 @@ export const nftToShoppingCartItem = (nft: NFT): ShoppingCartItem => { const { urlPrefix } = usePrefix() return { id: nft.id, - name: nameWithIndex(nft.name, nft.sn), + name: nft.name, currentOwner: nft.currentOwner, price: nft.price ?? '0', urlPrefix: urlPrefix.value, @@ -75,7 +74,7 @@ export const nftToListingCartItem = ( return { id: nft.id, - name: nameWithIndex(nft.name, nft.sn), + name: nft.name, price: nft.price ?? '0', urlPrefix: urlPrefix.value, collection: { @@ -96,7 +95,7 @@ export const nftToOfferItem = (nft: NFT & TokenId): MakingOfferItem => { return { id: nft.id, - name: nameWithIndex(nft.name, nft.sn), + name: nft.name, price: nft.price ?? '0', urlPrefix: urlPrefix.value, collection: nft.collection, @@ -113,7 +112,7 @@ export const shoppingCartItemToListingCartItem = ( ): ListCartItem => { return { id: item.id, - name: nameWithIndex(item.name, item.sn), + name: item.name, price: item.price ?? '0', urlPrefix: item.urlPrefix, collection: { ...item.collection, floor }, diff --git a/components/gallery/GalleryItem.vue b/components/gallery/GalleryItem.vue index cf9c3a0233..d953b1504b 100644 --- a/components/gallery/GalleryItem.vue +++ b/components/gallery/GalleryItem.vue @@ -257,10 +257,7 @@ onMounted(() => { const { isUnlockable, unlockLink } = useUnlockable(collection) const title = computed(() => - nameWithIndex( - nftMetadata.value?.name || nft.value?.name || '', - nft.value?.sn, - ), + nftMetadata.value?.name || nft.value?.name || '', ) const seoDescription = computed( () => convertMarkdownToText(nftMetadata.value?.description) || '', diff --git a/components/items/Items.vue b/components/items/Items.vue index ac77f45d95..ceffca4db5 100644 --- a/components/items/Items.vue +++ b/components/items/Items.vue @@ -20,7 +20,6 @@
0" :variant="variant" :hide-media-info="hideMediaInfo" - :display-name-with-sn="displayNameWithSn" :show-timestamp="showTimestamp" :collection-popover-hide="collectionPopoverHide" :lazy-loading="lazyLoading" @@ -109,7 +108,6 @@ const props = defineProps<{ hideMediaInfo?: boolean hideAction?: boolean hideVideoControls?: boolean - displayNameWithSn?: boolean showTimestamp?: boolean collectionPopoverHide?: boolean lazyLoading?: boolean diff --git a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue index ab47a6c10b..eed2fc528d 100644 --- a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue +++ b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue @@ -8,7 +8,6 @@ :show-price="isAvailableToBuy" :variant="variant" :hide-media-info="hideMediaInfo" - :display-name-with-sn="displayNameWithSn" :class="{ 'in-cart-border': shoppingCartStore.isItemInCart(nftForShoppingCart.id) @@ -122,7 +121,6 @@ const props = defineProps<{ hideMediaInfo?: boolean hideAction?: boolean hideVideoControls?: boolean - displayNameWithSn?: boolean lazyLoading?: boolean skeletonVariant: string }>() diff --git a/components/profile/ProfileDetail.vue b/components/profile/ProfileDetail.vue index dca062fd11..f64db4af6d 100644 --- a/components/profile/ProfileDetail.vue +++ b/components/profile/ProfileDetail.vue @@ -391,7 +391,6 @@ diff --git a/components/profile/activityTab/HistoryRow.vue b/components/profile/activityTab/HistoryRow.vue index 473d6e7c70..3e65ba466b 100644 --- a/components/profile/activityTab/HistoryRow.vue +++ b/components/profile/activityTab/HistoryRow.vue @@ -23,7 +23,7 @@ :to="`/${urlPrefix}/gallery/${event.Item.id}`" > - {{ nameWithIndex(event.Item.name, event.Item.sn) }} + {{ event.Item.name }}
@@ -129,7 +129,7 @@ :to="`/${urlPrefix}/gallery/${event.Item.id}`" > - {{ nameWithIndex(event.Item.name, event.Item.sn) }} + {{ event.Item.name }} @@ -208,7 +208,6 @@ import { import EventTag from '@/components/collection/activity/events/eventRow/EventTag.vue' import BlockExplorerLink from '@/components/shared/BlockExplorerLink.vue' import CommonTokenMoney from '@/components/shared/CommonTokenMoney.vue' -import { nameWithIndex } from '@/utils/nft' const props = defineProps<{ event: Event diff --git a/components/shared/nftCard/NftCard.vue b/components/shared/nftCard/NftCard.vue index ebf3278fb8..2dad9a3e5d 100644 --- a/components/shared/nftCard/NftCard.vue +++ b/components/shared/nftCard/NftCard.vue @@ -18,6 +18,7 @@ class="relative" :class="{ 'border border-stacked ml-5 mt-5 mr-2': isStacked }" > + {{ nft }} (), { diff --git a/components/shared/nftCard/NftMediaInfo.vue b/components/shared/nftCard/NftMediaInfo.vue index b67892a9b8..fcbe8b75a2 100644 --- a/components/shared/nftCard/NftMediaInfo.vue +++ b/components/shared/nftCard/NftMediaInfo.vue @@ -67,7 +67,6 @@ import { formatDistanceToNowStrict } from 'date-fns' import type { NeoNFT, NftCardVariant } from './types' import CommonTokenMoney from '@/components/shared/CommonTokenMoney.vue' import { getChainNameByPrefix } from '@/utils/chain' -import { nameWithIndex } from '@/utils/nft' const props = withDefaults( defineProps<{ @@ -78,7 +77,6 @@ const props = withDefaults( collectionPopoverHide?: boolean collectionPopoverShowDelay?: number variant?: NftCardVariant - displayNameWithSn?: boolean }>(), { collectionPopoverShowDelay: 500, @@ -86,17 +84,7 @@ const props = withDefaults( }, ) -const name = computed(() => { - const originalName = props.nft.name - if (!props.displayNameWithSn) { - return originalName - } - const sn = isTokenEntity(props.nft) - ? props.nft?.cheapest?.id?.split('-')[1] - : props.nft?.sn - - return sn ? nameWithIndex(props.nft.name, sn) : originalName -}) +const name = computed(() => props.nft.name) const isMinimal = computed(() => props.variant ? props.variant.includes('minimal') : false, ) diff --git a/composables/collectionActivity/useCollectionActivity.ts b/composables/collectionActivity/useCollectionActivity.ts index 2b3df26171..1879987e84 100644 --- a/composables/collectionActivity/useCollectionActivity.ts +++ b/composables/collectionActivity/useCollectionActivity.ts @@ -2,7 +2,6 @@ import { useQuery } from '@tanstack/vue-query' import type { Prefix } from '@kodadot1/static' import type { Flippers, InteractionWithNFT, Offer, Owners } from './types' import { getFlippers, getOwners } from './helpers' -import { nameWithIndex } from '@/utils/nft' export const useCollectionActivity = ({ collectionId, @@ -47,7 +46,7 @@ export const useCollectionActivity = ({ const nfts = result.collection?.nfts.map(nft => ({ ...nft, - name: nameWithIndex(nft?.name, nft?.sn), + name: nft?.name, })) ?? [] // flat events for chart const interactions: InteractionWithNFT[] = nfts diff --git a/composables/useNft.ts b/composables/useNft.ts index 005d5d3416..55464b1ac1 100644 --- a/composables/useNft.ts +++ b/composables/useNft.ts @@ -96,7 +96,7 @@ export async function getNftMetadata( return { ...nft, - name: nameWithIndex(name, nft.sn) || nft.id, + name, description, image, animationUrl, diff --git a/utils/carousel.ts b/utils/carousel.ts index 617ef11393..fa0d0e9d4a 100644 --- a/utils/carousel.ts +++ b/utils/carousel.ts @@ -1,6 +1,5 @@ import { formatDistanceToNow } from 'date-fns' import type { LastEvent } from '@/utils/types/types' -import { nameWithIndex } from '@/utils/nft' import type { CarouselNFT } from '@/components/base/types' import { fetchNFTMetadata, getSanitizer, sanitizeIpfsUrl } from '@/utils/ipfs' /** @@ -34,7 +33,7 @@ export const formatNFT = (nfts, chain?: string): CarouselNFT[] => { image: metaImage && sanitizeIpfsUrl(metaImage), animationUrl: metaAnimationUrl && sanitizeIpfsUrl(metaAnimationUrl), collectionName: nft.collectionName || nft.collection?.name || '--', - name: nameWithIndex(name, nft.sn), + name: name, collectionId: nft.collectionId || nft.collection?.id, chain: chain || urlPrefix.value, } diff --git a/utils/nft.ts b/utils/nft.ts index 846bc87c6d..20af5f3ced 100644 --- a/utils/nft.ts +++ b/utils/nft.ts @@ -7,23 +7,3 @@ export const parseNftAvatar = async ( const meta = await getNftMetadata(entity as unknown as NFTWithMetadata) return meta.image } - -const trimLeadingZeros = (of: number | string) => { - const trimmedString = of.toString().replace(/^0+(?=\d)/, '') - - return typeof of === 'number' ? Number(trimmedString) : trimmedString -} - -const suffixRegex = /#\d+$/ -export const nameWithIndex = (name: string = '', sn?: string) => { - if (!name || !sn) { - return name - } - - // display sn if nftId is less than 4 digits - if (sn.length >= 4) { - return name - } - - return suffixRegex.test(name) ? name : `${name} #${trimLeadingZeros(sn)}` -} From e50695dc48fdddb5b6ba736a196cafbc04a2e338 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Thu, 5 Sep 2024 16:23:51 +0700 Subject: [PATCH 10/74] refactor(nftCard): remove redundant text rendering in template --- components/shared/nftCard/NftCard.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/components/shared/nftCard/NftCard.vue b/components/shared/nftCard/NftCard.vue index 2dad9a3e5d..7285cf17f7 100644 --- a/components/shared/nftCard/NftCard.vue +++ b/components/shared/nftCard/NftCard.vue @@ -18,7 +18,6 @@ class="relative" :class="{ 'border border-stacked ml-5 mt-5 mr-2': isStacked }" > - {{ nft }} Date: Thu, 5 Sep 2024 16:30:17 +0700 Subject: [PATCH 11/74] fix(gallery): add early return for remark chain in useGalleryItem --- components/gallery/useGalleryItem.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index c468366275..edb1667e90 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -88,7 +88,12 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { }) }) + const { isRemark } = useIsChain(usePrefix().urlPrefix) onBeforeMount(async () => { + if (isRemark.value) { + return + } + const getMetadata = await fetchOdaToken(urlPrefix.value, collectionId, tokenId) const metadata = getMetadata.metadata nftMetadata.value = metadata From 60ff2c98d79016da7b0dd487819accb068fbc998 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Fri, 6 Sep 2024 13:24:11 +0700 Subject: [PATCH 12/74] fix(gallery): handle missing metadata in useGalleryItem function --- components/gallery/useGalleryItem.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index edb1667e90..737d38cf02 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -96,6 +96,11 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { const getMetadata = await fetchOdaToken(urlPrefix.value, collectionId, tokenId) const metadata = getMetadata.metadata + + if (!metadata) { + return + } + nftMetadata.value = metadata const mimeType = metadata.image ? await fetchMimeType(metadata.image) : null From eba17e0abd639e4f9e7f290b8b3e368bae96e6ff Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Fri, 6 Sep 2024 14:15:49 +0700 Subject: [PATCH 13/74] refactor(routing): implement redirect to gallery page using script setup --- pages/[prefix]/detail/[id].vue | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pages/[prefix]/detail/[id].vue b/pages/[prefix]/detail/[id].vue index fca0bbf2f8..b09e25ed69 100644 --- a/pages/[prefix]/detail/[id].vue +++ b/pages/[prefix]/detail/[id].vue @@ -2,8 +2,12 @@ - From 93e49236c2348bd283cd9d86ee9e860bb61a70ea Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Sat, 7 Sep 2024 13:00:23 +0700 Subject: [PATCH 14/74] test(e2e): update collection test for new collection details --- tests/e2e/collection.spec.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/e2e/collection.spec.ts b/tests/e2e/collection.spec.ts index 39d2d7dee3..da779432f7 100644 --- a/tests/e2e/collection.spec.ts +++ b/tests/e2e/collection.spec.ts @@ -1,8 +1,11 @@ import { expect, test } from './fixtures' -const COLLECTION_ADDRESS_PATH = '/ahp/collection/76/' -const COLLECTION_NAME = 'Assemblies' +const COLLECTION_ADDRESS_PATH = '/ahp/collection/127/' +const COLLECTION_NAME = '.hdd' const COLLECTION_OWNER = '15CoYMEnJhhWHvdEPXDuTBnZKXwrJzMQdcMwcHGsVx5kXYvW' +const COLLECTION_DESCRIPTION = '.hdd is a series of generative art inspired by PC components' +const COLLECTION_SEARCH = '127' +const COLLECTION_SEARCH_RESULT = '.hdd #127' test('Collection interactions', async ({ page, Commands }) => { await page.goto(COLLECTION_ADDRESS_PATH) @@ -15,7 +18,7 @@ test('Collection interactions', async ({ page, Commands }) => { await page.getByTestId('description-show-less-more-button').click() // collection description await expect(page.getByTestId('collection-description')).toContainText( - 'robotic', + COLLECTION_DESCRIPTION, ) }) @@ -69,13 +72,13 @@ test('Collection interactions', async ({ page, Commands }) => { await page.getByTestId('filter-checkbox-buynow').nth(1).click() await page .locator('[data-testid="search-bar-input"] >> visible = true') - .fill('26') + .fill(COLLECTION_SEARCH) await page.keyboard.press('Enter') await Commands.scrollDownSlow() await expect( page.locator('[class="infinite-scroll-item"]').first(), ).toBeVisible() - await expect(page.getByTestId('nft-name')).toHaveText('Assemblies #26') + await expect(page.getByTestId('nft-name')).toHaveText(COLLECTION_SEARCH_RESULT) }) // art view From 99277f6d88a69c2ff7c9c15decb8c17c492fd6eb Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Sat, 7 Sep 2024 13:10:41 +0700 Subject: [PATCH 15/74] test(search): update search term and landing path in e2e test --- tests/e2e/search.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/e2e/search.spec.ts b/tests/e2e/search.spec.ts index 645f0742fe..68b139099a 100644 --- a/tests/e2e/search.spec.ts +++ b/tests/e2e/search.spec.ts @@ -1,18 +1,19 @@ import { expect, test } from './fixtures' -const LANDING_PATH = '/ksm' +const LANDING_PATH = '/ahp' +const SEARCH_TERM = 'hdd' test('Check if search provide results', async ({ page }) => { await page.goto(LANDING_PATH) // Search term - await test.step('Search for the term Waifu and hover over results', async () => { + await test.step('Search for the term ' + SEARCH_TERM + ' and hover over results', async () => { await expect( page.locator('[data-testid="search-bar"] >> visible = true'), ).toBeVisible() await page .locator('[data-testid="search-bar-input"] >> visible = true') - .fill('waifu') + .fill(SEARCH_TERM) await page .locator('[data-testid="search-suggestion-container"] >> visible = true') .hover() From c2fb564743dec98c1a720b8f7e74903b83e48926 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Sat, 7 Sep 2024 17:33:22 +0700 Subject: [PATCH 16/74] fix(items-grid): display correct entity data from chain --- .../ItemsGrid/ItemsGridImageTokenEntity.vue | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue index eed2fc528d..9c14398a87 100644 --- a/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue +++ b/components/items/ItemsGrid/ItemsGridImageTokenEntity.vue @@ -1,7 +1,7 @@ diff --git a/locales/en.json b/locales/en.json index 0fd11b81e2..27dd3bbfaf 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1337,6 +1337,7 @@ "progress": "In Progress", "requiredDeposit": "A deposit of {0} is required to create a {1}. Please note, this initial deposit is refundable.", "royalty": { + "custom": "Pay royalties to a specific account", "mine": "Pay royalties to the creator account", "rate": "Royalty Rate (%)", "receiver": "Destination account for royalty" From 6f3ed25d60b2b9df9e17ae446da65312bace7c24 Mon Sep 17 00:00:00 2001 From: hassnian <44554284+hassnian@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:39:13 +0500 Subject: [PATCH 71/74] fix(useMetaTransaction.ts): sync wallet chain before signing --- .../transaction/evm/useMetaTransaction.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/composables/transaction/evm/useMetaTransaction.ts b/composables/transaction/evm/useMetaTransaction.ts index c4460d08d3..fb7a823848 100644 --- a/composables/transaction/evm/useMetaTransaction.ts +++ b/composables/transaction/evm/useMetaTransaction.ts @@ -1,6 +1,6 @@ import { type Address, TransactionExecutionError } from 'viem' import { simulateContract, waitForTransactionReceipt, writeContract } from '@wagmi/core' -import { useConfig } from '@wagmi/vue' +import { useChainId, useConfig, useSwitchChain } from '@wagmi/vue' import type { Abi } from '../types' import useTransactionStatus from '@/composables/useTransactionStatus' @@ -33,13 +33,15 @@ export type EvmHowAboutToExecuteOnSuccessParam = { */ export default function useEvmMetaTransaction() { const { $i18n } = useNuxtApp() - const { isLoading, initTransactionLoader, status, stopLoader } - = useTransactionStatus() + const { isLoading, initTransactionLoader, status, stopLoader } = useTransactionStatus() + const { switchChainAsync: switchChain } = useSwitchChain() + const { urlPrefix: prefix } = usePrefix() + const chainId = useChainId() + const wagmiConfig = useConfig() + const tx = ref() const isError = ref(false) - const wagmiConfig = useConfig() - const howAboutToExecute: EvmHowAboutToExecute = async ({ account, functionName, @@ -51,6 +53,8 @@ export default function useEvmMetaTransaction() { onError, }: EvmHowAboutToExecuteParam): Promise => { try { + await syncWalletChain() + const { request } = await simulateContract(wagmiConfig, { account, address, @@ -79,6 +83,13 @@ export default function useEvmMetaTransaction() { } } + const syncWalletChain = async () => { + const chain = PREFIX_TO_CHAIN[prefix.value] + if (chain && chainId.value !== chain.id) { + await switchChain({ chainId: chain.id }) + } + } + const successCb = (onSuccess?: (param: EvmHowAboutToExecuteOnSuccessParam) => void) => async ({ txHash, blockNumber }) => { From 0dfd77ec01a35ab082b5f8b803c80446421bc7eb Mon Sep 17 00:00:00 2001 From: hassnian <44554284+hassnian@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:27:13 +0500 Subject: [PATCH 72/74] ref(SkeletonLoader.vue): typo --- components/shared/SkeletonLoader.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/shared/SkeletonLoader.vue b/components/shared/SkeletonLoader.vue index 898417d56c..6c6b5eb85c 100644 --- a/components/shared/SkeletonLoader.vue +++ b/components/shared/SkeletonLoader.vue @@ -86,10 +86,10 @@ const calculateTextContainerWidth = () => { nextTick(() => { const title = titleRef.value?.clientWidth || 0 - const substitle = subtitleRef.value?.clientWidth || 0 - const subtitlePlusDots = substitle + DOTS_PLUS_MARGIN_WIDTH + const subtitle = subtitleRef.value?.clientWidth || 0 + const subtitlePlusDots = subtitle + DOTS_PLUS_MARGIN_WIDTH - if (substitle && subtitlePlusDots > title) { + if (subtitle && subtitlePlusDots > title) { textContainerWidth.value = `${subtitlePlusDots}px` } }) From 557698dd030c87a4c0aac5c7e7eb76b2b39ab006 Mon Sep 17 00:00:00 2001 From: hassnian <44554284+hassnian@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:33:17 +0500 Subject: [PATCH 73/74] fix(OfferRow.vue): `isExpired` typo --- components/profile/activityTab/OfferRow.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/profile/activityTab/OfferRow.vue b/components/profile/activityTab/OfferRow.vue index 5ad756f6f0..63f3ff0ebd 100644 --- a/components/profile/activityTab/OfferRow.vue +++ b/components/profile/activityTab/OfferRow.vue @@ -145,7 +145,7 @@ {{ blank }}