diff --git a/src/components/app-body/slp-tokens/index.js b/src/components/app-body/slp-tokens/index.js index 81c3939..9fa58e6 100644 --- a/src/components/app-body/slp-tokens/index.js +++ b/src/components/app-body/slp-tokens/index.js @@ -15,6 +15,7 @@ import RefreshTokenBalance from './refresh-tokens' const SlpTokens = (props) => { const [appData, setAppData] = useState(props.appData) const [iconsAreLoaded, setIconsAreLoaded] = useState(false) + const [dataAreLoaded, setDataAreLoaded] = useState(false) const [tokens, setTokens] = useState([]) const refreshTokenButtonRef = React.useRef() @@ -44,37 +45,68 @@ const SlpTokens = (props) => { return url } + // This function loads the token data . + const lazyLoadTokenData = useCallback(async (tokens) => { + try { + setDataAreLoaded(false) + // map each token and fetch the token data + 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 + + // Try to get token data. + const tokenData = await appData.wallet.getTokenData(thisToken.tokenId) + console.log('tokenData', tokenData) + 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 + } + + // Mark token to prevent fetch token data again. + thisToken.dataAlreadyDownloaded = true + } + + setDataAreLoaded(true) + } catch (error) { + setDataAreLoaded(true) + } + }, [appData]) + // Fetch mutable data if it exist and get the token icon url - const fetchTokenIcon = useCallback(async (token) => { + const fetchTokenMutableData = useCallback(async (token) => { try { // Get the token data - const tokenData = await appData.wallet.getTokenData(token.tokenId) + const tokenData = token.tokenData + if (!tokenData.mutableData) return false // Return false if no mutable data + // Get the token icon from the mutable data const cid = parseCid(tokenData.mutableData) console.log('mutable data cid', cid) const { json } = await appData.wallet.cid2json({ cid }) - + console.log('json: ', json) if (!json) return false - const iconUrl = json.tokenIcon + let iconUrl = json.tokenIcon + + if (json.fullSizedUrl && json.fullSizedUrl.includes('http')) { + iconUrl = json.fullSizedUrl + } + const userData = json.userData // Return icon url - return iconUrl + return { iconUrl, userData } } catch (error) { return false } }, [appData]) // This function loads the token icons from the ipfs gateways. - const lazyLoadTokenIcons = useCallback(async () => { + const lazyLoadMutableData = useCallback(async (tokens) => { try { setIconsAreLoaded(false) - - const tokens = appData.bchWalletState.slpTokens - - setTokens(tokens) // update token state - // map each token and fetch the icon url for (let i = 0; i < tokens.length; i++) { const thisToken = tokens[i] @@ -83,28 +115,35 @@ const SlpTokens = (props) => { if (thisToken.iconAlreadyDownloaded) continue // Try to get token icon url from mutable data. - const iconUrl = await fetchTokenIcon(thisToken) + const { iconUrl, userData } = await fetchTokenMutableData(thisToken) console.log('iconUrl', iconUrl) if (iconUrl) { // Set the icon url to the token , this can be used to display the icon in the token card component. thisToken.icon = iconUrl + thisToken.tokenData.userData = userData } // Mark token to prevent fetch token icon again. thisToken.iconAlreadyDownloaded = true } - appData.updateBchWalletState({ walletObj: { slpTokens: tokens }, appData }) setIconsAreLoaded(true) } catch (error) { setIconsAreLoaded(true) } - }, [appData, fetchTokenIcon]) + }, [fetchTokenMutableData]) + + const loadData = useCallback(async () => { + const tokens = appData.bchWalletState.slpTokens + setTokens(tokens) + await lazyLoadTokenData(tokens) + await lazyLoadMutableData(tokens) + }, [appData, lazyLoadTokenData, lazyLoadMutableData]) // Start to load the token icons when the component is mounted useEffect(() => { - lazyLoadTokenIcons() - }, [lazyLoadTokenIcons]) + loadData() + }, [loadData]) // Generate the token cards for each token in the wallet. const generateCards = () => { @@ -127,7 +166,7 @@ const SlpTokens = (props) => { @@ -138,8 +177,16 @@ const SlpTokens = (props) => { - { - !iconsAreLoaded && ( + {/** Show spinner info if tokens are loaded but data is not loaded */ + !dataAreLoaded && ( +
+ Loading Token Data + +
+ ) + } + {/** Show spinner info if tokens are loaded but icons are not loaded */ + dataAreLoaded && !iconsAreLoaded && (
Loading Token Icons diff --git a/src/components/app-body/slp-tokens/info-button.js b/src/components/app-body/slp-tokens/info-button.js index bfac91e..1fda591 100644 --- a/src/components/app-body/slp-tokens/info-button.js +++ b/src/components/app-body/slp-tokens/info-button.js @@ -6,8 +6,8 @@ */ // Global npm libraries -import React, { useState } from 'react' -import { Button, Modal, Container, Row, Col } from 'react-bootstrap' +import React, { useState, useEffect } from 'react' +import { Button, Modal, Container, Row, Col, Spinner } from 'react-bootstrap' // Takes a string as input. If it matches a pattern for a link, a JSX object is // returned with a link. Otherwise the original string is returned. @@ -29,6 +29,7 @@ function linkIfUrl (url) { function InfoButton (props) { const [show, setShow] = useState(false) + const [mutableDataCid, setMutableDataCid] = useState(null) const handleClose = () => { setShow(false) @@ -45,6 +46,33 @@ function InfoButton (props) { // console.log('props.token: ', props.token) + // Get Cid from url + const parseCid = (url) => { + // get the cid from the url format 'ipfs://bafybeicem27xbzs65uvbcgykcmscsgln3lmhbfrcoec3gdttkdgtxv5acq + if (url && url.includes('ipfs://')) { + const cid = url.split('ipfs://')[1] + return cid + } + return url + } + // Get token user data if it exists and verify if it contains media or markdown + useEffect(() => { + try { + console.log('props token', props.token) + const userDataStr = props.token.tokenData.userData + if (userDataStr) { + const userData = JSON.parse(userDataStr) + + // If user data contains media or markdown, set the mutable data cid + if (userData?.media || userData?.markdown) { + setMutableDataCid(parseCid(props.token.tokenData.mutableData)) + } + } + } catch (error) { + // Do nothing + } + }, [props.token, show]) + return ( <> @@ -87,6 +115,25 @@ function InfoButton (props) { URL: {url} + {!props.token.iconAlreadyDownloaded && ( +
+ +
+ )} + {props.token.iconAlreadyDownloaded && mutableDataCid && ( + + User Data: + + + + + )}