mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-21 16:52:01 -07:00
feat(NFT): Lazy-load NFT token icons
This commit is contained in:
+9
-9
@@ -1,18 +1,18 @@
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack')
|
||||||
|
|
||||||
module.exports = function override(config) {
|
module.exports = function override (config) {
|
||||||
config.resolve.fallback = {
|
config.resolve.fallback = {
|
||||||
...config.resolve.fallback,
|
...config.resolve.fallback,
|
||||||
crypto: require.resolve('crypto-browserify'),
|
crypto: require.resolve('crypto-browserify'),
|
||||||
stream: require.resolve('stream-browserify'),
|
stream: require.resolve('stream-browserify'),
|
||||||
vm: require.resolve('vm-browserify'),
|
vm: require.resolve('vm-browserify')
|
||||||
};
|
}
|
||||||
|
|
||||||
config.plugins = (config.plugins || []).concat([
|
config.plugins = (config.plugins || []).concat([
|
||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
process: 'process/browser',
|
process: 'process/browser'
|
||||||
}),
|
})
|
||||||
]);
|
])
|
||||||
|
|
||||||
return config;
|
return config
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
Shows NFTs for sale on the DEX.
|
Shows NFTs for sale on the DEX.
|
||||||
|
|
||||||
|
workflow:
|
||||||
|
1. Load offers
|
||||||
|
2. display token cards with current offers data
|
||||||
|
3. Load tokens data
|
||||||
|
4. add data to offers and update card with new data
|
||||||
|
5. Load tokens icons
|
||||||
|
6. add icons to offers and update card with new icons
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Global npm libraries
|
// Global npm libraries
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
import { Container, Row, Card, Col, Button, Spinner } from 'react-bootstrap'
|
import { Container, Row, Col, Button, Spinner } from 'react-bootstrap'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Jdenticon from '@chris.troutner/react-jdenticon'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faRedo } from '@fortawesome/free-solid-svg-icons'
|
import { faRedo } from '@fortawesome/free-solid-svg-icons'
|
||||||
// import BchDexLib from 'bch-dex-lib'
|
|
||||||
// import RetryQueue from '@chris.troutner/retry-queue'
|
|
||||||
|
|
||||||
// Local libraries
|
// Local libraries
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
@@ -21,111 +26,188 @@ const SERVER = config.dexServer
|
|||||||
function NftsForSale (props) {
|
function NftsForSale (props) {
|
||||||
// Dependency injection through props
|
// Dependency injection through props
|
||||||
const appData = props.appData
|
const appData = props.appData
|
||||||
console.log('NftsForSale() appData: ', appData)
|
|
||||||
|
|
||||||
// State
|
|
||||||
const [offers, setOffers] = useState([])
|
const [offers, setOffers] = useState([])
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [offersAreLoaded, setOffersAreLoaded] = useState(false)
|
||||||
|
const [iconsAreLoaded, setIconsAreLoaded] = useState(false)
|
||||||
|
const [dataAreLoaded, setDataAreLoaded] = useState(false)
|
||||||
|
|
||||||
async function getNftOffers(page = 0) {
|
// Handler for refresh button
|
||||||
|
const handleRefresh = () => {
|
||||||
|
console.log('handling refresh')
|
||||||
|
loadNftOffers()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to process token data
|
||||||
|
const processTokenData = useCallback(async (offer) => {
|
||||||
try {
|
try {
|
||||||
const options = {
|
const { wallet, bchWalletState } = appData
|
||||||
method: 'GET',
|
const { bchjs } = wallet
|
||||||
url: `${SERVER}/offer/list/nft/${page}`,
|
|
||||||
data: {}
|
|
||||||
}
|
|
||||||
const result = await axios.request(options)
|
|
||||||
console.log('result.data: ', result.data)
|
|
||||||
|
|
||||||
|
// Calculate USD price
|
||||||
|
const rateInSats = parseInt(offer.rateInBaseUnit)
|
||||||
|
const bchCost = bchjs.BitcoinCash.toBitcoinCash(rateInSats)
|
||||||
|
const usdPrice = bchCost * bchWalletState.bchUsdPrice * offer.numTokens
|
||||||
|
offer.usdPrice = `$${usdPrice.toFixed(3)}`
|
||||||
|
|
||||||
|
return offer
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error processing token:', err)
|
||||||
|
return offer
|
||||||
|
}
|
||||||
|
}, [appData])
|
||||||
|
|
||||||
|
// Fetch offers
|
||||||
|
const getNftOffers = useCallback(async (page = 0) => {
|
||||||
|
try {
|
||||||
|
setOffersAreLoaded(false)
|
||||||
|
|
||||||
|
const result = await axios.get(`${SERVER}/offer/list/nft/${page}`)
|
||||||
const rawOffers = result.data
|
const rawOffers = result.data
|
||||||
|
console.log('rawOffers: ', rawOffers)
|
||||||
|
|
||||||
const bchjs = appData.wallet.bchjs
|
// Process each offer
|
||||||
const wallet = appData.wallet
|
const processedOffers = []
|
||||||
|
|
||||||
// Instantiate the BchDexLib object.
|
|
||||||
// const dexLib = new BchDexLib({bchWallet: wallet, p2wdbRead: {}, p2wdbWrite: {}})
|
|
||||||
|
|
||||||
const offerDataCallback = (offer) => {
|
|
||||||
console.log('offerDataCallback() offer: ', offer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a default icon.
|
|
||||||
for (let i = 0; i < rawOffers.length; i++) {
|
for (let i = 0; i < rawOffers.length; i++) {
|
||||||
const thisOffer = rawOffers[i]
|
const offer = rawOffers[i]
|
||||||
|
const processedOffer = await processTokenData(offer)
|
||||||
thisOffer.icon = (<Jdenticon size='100' value={thisOffer.tokenId} />)
|
processedOffers.push(processedOffer)
|
||||||
thisOffer.iconDownloaded = false
|
|
||||||
|
|
||||||
// Convert sats to BCH, and then calculate cost in USD.
|
|
||||||
const rateInSats = parseInt(thisOffer.rateInBaseUnit)
|
|
||||||
// console.log('rateInSats: ', rateInSats)
|
|
||||||
const bchCost = bchjs.BitcoinCash.toBitcoinCash(rateInSats)
|
|
||||||
// console.log('bchCost: ', bchCost)
|
|
||||||
// console.log('bchUsdPrice: ', this.state.appData.bchWalletState.bchUsdPrice)
|
|
||||||
const usdPrice = bchCost * appData.bchWalletState.bchUsdPrice * thisOffer.numTokens
|
|
||||||
// usdPrice = bchjs.Util.floor2(usdPrice)
|
|
||||||
// console.log(`usdPrice: ${usdPrice}`)
|
|
||||||
const priceStr = `$${usdPrice.toFixed(3)}`
|
|
||||||
thisOffer.usdPrice = priceStr
|
|
||||||
|
|
||||||
// Download token data
|
|
||||||
// await dexLib.tokenData.getTokenData(thisOffer, offerDataCallback)
|
|
||||||
const tokenData = await wallet.getTokenData(thisOffer.tokenId)
|
|
||||||
console.log('complete tokenData: ', tokenData)
|
|
||||||
|
|
||||||
// const mutableCid = tokenData.mutableData.slice(7)
|
|
||||||
// const url1 = `https://free-bch.fullstack.cash/ipfs/file-info/${mutableCid}`
|
|
||||||
// console.log('url1: ', url1)
|
|
||||||
// const resp1 = await axios.get(url1)
|
|
||||||
// console.log('resp1.data: ', resp1.data)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const mutableCid = tokenData.mutableData.slice(7)
|
|
||||||
console.log(`mutable CID for token ${thisOffer.tokenId}: ${mutableCid}`)
|
|
||||||
let mutableData = await wallet.cid2json({ cid: mutableCid })
|
|
||||||
console.log('mutableData: ', mutableData)
|
|
||||||
mutableData = mutableData.json
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Could not download mutable data for token ${thisOffer.tokenId}: ${err.message}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawOffers
|
setOffersAreLoaded(true)
|
||||||
} catch(err) {
|
|
||||||
console.error('NftsForSale() getNftOffers() error: ', err)
|
return processedOffers
|
||||||
return []
|
} catch (err) {
|
||||||
|
console.error('getNftOffers error:', err)
|
||||||
|
setOffersAreLoaded(true)
|
||||||
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}, [processTokenData])
|
||||||
|
|
||||||
async function handleOffers() {
|
// This function loads the token data .
|
||||||
let offers = await getNftOffers()
|
const lazyLoadTokenData = useCallback(async (tokens) => {
|
||||||
console.log('offers: ', offers)
|
|
||||||
|
|
||||||
return offers
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleStartProcessingTokens() {
|
|
||||||
return await handleOffers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is an async startup function that is called by the useEffect hook.
|
|
||||||
const asyncStartup = async () => {
|
|
||||||
try {
|
try {
|
||||||
const offers = await handleStartProcessingTokens()
|
setDataAreLoaded(false)
|
||||||
setOffers(offers)
|
// 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)
|
||||||
|
|
||||||
|
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) {
|
} catch (error) {
|
||||||
console.error('NftsForSale() asyncStartup() error: ', error)
|
setDataAreLoaded(true)
|
||||||
}
|
}
|
||||||
}
|
}, [appData])
|
||||||
|
|
||||||
// Load NFT data when the page loads from the server or from the cache.
|
// Fetch mutable data if it exist and get the token icon url
|
||||||
|
const fetchTokenInfo = useCallback(async (token) => {
|
||||||
|
try {
|
||||||
|
// Get the token data
|
||||||
|
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
|
||||||
|
|
||||||
|
let iconUrl = json.tokenIcon
|
||||||
|
|
||||||
|
if (json.fullSizedUrl && json.fullSizedUrl.includes('http')) {
|
||||||
|
iconUrl = json.fullSizedUrl
|
||||||
|
}
|
||||||
|
// Return icon url
|
||||||
|
return iconUrl
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}, [appData])
|
||||||
|
|
||||||
|
// This function loads the token icons from the ipfs gateways.
|
||||||
|
const lazyLoadTokenIcons = useCallback(async (tokens) => {
|
||||||
|
try {
|
||||||
|
setIconsAreLoaded(false)
|
||||||
|
// map each token and fetch the icon url
|
||||||
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
|
const thisToken = tokens[i]
|
||||||
|
|
||||||
|
// Incon does not need to be downloaded, so continue with the next one
|
||||||
|
if (thisToken.iconAlreadyDownloaded) continue
|
||||||
|
|
||||||
|
// Try to get token icon url from mutable data.
|
||||||
|
const iconUrl = await fetchTokenInfo(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark token to prevent fetch token icon again.
|
||||||
|
thisToken.iconAlreadyDownloaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
setIconsAreLoaded(true)
|
||||||
|
} catch (error) {
|
||||||
|
setIconsAreLoaded(true)
|
||||||
|
}
|
||||||
|
}, [fetchTokenInfo])
|
||||||
|
|
||||||
|
// Main function to load NFT offers
|
||||||
|
const loadNftOffers = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
// Get tokens in offers
|
||||||
|
const offers = await getNftOffers()
|
||||||
|
console.log('offers: ', offers)
|
||||||
|
setOffers(offers)
|
||||||
|
// Load tokens data
|
||||||
|
await lazyLoadTokenData(offers)
|
||||||
|
// Load tokens icons
|
||||||
|
await lazyLoadTokenIcons(offers)
|
||||||
|
setIsLoading(false)
|
||||||
|
} catch (err) {
|
||||||
|
setIsLoading(false)
|
||||||
|
console.error('Error loading NFT offers:', err)
|
||||||
|
}
|
||||||
|
}, [lazyLoadTokenData, lazyLoadTokenIcons, getNftOffers])
|
||||||
|
|
||||||
|
// Effect to load NFTs on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
asyncStartup()
|
console.log('loading nfts for sale')
|
||||||
}, [])
|
loadNftOffers()
|
||||||
|
}, [loadNftOffers])
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
// This function generates a Token Card for each token in the wallet.
|
// This function generates a Token Card for each token in the wallet.
|
||||||
function generateCards () {
|
function generateCards (offers) {
|
||||||
// console.log('generateCards() offerData: ', offerData)
|
console.log('generateCards() offerData: ', offers)
|
||||||
|
|
||||||
const tokens = offers
|
const tokens = offers
|
||||||
|
|
||||||
@@ -133,13 +215,12 @@ function NftsForSale (props) {
|
|||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
const thisToken = tokens[i]
|
const thisToken = tokens[i]
|
||||||
// console.log(`thisToken: ${JSON.stringify(thisToken, null, 2)}`)
|
|
||||||
|
|
||||||
const thisTokenCard = (
|
const thisTokenCard = (
|
||||||
<TokenCard
|
<TokenCard
|
||||||
appData={appData}
|
appData={appData}
|
||||||
token={thisToken}
|
token={thisToken}
|
||||||
key={`${thisToken.tokenId}`}
|
key={`${thisToken.tokenId + i}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
tokenCards.push(thisTokenCard)
|
tokenCards.push(thisTokenCard)
|
||||||
@@ -155,18 +236,53 @@ function NftsForSale (props) {
|
|||||||
<h1>NFTs for Sale</h1>
|
<h1>NFTs for Sale</h1>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={12} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
<Button variant='success' >
|
{/** Show spinner info if tokens are loaded but data is not loaded */
|
||||||
<FontAwesomeIcon icon={faRedo} size='lg' /> Refresh
|
offersAreLoaded && !dataAreLoaded && (
|
||||||
</Button>
|
<div style={{ borderRadius: '10px', backgroundColor: '#f0f0f0', padding: '10px', display: 'flex', justifyContent: 'center', alignItems: 'center', width: 'fit-content' }}>
|
||||||
|
<span style={{ marginRight: '10px' }}>Loading Tokens Data </span>
|
||||||
|
<Spinner animation='border' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{/** Show spinner info if tokens are loaded but icons are not loaded */
|
||||||
|
dataAreLoaded && !iconsAreLoaded && (
|
||||||
|
<div style={{ borderRadius: '10px', backgroundColor: '#f0f0f0', padding: '10px', display: 'flex', justifyContent: 'center', alignItems: 'center', width: 'fit-content' }}>
|
||||||
|
<span style={{ marginRight: '10px' }}>Loading Tokens Icons </span>
|
||||||
|
<Spinner animation='border' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
{!isLoading && (
|
||||||
|
<Row>
|
||||||
|
<Col xs={6}>
|
||||||
|
<Button variant='success' onClick={handleRefresh}>
|
||||||
|
<FontAwesomeIcon icon={faRedo} size='lg' /> Refresh
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!offersAreLoaded && (
|
||||||
|
<Row className='d-block text-center'>
|
||||||
|
<Spinner animation='border' variant='primary' style={{ maegin: '0 auto' }} />
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
<Row>
|
<Row>
|
||||||
{generateCards()}
|
{offersAreLoaded && generateCards(offers)}
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
{/** Display a message if no tokens are found */}
|
||||||
|
{offersAreLoaded && offers.length === 0 && (
|
||||||
|
<Row className='text-center'>
|
||||||
|
<span> No Offers found. </span>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ function InfoButton (props) {
|
|||||||
setShow(true)
|
setShow(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('props.token: ', props.token)
|
|
||||||
|
|
||||||
// Replace with dummy button until token data is loaded.
|
// Replace with dummy button until token data is loaded.
|
||||||
if (!props.token.tokenData) {
|
if (!props.token.tokenData) {
|
||||||
return (
|
return (
|
||||||
@@ -34,15 +32,9 @@ function InfoButton (props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the url property of the token to a link, if it matches common patterns.
|
|
||||||
// let url = props.token.tokenurl
|
|
||||||
// url = linkIfUrl(props.token.url)
|
|
||||||
|
|
||||||
// console.log('props.token: ', props.token)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button variant='info' onClick={handleOpen}>Info</Button>
|
<Button variant='info' disabled={props.disabled} onClick={handleOpen}>Info</Button>
|
||||||
<Modal show={show} onHide={handleClose}>
|
<Modal show={show} onHide={handleClose}>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>Token Information</Modal.Title>
|
<Modal.Title>Token Information</Modal.Title>
|
||||||
@@ -56,7 +48,7 @@ function InfoButton (props) {
|
|||||||
|
|
||||||
<Row style={{ backgroundColor: '#eee' }}>
|
<Row style={{ backgroundColor: '#eee' }}>
|
||||||
<Col xs={4}><b>Name</b>:</Col>
|
<Col xs={4}><b>Name</b>:</Col>
|
||||||
<Col xs={8}>{props.token.tokenData.tokenStats.name}</Col>
|
<Col xs={8}>{props.token.tokenData.genesisData.name}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
@@ -70,19 +62,14 @@ function InfoButton (props) {
|
|||||||
|
|
||||||
<Row style={{ backgroundColor: '#eee' }}>
|
<Row style={{ backgroundColor: '#eee' }}>
|
||||||
<Col xs={4}><b>Decimals</b>:</Col>
|
<Col xs={4}><b>Decimals</b>:</Col>
|
||||||
<Col xs={8}>{props.token.tokenData.tokenStats.decimals}</Col>
|
<Col xs={8}>{props.token.tokenData.genesisData.decimals}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={4}><b>Token Type</b>:</Col>
|
<Col xs={4}><b>Token Type</b>:</Col>
|
||||||
<Col xs={8}>{props.token.tokenType}</Col>
|
<Col xs={8}>{props.token.tokenType}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/*
|
|
||||||
<Row style={{ backgroundColor: '#eee', wordBreak: 'break-all' }}>
|
|
||||||
<Col xs={4}><b>URL</b>:</Col>
|
|
||||||
<Col xs={8}>{url}</Col>
|
|
||||||
</Row>
|
|
||||||
*/}
|
|
||||||
</Container>
|
</Container>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer />
|
<Modal.Footer />
|
||||||
|
|||||||
@@ -3,63 +3,70 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Global npm libraries
|
// Global npm libraries
|
||||||
import React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { Container, Row, Col, Card } from 'react-bootstrap'
|
import { Container, Row, Col, Card } from 'react-bootstrap'
|
||||||
// import BuyNftButton from './buy-button'
|
|
||||||
import InfoButton from './info-button'
|
import InfoButton from './info-button'
|
||||||
// import FlagButton from './flag-button'
|
import Jdenticon from '@chris.troutner/react-jdenticon'
|
||||||
|
|
||||||
// Local libraries
|
|
||||||
// import InfoButton from './info-button'
|
|
||||||
// import SendTokenButton from './send-token-button'
|
|
||||||
// import SellButton from './sell-button'
|
|
||||||
|
|
||||||
function TokenCard (props) {
|
function TokenCard (props) {
|
||||||
let imageLink = ''
|
const { token } = props
|
||||||
if (props.token.mutableData) {
|
const [icon, setIcon] = useState(token.icon)
|
||||||
imageLink = props.token.mutableData.tokenIcon
|
const [tokenData, setTokenData] = useState(token.tokenData)
|
||||||
|
|
||||||
if (props.token.mutableData.fullSizedUrl && props.token.mutableData.fullSizedUrl.includes('http')) {
|
// Update icon state every token.icon and token.tokenData changes
|
||||||
imageLink = props.token.mutableData.fullSizedUrl
|
useEffect(() => {
|
||||||
}
|
setIcon(token.icon)
|
||||||
}
|
setTokenData(token.tokenData)
|
||||||
|
}, [token.icon, token.tokenData])
|
||||||
console.log('TokenCard props.token: ', props.token)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Col xs={12} sm={6} lg={4} style={{ padding: '25px' }}>
|
<Col xs={12} sm={6} lg={4} style={{ padding: '25px' }}>
|
||||||
<Card>
|
<Card>
|
||||||
<Card.Body style={{ textAlign: 'center' }}>
|
<Card.Body style={{ textAlign: 'center' }}>
|
||||||
<a href={imageLink} target='_blank' rel='noreferrer'>
|
{/** If the icon is loaded, display it */
|
||||||
{props.token.icon}
|
icon && (
|
||||||
</a>
|
<Card.Img
|
||||||
<Card.Title style={{ textAlign: 'center' }}>
|
src={icon}
|
||||||
<h4>{props.token.ticker}</h4>
|
style={{ height: '100px', width: 'auto' }}
|
||||||
|
onError={(e) => {
|
||||||
|
setIcon(null) // Set the icon to null if it fails to load the image url.
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{/** If the icon is not loaded, display the Jdenticon */
|
||||||
|
!icon && (
|
||||||
|
<Jdenticon size='100' value={token.tokenId} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Card.Title style={{ textAlign: 'center', marginTop: '10px' }}>
|
||||||
|
<h4>{token.ticker}</h4>
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
{props.token.tokenData ? props.token.tokenData.tokenStats.name : null}
|
{tokenData && tokenData.genesisData ? tokenData.genesisData.name : null}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col>Price:</Col>
|
<Col>Price:</Col>
|
||||||
<Col>{props.token.usdPrice}</Col>
|
<Col>{token.usdPrice}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<InfoButton token={props.token} />
|
<InfoButton token={token} disabled={!token.tokenData} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col></Col>
|
<Col />
|
||||||
|
|
||||||
<Col></Col>
|
<Col />
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
</Container>
|
</Container>
|
||||||
@@ -69,7 +76,7 @@ function TokenCard (props) {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
{/* <Col>
|
{/* <Col>
|
||||||
<InfoButton token={props.token} />
|
<InfoButton token={props.token} />
|
||||||
</Col>
|
</Col>
|
||||||
@@ -80,6 +87,6 @@ function TokenCard (props) {
|
|||||||
|
|
||||||
<Col>
|
<Col>
|
||||||
<BuyNftButton appData={props.appData} offer={props.token} />
|
<BuyNftButton appData={props.appData} offer={props.token} />
|
||||||
</Col> */}
|
</Col> */ }
|
||||||
|
|
||||||
export default TokenCard
|
export default TokenCard
|
||||||
|
|||||||
Reference in New Issue
Block a user