diff --git a/components/Account/DexOrdersData.js b/components/Account/DexOrdersData.js index 4646f3ed..79012714 100644 --- a/components/Account/DexOrdersData.js +++ b/components/Account/DexOrdersData.js @@ -161,7 +161,7 @@ export default function DexOrdersData({ account, offerList, ledgerTimestamp, set {offerList.length > 5 ? ( <> The last 5 DEX orders{historicalTitle} [ - + View all ({offerList.length} total) {' '} ] @@ -169,7 +169,7 @@ export default function DexOrdersData({ account, offerList, ledgerTimestamp, set ) : ( <> {offerList.length} DEX orders{historicalTitle} [ - + View details ] @@ -204,7 +204,7 @@ export default function DexOrdersData({ account, offerList, ledgerTimestamp, set {offerList.length > 5 ? ( <> The last 5 DEX orders{historicalTitle} [ - + View all ({offerList.length} total) {' '} ] @@ -212,7 +212,7 @@ export default function DexOrdersData({ account, offerList, ledgerTimestamp, set ) : ( <> {offerList.length} DEX orders{historicalTitle} [ - + View details ] diff --git a/components/Account/NFTokenData.js b/components/Account/NFTokenData.js index 39867b3d..3304b27b 100644 --- a/components/Account/NFTokenData.js +++ b/components/Account/NFTokenData.js @@ -1,16 +1,35 @@ import Link from 'next/link' +import { useState, useEffect } from 'react' +import axios from 'axios' +import { useTranslation } from 'next-i18next' +import { xahauNetwork, useWidth } from '../../utils' +import { nftName, nftNameLink, nftThumbnail, nftUrl } from '../../utils/nft' +import LinkIcon from '../../public/images/link.svg' +import { LinkTx } from '../../utils/links' +import { + amountFormat, + fullDateAndTime, + nftLink, + nftOfferLink, + addressUsernameOrServiceLink, + AddressWithIconFilled, + dateFormat +} from '../../utils/format' -import { AddressWithIconFilled, fullDateAndTime } from '../../utils/format' - -export default function NFTokenData({ data, objects, ledgerTimestamp }) { - if ( - !data?.ledgerInfo?.nftokenMinter && - !data.ledgerInfo?.burnedNFTokens && - !data.ledgerInfo?.mintedNFTokens && - !(objects?.nftOfferList?.length > 0) && - !(objects?.nftList?.length > 0) - ) - return '' +export default function NFTokenData({ data, address, objects, ledgerTimestamp, selectedCurrency }) { + const windowWidth = useWidth() + const { t } = useTranslation() + const [ownedNfts, setOwnedNfts] = useState([]) + const [soldNfts, setSoldNfts] = useState([]) + const [createdOffers, setCreatedOffers] = useState([]) + const [receivedOffers, setReceivedOffers] = useState([]) + const [errorMessage, setErrorMessage] = useState('') + const [loading, setLoading] = useState({ + owned: true, + sold: true, + createdOffers: true, + receivedOffers: true + }) const title = 'NFT Data' @@ -25,7 +44,7 @@ export default function NFTokenData({ data, objects, ledgerTimestamp }) { ) : objects?.nftList?.length > 0 ? ( <> {!ledgerTimestamp ? ( - + View owned NFTs ({objects.nftList.length}) ) : ( @@ -41,7 +60,7 @@ export default function NFTokenData({ data, objects, ledgerTimestamp }) { ) : objects?.nftOfferList?.length > 0 ? ( <> {!ledgerTimestamp ? ( - + View NFT Offers ({objects.nftOfferList.length}) ) : ( @@ -55,7 +74,7 @@ export default function NFTokenData({ data, objects, ledgerTimestamp }) { const mintedNftsNode = ( <> {!ledgerTimestamp ? ( - + View minted NFTs ({data.ledgerInfo.mintedNFTokens}) ) : ( @@ -68,11 +87,7 @@ export default function NFTokenData({ data, objects, ledgerTimestamp }) { <> {!ledgerTimestamp ? ( View burned NFTs ({data.ledgerInfo.burnedNFTokens}) @@ -84,90 +99,448 @@ export default function NFTokenData({ data, objects, ledgerTimestamp }) { const nftMinterNode = - return ( - <> - + const fetchOwnedNfts = async () => { + try { + const response = await axios(`v2/nfts?owner=${address}&order=mintedNew&includeWithoutMediaData=true`) + console.log(response.data.nfts.length) + if (response?.data?.nfts) { + setOwnedNfts(response.data.nfts) + } else { + setOwnedNfts([]) + } + } catch (error) { + setErrorMessage(t('error.' + error.message)) + setOwnedNfts([]) + } finally { + setLoading((prev) => ({ ...prev, owned: false })) + } + } + + const fetchSoldNfts = async () => { + try { + const response = await axios( + `v2/nft-sales?seller=${address}&list=lastSold&convertCurrencies=${selectedCurrency?.toLowerCase()}&sortCurrency=${selectedCurrency?.toLowerCase()}` + ) + if (response?.data?.sales) { + setSoldNfts(response.data.sales) + } else { + setSoldNfts([]) + } + } catch (error) { + setErrorMessage(t('error.' + error.message)) + setSoldNfts([]) + } finally { + setLoading((prev) => ({ ...prev, sold: false })) + } + } + + const fetchCreatedOffers = async () => { + try { + const response = await axios(`v2/nft-offers/${address}?list=counterOffers&nftoken=true&offersValidate=true`) + if (response?.data?.nftOffers) { + setCreatedOffers( + response.data.nftOffers + .sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1)) + .filter(function (offer) { + return offer.valid + }) + .slice(0, 5) + ) + } else { + setCreatedOffers([]) + } + } catch (error) { + setErrorMessage(t('error.' + error.message)) + setCreatedOffers([]) + } finally { + setLoading((prev) => ({ ...prev, createdOffers: false })) + } + } + + const fetchReceivedOffers = async () => { + try { + const response = await axios( + `v2/nft-offers/${address}?list=privatelyOfferedToAddress&nftoken=true&offersValidate=true` + ) + if (response?.data?.nftOffers) { + setReceivedOffers( + response.data.nftOffers + .sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1)) + .filter(function (offer) { + return offer.valid + }) + .slice(0, 5) + ) + } else { + setReceivedOffers([]) + } + } catch (error) { + setErrorMessage(t('error.' + error.message)) + setReceivedOffers([]) + } finally { + setLoading((prev) => ({ ...prev, receivedOffers: false })) + } + } + + useEffect(() => { + // Fetch owned NFTs (last bought) + if (objects?.nftList?.length > 0) { + fetchOwnedNfts() + } + + // Fetch sold NFTs + fetchSoldNfts() + + // Fetch created offers + if (objects?.nftOfferList?.length > 0) { + fetchCreatedOffers() + } + + // Fetch received offers + if (objects?.nftList?.length > 0) { + fetchReceivedOffers() + } + }, [objects]) + + const renderNFTSection = (type, title, nfts, loading) => { + if (loading) { + return ( +
+

{title}

+
+ +
+ {t('general.loading')} +
+
+ ) + } + + return ( +
800 ? 'table-details' : 'table-mobile'} + style={{ width: '100%', marginTop: '15px' }} + > - + - {data?.ledgerInfo?.activated && ( - - - - - )} - {data.ledgerInfo?.mintedNFTokens && ( - - - - - )} - {data.ledgerInfo?.burnedNFTokens && ( - - - - - )} - {data?.ledgerInfo?.activated && ( - - - - - )} - {data.ledgerInfo?.firstNFTokenSequence && ( - - - - - )} - {data.ledgerInfo?.nftokenMinter && ( + + {nfts?.length > 0 ? ( + + ) : ( + + )} + + +
{historicalTitle || title} + {nfts?.length > 1 ? nfts.length : ''} {title} + {nfts?.length > 0 && ( + <> + {' '} + [ + + View All + + ] + + )} +
Owned NFTs{ownedNftsNode}
Minted NFTs{mintedNftsNode}
Burned NFTs{burnedNftsNode}
NFT Offers{nftOffersNode}
First NFT sequence{data.ledgerInfo.firstNFTokenSequence}
+ {nfts?.map((nft, i) => ( + + {nftName(nft?.nftoken + + ))} + +

We couldn't find any NFTs.

+
+ ) + } + + const renderOffersSection = (type, title, offers, loading) => { + const titleNode = ( + <> + {offers?.length > 1 ? offers.length : ''} {title}{' '} + {offers?.length ? ( + <> + [ + + View All + + ] + + ) : ( + '' + )} + + ) + if (windowWidth > 800) { + return ( + + - - + - )} + + + {!offers?.length ? ( +

{t('nft-offers.no-nft-offers')}

+ ) : ( + + {!xahauNetwork && ( + + )} + + + + + {type === 'created' && } + + )} + {loading ? ( + + + + ) : ( + <> + {!errorMessage ? ( + offers.slice(0, 5).map((offer, i) => ( + + {!xahauNetwork && ( + + )} + + + + + {type === 'created' && } + + )) + ) : ( + + + + )} + + )} + +
NFT minter{nftMinterNode}{titleNode}
+ {t('table.offer')} + NFT{t('table.type')}{t('table.amount')}{t('table.placed')}{t('table.destination')}
+ +
+ {t('general.loading')} +
+
+ + + + {nftThumbnail(offer?.nftoken)} + {offer?.flags?.sellToken === true || xahauNetwork ? t('table.text.sell') : t('table.text.buy')} + {amountFormat(offer?.amount, { tooltip: true, maxFractionDigits: 2 })} + {dateFormat(offer?.createdAt)} + {nftLink(offer, 'destination')}
+ {errorMessage} +
+ ) + } else { + return ( + + + + {loading ? ( + + + + ) : ( + <> + + + + {!errorMessage && offers?.length ? ( + offers.slice(0, 5).map((offer, i) => ( + + + + + )) + ) : ( + + {errorMessage && ( + + )} + {!offers?.length && ( + + )} + + )} + + )} + +
+
+ +
+ {t('general.loading')} +
+
+
{titleNode}
+

{nftThumbnail(offer.nftoken)}

+
+ {!xahauNetwork && ( +

+ {t('table.offer')}: {nftOfferLink(offer.offerIndex)} +

+ )} +

+ NFT: {nftName(offer.nftoken) ? nftNameLink(offer.nftoken) : nftOfferLink(offer.offerIndex)} +

+

+ {t('table.type')}:{' '} + {offer.flags?.sellToken === true || xahauNetwork ? t('table.text.sell') : t('table.text.buy')} +

+

+ {t('table.amount')}: {amountFormat(offer.amount)} +

+

+ {t('table.placed')}: {dateFormat(offer.createdAt)}{' '} + +

+ {offer.destination && ( +

+ {t('table.destination')}: {addressUsernameOrServiceLink(offer, 'destination')} +

+ )} + {!offer.valid && ( +

+ {t('table.status')}: {t('table.text.invalid')} +

+ )} +
+ {errorMessage} + + {t('nft-offers.no-nft-offers')} +
+ ) + } + } + + const getMetaData = () => { + return ( + <> + {renderNFTSection('owned', 'Owned NFTs', ownedNfts, loading.owned, 'name')} + {renderNFTSection('sold', 'Sold NFTs', soldNfts, loading.sold, 'soldNew')} + {renderOffersSection('created', 'NFT Offers Created', createdOffers, loading.createdOffers)} + {renderOffersSection('received', 'Received NFT Offers', receivedOffers, loading.receivedOffers)} + {windowWidth < 800 &&
} + + ) + } + + let isEmpty = + !data?.ledgerInfo?.nftokenMinter && + !data.ledgerInfo?.burnedNFTokens && + !data.ledgerInfo?.mintedNFTokens && + !(objects?.nftOfferList?.length > 0) && + !(objects?.nftList?.length > 0) - {data.ledgerInfo?.flags?.disallowIncomingNFTokenOffer && ( + return ( + <> + {!isEmpty && ( + + - - + - )} - -
Incoming NFT offersdisallowed{historicalTitle || title}
+ + + {data.ledgerInfo?.mintedNFTokens && ( + + Minted NFTs + {mintedNftsNode} + + )} + {data.ledgerInfo?.burnedNFTokens && ( + + Burned NFTs + {burnedNftsNode} + + )} + {data.ledgerInfo?.firstNFTokenSequence && ( + + First NFT sequence + {data.ledgerInfo.firstNFTokenSequence} + + )} + {data.ledgerInfo?.nftokenMinter && ( + + NFT minter + {nftMinterNode} + + )} + {data.ledgerInfo?.flags?.disallowIncomingNFTokenOffer && ( + + Incoming NFT offers + disallowed + + )} + + + )} + {getMetaData()} +
{historicalTitle || title.toUpperCase()}
- - {data?.ledgerInfo?.activated &&

{ownedNftsNode}

} - {data.ledgerInfo?.mintedNFTokens &&

{mintedNftsNode}

} - {data.ledgerInfo?.burnedNFTokens &&

{burnedNftsNode}

} - {data?.ledgerInfo?.activated &&

{nftOffersNode}

} - {data.ledgerInfo?.firstNFTokenSequence && ( -

- First NFT sequence {data.ledgerInfo.firstNFTokenSequence} -

- )} - {data.ledgerInfo?.nftokenMinter && ( + {!isEmpty && ( <> -

- NFT minter -

- {nftMinterNode} + {data?.ledgerInfo?.activated &&

{ownedNftsNode}

} + {data.ledgerInfo?.mintedNFTokens &&

{mintedNftsNode}

} + {data.ledgerInfo?.burnedNFTokens &&

{burnedNftsNode}

} + {data?.ledgerInfo?.activated &&

{nftOffersNode}

} + {data.ledgerInfo?.firstNFTokenSequence && ( +

+ First NFT sequence {data.ledgerInfo.firstNFTokenSequence} +

+ )} + {data.ledgerInfo?.nftokenMinter && ( + <> +

+ NFT minter +

+ {nftMinterNode} + + )} + {data.ledgerInfo?.flags?.disallowIncomingNFTokenOffer && ( +

+ Incoming NFT offers disallowed +

+ )} + {data.ledgerInfo?.flags?.uriTokenIssuer && ( +

+ URI token issuer true +

+ )} )} - {data.ledgerInfo?.flags?.disallowIncomingNFTokenOffer && ( -

- Incoming NFT offers disallowed -

- )} - {data.ledgerInfo?.flags?.uriTokenIssuer && ( -

- URI token issuer true -

- )}
) diff --git a/components/Tiles.js b/components/Tiles.js index 0688e4cc..487d2192 100644 --- a/components/Tiles.js +++ b/components/Tiles.js @@ -30,7 +30,7 @@ const addressName = (details, name) => { return '' } -export default function Tiles({ nftList, type = 'name', convertCurrency, account }) { +export default function Tiles({ nftList, type = 'name', convertCurrency, account, disabled = false }) { const { t } = useTranslation() const router = useRouter() @@ -107,25 +107,27 @@ export default function Tiles({ nftList, type = 'name', convertCurrency, account
{type === 'name' ? nftName(nft, { maxLength: 18 }) : saleData(nft.sellOffers)}
-
- {nftName(nft) ? ( - <> - {t('table.name')}: {nftName(nft)} -
- - ) : ( - '' - )} - {!xahauNetwork && ( - <> - {t('table.serial')}: {nft.sequence} -
- {t('table.taxon')}: {nft.nftokenTaxon} - - )} - {addressName(nft.issuerDetails, t('table.issuer'))} - {addressName(nft.ownerDetails, t('table.owner'))} -
+ {!disabled && +
+ {nftName(nft) ? ( + <> + {t('table.name')}: {nftName(nft)} +
+ + ) : ( + '' + )} + {!xahauNetwork && ( + <> + {t('table.serial')}: {nft.sequence} +
+ {t('table.taxon')}: {nft.nftokenTaxon} + + )} + {addressName(nft.issuerDetails, t('table.issuer'))} + {addressName(nft.ownerDetails, t('table.owner'))} +
+ } @@ -157,26 +159,28 @@ export default function Tiles({ nftList, type = 'name', convertCurrency, account
{convertedAmount(nft, convertCurrency, { short: true }) || amountFormat(nft.amount)}
- {timeOrDate(nft.acceptedAt)} -
-
- {nftName(nft.nftoken) ? ( - <> - {t('table.name')}: {nftName(nft.nftoken, { maxLength: 18 })} - - ) : ( - '' - )} - {addressName(nft.nftoken?.issuerDetails, t('table.issuer'))} -
- {t('table.price')}: {amountFormat(nft.amount)} - {nft.marketplace && ( - <> -
- {t('table.marketplace')}: {nft.marketplace} - - )} + {!disabled && timeOrDate(nft.acceptedAt)}
+ {!disabled && +
+ {nftName(nft.nftoken) ? ( + <> + {t('table.name')}: {nftName(nft.nftoken, { maxLength: 18 })} + + ) : ( + '' + )} + {addressName(nft.nftoken?.issuerDetails, t('table.issuer'))} +
+ {t('table.price')}: {amountFormat(nft.amount)} + {nft.marketplace && ( + <> +
+ {t('table.marketplace')}: {nft.marketplace} + + )} +
+ } diff --git a/pages/account/[id].js b/pages/account/[id].js index 40fddd73..c21d702a 100644 --- a/pages/account/[id].js +++ b/pages/account/[id].js @@ -582,7 +582,11 @@ export default function Account({ {/* don't show yet obligations historically */} {data?.obligations?.trustlines > 0 && !data?.ledgerInfo?.ledgerTimestamp && ( - + )}
@@ -600,8 +604,10 @@ export default function Account({ ) : ( )} @@ -680,4 +686,4 @@ export default function Account({ `} ) -} \ No newline at end of file +}