mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Feat: Display NFT in asset details (#1125)
* feat: add decodeTokenUri helper
* refactor: restructure of MetaMain component
* feat: add nft tooltip
* feat: add opensea link for nfts
* style: adjust nft image size in tooltip
* feat: add nft data to publish preview
* fix: readd owner to nft metadata
* refactor: conditional display of nft tooltip
* style: fix link styles in nft tooltip
* feat: add placeholder graphic as fallback if nft data does not contain one
* fix: display openSea link only on supported networks
* fix: rename ddo props to asset in metamain related components
* feat: add original publisher to asset details
* chore: remove unused imports
* fix: remove unused prop
* feat: convert publisher address to checksum address
* chore: remove console.error when decoding tokenURI
* Revert "chore: remove console.error when decoding tokenURI"
This reverts commit f387175970
.
* feat: shorten nft address in tooltip preview
* fix: use Web3.utils instead of the actual web3 instance to convert wei in ether
Co-authored-by: Luca Milanese <luca.milanese90@gmail.com>
Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
8b331c0a63
commit
9d1b7794a3
@ -1,13 +1,13 @@
|
|||||||
import { SvgWaves } from './SvgWaves'
|
|
||||||
import {
|
import {
|
||||||
Asset,
|
|
||||||
LoggerInstance,
|
LoggerInstance,
|
||||||
|
Asset,
|
||||||
getHash,
|
getHash,
|
||||||
Nft,
|
Nft,
|
||||||
ProviderInstance,
|
ProviderInstance,
|
||||||
DDO,
|
DDO,
|
||||||
MetadataAndTokenURI
|
MetadataAndTokenURI
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
|
import { SvgWaves } from './SvgWaves'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { TransactionReceipt } from 'web3-core'
|
import { TransactionReceipt } from 'web3-core'
|
||||||
|
|
||||||
@ -60,6 +60,8 @@ export function generateNftMetadata(): NftMetadata {
|
|||||||
return newNft
|
return newNft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tokenUriPrefix = 'data:application/json;base64,'
|
||||||
|
|
||||||
export function generateNftCreateData(nftMetadata: NftMetadata): any {
|
export function generateNftCreateData(nftMetadata: NftMetadata): any {
|
||||||
const nftCreateData = {
|
const nftCreateData = {
|
||||||
name: nftMetadata.name,
|
name: nftMetadata.name,
|
||||||
@ -71,6 +73,19 @@ export function generateNftCreateData(nftMetadata: NftMetadata): any {
|
|||||||
return nftCreateData
|
return nftCreateData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeTokenURI(tokenURI: string): NftMetadata {
|
||||||
|
if (!tokenURI) return undefined
|
||||||
|
try {
|
||||||
|
const nftMeta = JSON.parse(
|
||||||
|
Buffer.from(tokenURI.replace(tokenUriPrefix, ''), 'base64').toString()
|
||||||
|
) as NftMetadata
|
||||||
|
|
||||||
|
return nftMeta
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error(`[NFT] ${error.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function setNftMetadata(
|
export async function setNftMetadata(
|
||||||
asset: Asset | DDO,
|
asset: Asset | DDO,
|
||||||
accountId: string,
|
accountId: string,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { usePrices } from '@context/Prices'
|
import { usePrices } from '@context/Prices'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import Web3 from 'web3'
|
||||||
import useNftFactory from '@hooks/contracts/useNftFactory'
|
import useNftFactory from '@hooks/contracts/useNftFactory'
|
||||||
import { NftFactory } from '@oceanprotocol/lib'
|
import { NftFactory } from '@oceanprotocol/lib'
|
||||||
import Conversion from '@shared/Price/Conversion'
|
import Conversion from '@shared/Price/Conversion'
|
||||||
@ -20,7 +21,7 @@ const getEstGasFee = async (
|
|||||||
|
|
||||||
const gasPrice = await web3.eth.getGasPrice()
|
const gasPrice = await web3.eth.getGasPrice()
|
||||||
const gasLimit = await nftFactory?.estGasCreateNFT(address, nft)
|
const gasLimit = await nftFactory?.estGasCreateNFT(address, nft)
|
||||||
const gasFeeEth = web3.utils.fromWei(
|
const gasFeeEth = Web3.utils.fromWei(
|
||||||
(+gasPrice * +gasLimit).toString(),
|
(+gasPrice * +gasLimit).toString(),
|
||||||
'ether'
|
'ether'
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -25,11 +24,12 @@
|
|||||||
fill: var(--brand-alert-green);
|
fill: var(--brand-alert-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.copied::after {
|
.action {
|
||||||
content: 'Copied!';
|
display: flex;
|
||||||
position: absolute;
|
gap: 5px;
|
||||||
top: -150%;
|
}
|
||||||
left: -140%;
|
|
||||||
|
.feedback {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
color: var(--brand-alert-green);
|
color: var(--brand-alert-green);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@ export default function Copy({ text }: { text: string }): ReactElement {
|
|||||||
onSuccess={() => setIsCopied(true)}
|
onSuccess={() => setIsCopied(true)}
|
||||||
className={`${styles.button} ${isCopied ? styles.copied : ''}`}
|
className={`${styles.button} ${isCopied ? styles.copied : ''}`}
|
||||||
>
|
>
|
||||||
|
<div className={styles.action}>
|
||||||
<IconCopy className={styles.icon} />
|
<IconCopy className={styles.icon} />
|
||||||
|
{isCopied && <span className={styles.feedback}>Copied!</span>}
|
||||||
|
</div>
|
||||||
</Clipboard>
|
</Clipboard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ const getReceipts = gql`
|
|||||||
id
|
id
|
||||||
nft {
|
nft {
|
||||||
address
|
address
|
||||||
|
owner
|
||||||
}
|
}
|
||||||
tx
|
tx
|
||||||
timestamp
|
timestamp
|
||||||
@ -25,7 +26,13 @@ const getReceipts = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default function EditHistory(): ReactElement {
|
export default function EditHistory({
|
||||||
|
receipts,
|
||||||
|
setReceipts
|
||||||
|
}: {
|
||||||
|
receipts: ReceiptData[]
|
||||||
|
setReceipts: (receipts: ReceiptData[]) => void
|
||||||
|
}): ReactElement {
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
|
|
||||||
function getUpdateType(type: string): string {
|
function getUpdateType(type: string): string {
|
||||||
@ -66,13 +73,11 @@ export default function EditHistory(): ReactElement {
|
|||||||
//
|
//
|
||||||
// 2. Construct display data based on fetched data.
|
// 2. Construct display data based on fetched data.
|
||||||
//
|
//
|
||||||
const [receipts, setReceipts] = useState<ReceiptData[]>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data || data.nftUpdates.length === 0) return
|
if (!data || data.nftUpdates.length === 0) return
|
||||||
const receiptCollection = data.nftUpdates
|
const receiptCollection = data.nftUpdates
|
||||||
setReceipts(receiptCollection)
|
setReceipts(receiptCollection)
|
||||||
}, [data])
|
}, [data, setReceipts])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
.meta {
|
|
||||||
margin-bottom: calc(var(--spacer) / 1.5);
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset {
|
|
||||||
margin-left: -2rem;
|
|
||||||
margin-right: -2rem;
|
|
||||||
padding-left: 2rem;
|
|
||||||
padding-right: 3rem;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
margin-bottom: calc(var(--spacer) / 1.5);
|
|
||||||
padding-bottom: calc(var(--spacer) / 1.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
|
||||||
.asset {
|
|
||||||
margin-top: -0.65rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.assetType {
|
|
||||||
display: inline-block;
|
|
||||||
border-right: 1px solid var(--border-color);
|
|
||||||
padding-right: calc(var(--spacer) / 3.5);
|
|
||||||
margin-right: calc(var(--spacer) / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.datatoken {
|
|
||||||
white-space: pre;
|
|
||||||
margin-right: calc(var(--spacer) / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.byline {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.updated {
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
}
|
|
||||||
|
|
||||||
.addWrap {
|
|
||||||
padding-left: calc(var(--spacer) / 5);
|
|
||||||
border-left: 1px solid var(--border-color);
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add {
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import { useAsset } from '@context/Asset'
|
|
||||||
import { useWeb3 } from '@context/Web3'
|
|
||||||
import ExplorerLink from '@shared/ExplorerLink'
|
|
||||||
import Publisher from '@shared/Publisher'
|
|
||||||
import AddToken from '@shared/AddToken'
|
|
||||||
import Time from '@shared/atoms/Time'
|
|
||||||
import AssetType from '@shared/AssetType'
|
|
||||||
import styles from './MetaMain.module.css'
|
|
||||||
import { getServiceByName } from '@utils/ddo'
|
|
||||||
import { Asset } from '@oceanprotocol/lib'
|
|
||||||
|
|
||||||
export default function MetaMain({ ddo }: { ddo: Asset }): ReactElement {
|
|
||||||
const { isAssetNetwork } = useAsset()
|
|
||||||
const { web3ProviderInfo } = useWeb3()
|
|
||||||
|
|
||||||
const isCompute = Boolean(getServiceByName(ddo, 'compute'))
|
|
||||||
const accessType = isCompute ? 'compute' : 'access'
|
|
||||||
const blockscoutNetworks = [1287, 2021000, 2021001, 44787, 246, 1285]
|
|
||||||
const isBlockscoutExplorer = blockscoutNetworks.includes(ddo?.chainId)
|
|
||||||
|
|
||||||
const dataTokenName = ddo?.datatokens[0]?.name
|
|
||||||
const dataTokenSymbol = ddo?.datatokens[0]?.symbol
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside className={styles.meta}>
|
|
||||||
<header className={styles.asset}>
|
|
||||||
<AssetType
|
|
||||||
type={ddo?.metadata.type}
|
|
||||||
accessType={accessType}
|
|
||||||
className={styles.assetType}
|
|
||||||
/>
|
|
||||||
<ExplorerLink
|
|
||||||
className={styles.datatoken}
|
|
||||||
networkId={ddo?.chainId}
|
|
||||||
path={
|
|
||||||
isBlockscoutExplorer
|
|
||||||
? `tokens/${ddo?.services[0].datatokenAddress}`
|
|
||||||
: `token/${ddo?.services[0].datatokenAddress}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{`${dataTokenName} — ${dataTokenSymbol}`}
|
|
||||||
</ExplorerLink>
|
|
||||||
|
|
||||||
{web3ProviderInfo?.name === 'MetaMask' && isAssetNetwork && (
|
|
||||||
<span className={styles.addWrap}>
|
|
||||||
<AddToken
|
|
||||||
address={ddo?.services[0].datatokenAddress}
|
|
||||||
symbol={(ddo as Asset)?.datatokens[0]?.symbol}
|
|
||||||
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/datatoken.png"
|
|
||||||
text={`Add ${(ddo as Asset)?.datatokens[0]?.symbol} to wallet`}
|
|
||||||
className={styles.add}
|
|
||||||
minimal
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div className={styles.byline}>
|
|
||||||
Published By <Publisher account={(ddo as Asset)?.nft?.owner} />
|
|
||||||
<p>
|
|
||||||
<Time date={ddo?.metadata.created} relative />
|
|
||||||
{ddo?.metadata.created !== ddo?.metadata.updated && (
|
|
||||||
<>
|
|
||||||
{' — '}
|
|
||||||
<span className={styles.updated}>
|
|
||||||
updated <Time date={ddo?.metadata.updated} relative />
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding-left: calc(var(--spacer) / 2);
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datatoken {
|
||||||
|
white-space: pre;
|
||||||
|
margin-right: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addWrap {
|
||||||
|
padding-left: calc(var(--spacer) / 5);
|
||||||
|
border-left: 1px solid var(--border-color);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
}
|
54
src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx
Normal file
54
src/components/Asset/AssetContent/MetaMain/MetaAsset.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { useAsset } from '@context/Asset'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
|
import AddToken from '@shared/AddToken'
|
||||||
|
import ExplorerLink from '@shared/ExplorerLink'
|
||||||
|
import Publisher from '@shared/Publisher'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './MetaAsset.module.css'
|
||||||
|
|
||||||
|
export default function MetaAsset({
|
||||||
|
asset,
|
||||||
|
isBlockscoutExplorer
|
||||||
|
}: {
|
||||||
|
asset: Asset
|
||||||
|
isBlockscoutExplorer: boolean
|
||||||
|
}): ReactElement {
|
||||||
|
const { isAssetNetwork } = useAsset()
|
||||||
|
const { web3ProviderInfo } = useWeb3()
|
||||||
|
|
||||||
|
const dataTokenSymbol = asset?.datatokens[0]?.symbol
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<span className={styles.owner}>
|
||||||
|
Owned by <Publisher account={asset?.nft?.owner} />
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<ExplorerLink
|
||||||
|
className={styles.datatoken}
|
||||||
|
networkId={asset?.chainId}
|
||||||
|
path={
|
||||||
|
isBlockscoutExplorer
|
||||||
|
? `tokens/${asset?.services[0].datatokenAddress}`
|
||||||
|
: `token/${asset?.services[0].datatokenAddress}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{`Accessed with ${dataTokenSymbol}`}
|
||||||
|
</ExplorerLink>
|
||||||
|
{web3ProviderInfo?.name === 'MetaMask' && isAssetNetwork && (
|
||||||
|
<span className={styles.addWrap}>
|
||||||
|
<AddToken
|
||||||
|
address={asset?.services[0].datatokenAddress}
|
||||||
|
symbol={(asset as Asset)?.datatokens[0]?.symbol}
|
||||||
|
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/datatoken.png"
|
||||||
|
text={`Add ${(asset as Asset)?.datatokens[0]?.symbol} to wallet`}
|
||||||
|
className={styles.add}
|
||||||
|
minimal
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
.wrapper {
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assetType {
|
||||||
|
display: inline-block;
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
padding-right: calc(var(--spacer) / 3.5);
|
||||||
|
margin-right: calc(var(--spacer) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.byline {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.updated {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
}
|
47
src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx
Normal file
47
src/components/Asset/AssetContent/MetaMain/MetaInfo.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
|
import AssetType from '@shared/AssetType'
|
||||||
|
import Time from '@shared/atoms/Time'
|
||||||
|
import Publisher from '@shared/Publisher'
|
||||||
|
import { getServiceByName } from '@utils/ddo'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './MetaInfo.module.css'
|
||||||
|
|
||||||
|
export default function MetaInfo({
|
||||||
|
asset,
|
||||||
|
nftPublisher
|
||||||
|
}: {
|
||||||
|
asset: Asset
|
||||||
|
nftPublisher: string
|
||||||
|
}): ReactElement {
|
||||||
|
const isCompute = Boolean(getServiceByName(asset, 'compute'))
|
||||||
|
const accessType = isCompute ? 'compute' : 'access'
|
||||||
|
const nftOwner = asset?.nft?.owner
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<AssetType
|
||||||
|
type={asset?.metadata.type}
|
||||||
|
accessType={accessType}
|
||||||
|
className={styles.assetType}
|
||||||
|
/>
|
||||||
|
<div className={styles.byline}>
|
||||||
|
<p>
|
||||||
|
Published <Time date={asset?.metadata.created} relative />
|
||||||
|
{nftPublisher && nftPublisher !== nftOwner && (
|
||||||
|
<span>
|
||||||
|
{' by '} <Publisher account={nftPublisher} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{asset?.metadata.created !== asset?.metadata.updated && (
|
||||||
|
<>
|
||||||
|
{' — '}
|
||||||
|
<span className={styles.updated}>
|
||||||
|
updated <Time date={asset?.metadata.updated} relative />
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper img {
|
||||||
|
margin: 0;
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address button::after {
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
margin-top: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.links a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback {
|
||||||
|
padding-top: calc(var(--spacer) / 3);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
79
src/components/Asset/AssetContent/MetaMain/NftTooltip.tsx
Normal file
79
src/components/Asset/AssetContent/MetaMain/NftTooltip.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import Copy from '@shared/atoms/Copy'
|
||||||
|
import External from '@images/external.svg'
|
||||||
|
import ExplorerLink from '@shared/ExplorerLink'
|
||||||
|
import { NftMetadata } from '@utils/nft'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './NftTooltip.module.css'
|
||||||
|
import explorerLinkStyles from '@shared/ExplorerLink/index.module.css'
|
||||||
|
import { accountTruncate } from '@utils/web3'
|
||||||
|
|
||||||
|
export default function NftTooltip({
|
||||||
|
nft,
|
||||||
|
address,
|
||||||
|
chainId,
|
||||||
|
isBlockscoutExplorer
|
||||||
|
}: {
|
||||||
|
nft: NftMetadata
|
||||||
|
address: string
|
||||||
|
chainId: number
|
||||||
|
isBlockscoutExplorer: boolean
|
||||||
|
}): ReactElement {
|
||||||
|
// Currently Ocean NFTs are not displayed correctly on OpenSea
|
||||||
|
// Code prepared to easily integrate this feature once this is fixed
|
||||||
|
//
|
||||||
|
// Supported OpeanSea networks:
|
||||||
|
// https://support.opensea.io/hc/en-us/articles/4404027708051-Which-blockchains-does-OpenSea-support-
|
||||||
|
const openseaNetworks = [1, 137]
|
||||||
|
const openseaTestNetworks = [4]
|
||||||
|
const openSeaSupported = openseaNetworks
|
||||||
|
.concat(openseaTestNetworks)
|
||||||
|
.includes(chainId)
|
||||||
|
|
||||||
|
const openSeaBaseUri = openSeaSupported
|
||||||
|
? openseaTestNetworks.includes(chainId)
|
||||||
|
? 'https://testnets.opensea.io'
|
||||||
|
: 'https://opensea.io'
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
{nft && <img src={nft.image_data} alt={nft?.name} />}
|
||||||
|
<div className={styles.info}>
|
||||||
|
{nft && <h5>{nft.name}</h5>}
|
||||||
|
{address && (
|
||||||
|
<span title={address} className={styles.address}>
|
||||||
|
{accountTruncate(address)} <Copy text={address} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<div className={styles.links}>
|
||||||
|
{address && (
|
||||||
|
<ExplorerLink
|
||||||
|
networkId={chainId}
|
||||||
|
path={
|
||||||
|
isBlockscoutExplorer ? `tokens/${address}` : `token/${address}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
View on explorer
|
||||||
|
</ExplorerLink>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{openSeaSupported && nft && address && (
|
||||||
|
<a
|
||||||
|
href={`${openSeaBaseUri}/assets/${address}/1`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={explorerLinkStyles.link}
|
||||||
|
>
|
||||||
|
View on OpeanSea <External />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!nft?.image_data && (
|
||||||
|
<p className={styles.fallback}>
|
||||||
|
This Data NFT was not created on Ocean Market
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
44
src/components/Asset/AssetContent/MetaMain/index.module.css
Normal file
44
src/components/Asset/AssetContent/MetaMain/index.module.css
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.meta {
|
||||||
|
margin-left: calc(var(--spacer) * -1);
|
||||||
|
margin-right: calc(var(--spacer) * -1);
|
||||||
|
margin-bottom: calc(var(--spacer) / 1.5);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: calc(var(--spacer) * 2);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nftImage {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
width: calc(var(--spacer) * 2);
|
||||||
|
height: calc(var(--spacer) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nftImage img,
|
||||||
|
.nftImage > svg:first-of-type {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nftImage > svg:first-of-type {
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nftImage .tooltip {
|
||||||
|
position: absolute;
|
||||||
|
padding: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nftImage .tooltip svg {
|
||||||
|
fill: var(--font-color-text);
|
||||||
|
}
|
53
src/components/Asset/AssetContent/MetaMain/index.tsx
Normal file
53
src/components/Asset/AssetContent/MetaMain/index.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
|
import { decodeTokenURI } from '@utils/nft'
|
||||||
|
import MetaAsset from './MetaAsset'
|
||||||
|
import MetaInfo from './MetaInfo'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
|
import NftTooltip from './NftTooltip'
|
||||||
|
import Logo from '@shared/atoms/Logo'
|
||||||
|
|
||||||
|
export default function MetaMain({
|
||||||
|
asset,
|
||||||
|
nftPublisher
|
||||||
|
}: {
|
||||||
|
asset: Asset
|
||||||
|
nftPublisher: string
|
||||||
|
}): ReactElement {
|
||||||
|
const nftMetadata = decodeTokenURI(asset?.nft?.tokenURI)
|
||||||
|
|
||||||
|
const blockscoutNetworks = [1287, 2021000, 2021001, 44787, 246, 1285]
|
||||||
|
const isBlockscoutExplorer = blockscoutNetworks.includes(asset?.chainId)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className={styles.meta}>
|
||||||
|
<header className={styles.asset}>
|
||||||
|
<div className={styles.nftImage}>
|
||||||
|
{nftMetadata?.image_data ? (
|
||||||
|
<img src={nftMetadata?.image_data} alt={asset?.nft?.name} />
|
||||||
|
) : (
|
||||||
|
<Logo noWordmark />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(nftMetadata || asset?.nftAddress) && (
|
||||||
|
<Tooltip
|
||||||
|
className={styles.tooltip}
|
||||||
|
content={
|
||||||
|
<NftTooltip
|
||||||
|
nft={nftMetadata}
|
||||||
|
address={asset?.nftAddress}
|
||||||
|
chainId={asset?.chainId}
|
||||||
|
isBlockscoutExplorer={isBlockscoutExplorer}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<MetaAsset asset={asset} isBlockscoutExplorer={isBlockscoutExplorer} />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<MetaInfo asset={asset} nftPublisher={nftPublisher} />
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
composes: box from '@shared/atoms/Box.module.css';
|
composes: box from '@shared/atoms/Box.module.css';
|
||||||
|
padding-top: 0;
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,27 @@ import NetworkName from '@shared/NetworkName'
|
|||||||
import content from '../../../../content/purgatory.json'
|
import content from '../../../../content/purgatory.json'
|
||||||
import { AssetExtended } from 'src/@types/AssetExtended'
|
import { AssetExtended } from 'src/@types/AssetExtended'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
|
||||||
export default function AssetContent({
|
export default function AssetContent({
|
||||||
asset
|
asset
|
||||||
}: {
|
}: {
|
||||||
asset: AssetExtended
|
asset: AssetExtended
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { debug } = useUserPreferences()
|
|
||||||
const [isOwner, setIsOwner] = useState(false)
|
const [isOwner, setIsOwner] = useState(false)
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const { isInPurgatory, purgatoryData, owner, isAssetNetwork } = useAsset()
|
const { isInPurgatory, purgatoryData, owner, isAssetNetwork } = useAsset()
|
||||||
|
const { debug } = useUserPreferences()
|
||||||
|
const [receipts, setReceipts] = useState([])
|
||||||
|
const [nftPublisher, setNftPublisher] = useState<string>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNftPublisher(
|
||||||
|
Web3.utils.toChecksumAddress(
|
||||||
|
receipts?.find((e) => e.type === 'METADATA_CREATED')?.nft?.owner
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}, [receipts])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!accountId || !owner) return
|
if (!accountId || !owner) return
|
||||||
@ -43,11 +54,10 @@ export default function AssetContent({
|
|||||||
<article className={styles.grid}>
|
<article className={styles.grid}>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<MetaMain ddo={asset} />
|
<MetaMain asset={asset} nftPublisher={nftPublisher} />
|
||||||
{asset?.accessDetails?.datatoken !== null && (
|
{asset?.accessDetails?.datatoken !== null && (
|
||||||
<Bookmark did={asset?.id} />
|
<Bookmark did={asset?.id} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isInPurgatory === true ? (
|
{isInPurgatory === true ? (
|
||||||
<Alert
|
<Alert
|
||||||
title={content.asset.title}
|
title={content.asset.title}
|
||||||
@ -64,9 +74,8 @@ export default function AssetContent({
|
|||||||
<MetaSecondary ddo={asset} />
|
<MetaSecondary ddo={asset} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<MetaFull ddo={asset} />
|
<MetaFull ddo={asset} />
|
||||||
<EditHistory />
|
<EditHistory receipts={receipts} setReceipts={setReceipts} />
|
||||||
{debug === true && <DebugOutput title="DDO" output={asset} />}
|
{debug === true && <DebugOutput title="DDO" output={asset} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -181,6 +181,7 @@ export async function transformPublishFormToDdo(
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
nft: {
|
nft: {
|
||||||
|
...generateNftCreateData(values?.metadata.nft),
|
||||||
owner: accountId
|
owner: accountId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user