Compare commits

..
2 Commits
Author SHA1 Message Date
Chris Troutner 0a33c9a8c7 Merge pull request #85 from Permissionless-Software-Foundation/dh-nft-loading
fix(NFTs): Refine token metadata loading and caching
2025-12-02 08:24:36 -08:00
Daniel Gonzalez 5451d0abe3 fix(NFTs): Refine token metadata loading and caching 2025-12-02 10:36:47 -04:00
4 changed files with 91 additions and 26 deletions
@@ -10,7 +10,6 @@ import AsyncLoad from '../../../services/async-load'
function BuyButton (props) {
const { token, appData, onSuccess } = props
console.log('props: ', props)
const [show, setShow] = useState(false) // show the modal
const [onFetch, setOnFetch] = useState(false) // show the spinner
const [error, setError] = useState(false) // show the error message
+37 -12
View File
@@ -74,7 +74,8 @@ function NftsForSale (props) {
return offer
}
}, [appData])
// Function to process token metadata (iconUrl , userData).
// Function to process token metadata (iconUrl , userData , tokenData).
const processOfferMetadata = useCallback(async (offer) => {
try {
// Token icon
@@ -82,7 +83,15 @@ function NftsForSale (props) {
offer.icon = offer.tokenIconUrl
offer.iconAlreadyDownloaded = true
offer.userData = JSON.parse(offer.userDataStr)
offer.mutableFromBackend = true
offer.mutableDataSource = 'backend'
}
// tokenData
if (offer.tokenData) {
offer.tokenDataFromBackend = true
offer.tokenDataSource = 'backend'
}
return offer
} catch (error) {
return offer
@@ -126,8 +135,10 @@ function NftsForSale (props) {
for (let i = 0; i < tokens.length; i++) {
const thisToken = tokens[i]
// data does not need to be downloaded, so continue with the next one
if (thisToken.dataAlreadyDownloaded) continue
// Skip if already has tokenData (from backend or cache)
if (thisToken.dataAlreadyDownloaded || thisToken.tokenData) {
continue
}
// Try to get token data.
const tokenData = await appData.wallet.getTokenData(thisToken.tokenId)
@@ -135,6 +146,7 @@ function NftsForSale (props) {
if (tokenData) {
// Set data to the token object , this can be used to display the token name in the token card component.
thisToken.tokenData = tokenData
thisToken.tokenDataSource = 'blockchain'
}
// Mark token to prevent fetch token data again.
@@ -195,6 +207,7 @@ function NftsForSale (props) {
// Set the icon url to the token , this can be used to display the icon in the token card component.
thisToken.icon = iconUrl
thisToken.userData = userData
thisToken.mutableDataSource = 'blockchain'
}
// Mark token to prevent fetch token icon again.
@@ -210,15 +223,23 @@ function NftsForSale (props) {
// Check if token data exists in the cache and add it to the tokens object.
const reviewNftCachedData = useCallback(async (offers) => {
const cacheData = appData.nftForSaleCacheData
console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
// console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
// Map all offers
for (let i = 0; i < offers.length; i++) {
const thisToken = offers[i]
// console.log(`Review offers:`,thisToken)
const cacheToken = cacheData[thisToken.tokenId]
// If cache data exists, add it to the token object
if (cacheToken) {
thisToken.tokenData = cacheToken.tokenData
// Only use cache if backend didn't provide the data
if (cacheToken && !thisToken.icon) {
thisToken.icon = cacheToken.tokenIcon
thisToken.mutableDataSource = 'cache'
}
if (cacheToken && !thisToken.tokenData) {
thisToken.tokenData = cacheToken.tokenData
thisToken.tokenDataSource = 'cache'
}
}
}, [appData])
@@ -228,13 +249,17 @@ function NftsForSale (props) {
// Map all offers
for (let i = 0; i < offers.length; i++) {
const thisToken = offers[i]
// save token icon and token data in cache
const newTokenCacheData = {
tokenIcon: thisToken.icon,
tokenData: thisToken.tokenData
}
console.log('newTokenCacheData: ', newTokenCacheData)
// Only cache if we fetched it ourselves (not from backend)
// Backend data is already "cached" server-side, no need to duplicate
if (thisToken.tokenDataFromBackend && thisToken.mutableFromBackend) continue
const newTokenCacheData = {}
if (!thisToken.tokenDataFromBackend) newTokenCacheData.tokenData = thisToken.tokenData
if (!thisToken.mutableFromBackend) newTokenCacheData.tokenIcon = thisToken.icon
appData.updateNFTCachedData(thisToken.tokenId, newTokenCacheData)
}
}, [appData])
@@ -13,6 +13,7 @@ import BuyButton from './buy-button'
import SellerProfile from './seller-profile'
function TokenCard (props) {
const { token, appData, handleRefresh, hideBuyBtn } = props
console.log('token', token)
const [icon, setIcon] = useState(token.icon)
const [tokenData, setTokenData] = useState(token.tokenData)
@@ -40,6 +40,29 @@ function NFTForSale (props) {
}
}, [appData])
// Function to process token metadata (iconUrl , userData , tokenData).
const processOfferMetadata = useCallback(async (offer) => {
try {
// Token icon
if (offer.tokenIconUrl) {
offer.icon = offer.tokenIconUrl
offer.iconAlreadyDownloaded = true
offer.userData = JSON.parse(offer.userDataStr)
offer.mutableFromBackend = true
offer.mutableDataSource = 'backend'
}
// tokenData
if (offer.tokenData) {
offer.tokenDataFromBackend = true
offer.tokenDataSource = 'backend'
}
return offer
} catch (error) {
return offer
}
}, [])
// Fetch offers
const getNftOffers = useCallback(async (page = 0) => {
try {
@@ -57,7 +80,8 @@ function NFTForSale (props) {
for (let i = 0; i < rawOffers.length; i++) {
const offer = rawOffers[i]
const processedOffer = await processTokenData(offer)
processedOffers.push(processedOffer)
const processedOfferMetadata = await processOfferMetadata(processedOffer)
processedOffers.push(processedOfferMetadata)
}
setOffersAreLoaded(true)
@@ -68,7 +92,7 @@ function NFTForSale (props) {
setOffersAreLoaded(true)
throw err
}
}, [processTokenData, profileAddresses])
}, [processTokenData, processOfferMetadata, profileAddresses])
// This function loads the token data .
const lazyLoadTokenData = useCallback(async (tokens) => {
@@ -78,8 +102,10 @@ function NFTForSale (props) {
for (let i = 0; i < tokens.length; i++) {
const thisToken = tokens[i]
// data does not need to be downloaded, so continue with the next one
if (thisToken.dataAlreadyDownloaded) continue
// Skip if already has tokenData (from backend or cache)
if (thisToken.dataAlreadyDownloaded || thisToken.tokenData) {
continue
}
// Try to get token data.
const tokenData = await appData.wallet.getTokenData(thisToken.tokenId)
@@ -87,6 +113,7 @@ function NFTForSale (props) {
if (tokenData) {
// Set data to the token object , this can be used to display the token name in the token card component.
thisToken.tokenData = tokenData
thisToken.tokenDataSource = 'blockchain'
}
// Mark token to prevent fetch token data again.
@@ -146,6 +173,7 @@ function NFTForSale (props) {
// Set the icon url to the token , this can be used to display the icon in the token card component.
thisToken.icon = iconUrl
thisToken.userData = userData
thisToken.mutableDataSource = 'blockchain'
}
// Mark token to prevent fetch token icon again.
@@ -161,15 +189,23 @@ function NFTForSale (props) {
// Check if token data exists in the cache and add it to the tokens object.
const reviewNftCachedData = useCallback(async (offers) => {
const cacheData = appData.nftForSaleCacheData
console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
// console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
// Map all offers
for (let i = 0; i < offers.length; i++) {
const thisToken = offers[i]
// console.log(`Review offers:`,thisToken)
const cacheToken = cacheData[thisToken.tokenId]
// If cache data exists, add it to the token object
if (cacheToken) {
thisToken.tokenData = cacheToken.tokenData
// Only use cache if backend didn't provide the data
if (cacheToken && !thisToken.icon) {
thisToken.icon = cacheToken.tokenIcon
thisToken.mutableDataSource = 'cache'
}
if (cacheToken && !thisToken.tokenData) {
thisToken.tokenData = cacheToken.tokenData
thisToken.tokenDataSource = 'cache'
}
}
}, [appData])
@@ -179,13 +215,17 @@ function NFTForSale (props) {
// Map all offers
for (let i = 0; i < offers.length; i++) {
const thisToken = offers[i]
// save token icon and token data in cache
const newTokenCacheData = {
tokenIcon: thisToken.icon,
tokenData: thisToken.tokenData
}
console.log('newTokenCacheData: ', newTokenCacheData)
// Only cache if we fetched it ourselves (not from backend)
// Backend data is already "cached" server-side, no need to duplicate
if (thisToken.tokenDataFromBackend && thisToken.mutableFromBackend) continue
const newTokenCacheData = {}
if (!thisToken.tokenDataFromBackend) newTokenCacheData.tokenData = thisToken.tokenData
if (!thisToken.mutableFromBackend) newTokenCacheData.tokenIcon = thisToken.icon
appData.updateNFTCachedData(thisToken.tokenId, newTokenCacheData)
}
}, [appData])