Skip to content
This repository was archived by the owner on Aug 3, 2021. It is now read-only.

metadata refactor , example #88

Merged
merged 5 commits into from
Aug 19, 2020
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -64,22 +64,33 @@ export default function MyComponent() {
const { ocean, web3, account } = useOcean()

// Get metadata for this asset
const { title, metadata } = useMetadata(did)
const { title, metadata, isLoaded, getBestPrice } = useMetadata(did)
const [price, setPrice] = useState<string>()

// publish asset
const { publish, publishStep } = usePublish()

// consume asset
const { consume, consumeStep } = useConsume()

useEffect(() => {
async function init(): Promise<void> {
if (isLoaded) {
const price = await getBestPrice()
setPrice(price)
}
}
init()
}, [isLoaded])

async function handleDownload() {
await consume(did)
}

return (
<div>
<h1>{title}</h1>
<p>Price: {web3.utils.fromWei(metadata.main.price)}</p>
<p>Price: {price}</p>

<p>Your account: {account}</p>
<button onClick={handleDownload}>
3 changes: 2 additions & 1 deletion example/src/AllDdos.tsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { DDO } from '@oceanprotocol/lib'
import { useState } from 'react'
import { useEffect } from 'react'
import shortid from 'shortid'
import { MetadataExample } from './MetadataExample'
export function AllDdos() {
const { accountId, chainId, account, ocean } = useOcean()

@@ -32,7 +33,7 @@ export function AllDdos() {
{ddos?.map((ddo) => {
return (
<div key={shortid.generate()}>
{ddo.id}
<MetadataExample did={ddo.id} />
<br />
</div>
)
7 changes: 2 additions & 5 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import { ConsumeDdo } from './ConsumeDdo'
import WalletConnectProvider from '@walletconnect/web3-provider'
import Torus from '@toruslabs/torus-embed'
import { NetworkMonitor } from './NetworkMonitor'
import { MetadataExample } from './MetadataExample'

// factory Address needs to be updated each time you deploy the contract on local network
const config = {
@@ -53,11 +54,7 @@ function App() {
}, [])

return (
<OceanProvider
initialConfig={configRinkeby}
web3ModalOpts={web3ModalOpts}
marketFeeAddress={'0x4D156A2ef69ffdDC55838176C6712C90f60a2285'}
>
<OceanProvider initialConfig={configRinkeby} web3ModalOpts={web3ModalOpts}>
<div className="container">
<NetworkMonitor />
<div>
25 changes: 25 additions & 0 deletions example/src/MetadataExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useEffect, useState } from 'react'
import { useMetadata } from '@oceanprotocol/react'

export function MetadataExample({ did }: { did: string }) {
const { title, isLoaded, getBestPrice } = useMetadata(did)
const [price, setPrice] = useState<string>()

useEffect(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we not move this into the hook? So that const { bestPrice } = useMetadata(did) would work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm confused, you say not to move it in the hook but then you want to use it in the hook const { bestPrice } = useMetadata(did) ? or am i understanding it wrong?
I didn't add bestPrice to reduce number of calls but if i think more, you would use this in an asset details page were you need all the metadata. So i'll add bestPrice and keep getBestPrice in case you don't want to load the whole ddo with useMetadata(did) and you just want to get a price for a data token like const { getBestPrice } = useMetadata() ; const price = getBestPrice(dtAddress)
useMetadata has 3 uses:

  • useMetadata(did) : it gets the ddo and then loads all the values (title, metadata etc)
  • useMetadata(ddo) : it uses the passed ddo and the loads all the values, in case you already got a list of ddo, so you don't have to fetch the ddo once again
  • useMetadata() : loads nothing, useful for using functions like getBestPrice or getBestPool (maybe more in the future) with minimal calls

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah sorry, confusing wording indeed, I meant to move it into the hook and if there is any reason not to do it. So your change is perfect :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and your 3 uses explanation list here is super useful, we should add this to the readme in https://github.com/oceanprotocol/react/tree/main/src/hooks/useMetadata so it doesn't get lost

async function init(): Promise<void> {
if (isLoaded) {
const price = await getBestPrice()
setPrice(price)
}
}
init()
}, [isLoaded])

return (
<>
<div>
{title} - {price}
</div>
</>
)
}
78 changes: 46 additions & 32 deletions src/hooks/useMetadata/useMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,95 @@
import { useState, useEffect } from 'react'
import { DID, DDO, Metadata, MetadataStore, Logger } from '@oceanprotocol/lib'
import { DID, DDO, Metadata, Logger } from '@oceanprotocol/lib'
import { useOcean } from '../../providers'
import ProviderStatus from '../../providers/OceanProvider/ProviderStatus'
import { getBestDataTokenPrice, getCheapestPool } from '../../utils/dtUtils'

interface UseMetadata {
ddo: DDO
did: DID | string
metadata: Metadata
title: string
getDDO: (did: DID | string) => Promise<DDO>
getMetadata: (did: DID | string) => Promise<Metadata>
getTitle: (did: DID | string) => Promise<string>
getBestPrice: (dataTokenAddress: string) => Promise<string>
isLoaded: boolean
getBestPrice: (dataTokenAddress?: string) => Promise<string>
getBestPool: (
dataTokenAddress: string
dataTokenAddress?: string
) => Promise<{ poolAddress: string; poolPrice: string }>
}

function useMetadata(did?: DID | string): UseMetadata {
function useMetadata(did?: DID | string, ddo?: DDO): UseMetadata {
const { ocean, status, config, accountId } = useOcean()
const [ddo, setDDO] = useState<DDO | undefined>()
const [internalDdo, setDDO] = useState<DDO | undefined>()
const [internalDid, setDID] = useState<DID | string | undefined>()
const [metadata, setMetadata] = useState<Metadata | undefined>()
const [title, setTitle] = useState<string | undefined>()
const [isLoaded, setIsLoaded] = useState(false)

async function getDDO(did: DID | string): Promise<DDO> {
if (status === ProviderStatus.CONNECTED) {
const ddo = await ocean.metadatastore.retrieveDDO(did)
return ddo
}

// fallback hitting MetadataStore directly
const metadataStore = new MetadataStore(config.metadataStoreUri, Logger)
const ddo = await metadataStore.retrieveDDO(did)
return ddo
}

async function getBestPrice(dataTokenAddress: string): Promise<string> {
async function getBestPrice(dataTokenAddress?: string): Promise<string> {
if (!dataTokenAddress) dataTokenAddress = internalDdo.dataToken
return await getBestDataTokenPrice(ocean, accountId, dataTokenAddress)
}
async function getBestPool(
dataTokenAddress: string
): Promise<{ poolAddress: string; poolPrice: string }> {
if (!dataTokenAddress) dataTokenAddress = internalDdo.dataToken
return await getCheapestPool(ocean, accountId, dataTokenAddress)
}

async function getMetadata(did: DID | string): Promise<Metadata> {
const ddo = await getDDO(did)
if (!ddo) return
const metadata = ddo.findServiceByType('metadata')
async function getMetadata(): Promise<Metadata> {
if (!internalDdo) return
const metadata = internalDdo.findServiceByType('metadata')
return metadata.attributes
}

async function getTitle(did: DID | string): Promise<string> {
const metadata = await getMetadata(did)
async function getTitle(): Promise<string> {
const metadata = await getMetadata()
return metadata.main.name
}

useEffect(() => {
async function init(): Promise<void> {
if (!did) return
const ddo = await getDDO(did)
setDDO(ddo)

const metadata = await getMetadata(did)
setMetadata(metadata)
setTitle(metadata.main.name)
Logger.debug('meta init', status)
if (ocean && status === ProviderStatus.CONNECTED) {
if (ddo) {
setDDO(ddo)
setDID(ddo.id)
}
Logger.debug('meta init', did)
if (did && !ddo) {
const ddo = await getDDO(did)
Logger.debug('DDO', ddo)
setDDO(ddo)
setDID(did)
}
}
}
init()
}, [ocean])
}, [ocean, status])

useEffect(() => {
async function init(): Promise<void> {
if (internalDdo) {
const metadata = await getMetadata()
setMetadata(metadata)
setTitle(metadata.main.name)
setIsLoaded(true)
}
}
init()
}, [internalDdo])
return {
ddo,
ddo: internalDdo,
did: internalDid,
metadata,
title,
getDDO,
getMetadata,
getTitle,
isLoaded,
getBestPrice,
getBestPool
}