From 7c30d974ad4b655139acb640a28d13bb028acd5d Mon Sep 17 00:00:00 2001 From: Norbi Date: Thu, 18 Feb 2021 12:02:12 +0200 Subject: [PATCH 01/29] WIP --- .../organisms/AlgorithmContent/index.tsx | 112 ++++++++++ .../templates/PageAlgorithmDetails.tsx | 43 ++++ src/pages/algorithm/index.tsx | 19 ++ src/providers/Algorithm.tsx | 205 ++++++++++++++++++ 4 files changed, 379 insertions(+) create mode 100644 src/components/organisms/AlgorithmContent/index.tsx create mode 100644 src/components/templates/PageAlgorithmDetails.tsx create mode 100644 src/pages/algorithm/index.tsx create mode 100644 src/providers/Algorithm.tsx diff --git a/src/components/organisms/AlgorithmContent/index.tsx b/src/components/organisms/AlgorithmContent/index.tsx new file mode 100644 index 000000000..8891f7131 --- /dev/null +++ b/src/components/organisms/AlgorithmContent/index.tsx @@ -0,0 +1,112 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import { graphql, useStaticQuery } from 'gatsby' +import Markdown from '../../atoms/Markdown' +import MetaFull from '../AssetContent/MetaFull' +import MetaSecondary from '../AssetContent/MetaSecondary' +import styles from './index.module.css' +import AssetActions from '../AssetActions' +import { useUserPreferences } from '../../../providers/UserPreferences' +import Pricing from '../AssetContent/Pricing' +import { useOcean } from '@oceanprotocol/react' +import Bookmark from '../AssetContent/Bookmark' +import { useAsset } from '../../../providers/Asset' +import Alert from '../../atoms/Alert' +import Button from '../../atoms/Button' +import Edit from '../AssetActions/Edit' +import DebugOutput from '../../atoms/DebugOutput' +import MetaMain from '../AssetContent/MetaMain' +// import EditHistory from './EditHistory' + +export interface AlgorithmContentProps { + path?: string +} + +const contentQuery = graphql` + query ContentQuery { + purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) { + edges { + node { + childContentJson { + asset { + title + description + } + } + } + } + } + } +` + +export default function AlgorithmContent( + props: AlgorithmContentProps +): ReactElement { + const data = useStaticQuery(contentQuery) + const content = data.purgatory.edges[0].node.childContentJson.asset + const { debug } = useUserPreferences() + const { accountId } = useOcean() + const { owner, isInPurgatory, purgatoryData } = useAsset() + const [showPricing, setShowPricing] = useState(false) + const [showEdit, setShowEdit] = useState() + const { ddo, price, metadata } = useAsset() + const isOwner = accountId === owner + + useEffect(() => { + if (!price) return + setShowPricing(isOwner && price.address === '') + }, [isOwner, price]) + + function handleEditButton() { + // move user's focus to top of screen + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) + setShowEdit(true) + } + + return showEdit ? ( + + ) : ( +
+
+ {showPricing && } +
+ + + + {isInPurgatory ? ( + + ) : ( + <> + + + + + {isOwner && ( +
+ +
+ )} + + )} + + + {/* */} + {debug === true && } +
+
+ +
+ +
+
+ ) +} diff --git a/src/components/templates/PageAlgorithmDetails.tsx b/src/components/templates/PageAlgorithmDetails.tsx new file mode 100644 index 000000000..8151ff8db --- /dev/null +++ b/src/components/templates/PageAlgorithmDetails.tsx @@ -0,0 +1,43 @@ +import React, { useState, useEffect, ReactElement } from 'react' +import { Router } from '@reach/router' +import AssetContent from '../organisms/AssetContent' +import Page from './Page' +import Alert from '../atoms/Alert' +import Loader from '../atoms/Loader' +import { useAlgorithm } from '../../providers/Algorithm' + +export default function PageTemplateAlgorithmDetails({ + uri +}: { + uri: string +}): ReactElement { + const { ddo, title, error, isInPurgatory } = useAlgorithm() + const [pageTitle, setPageTitle] = useState() + + useEffect(() => { + if (!ddo || error) { + setPageTitle('Could not retrieve asset') + return + } + + setPageTitle(isInPurgatory ? '' : title) + }, [ddo, error, isInPurgatory, title]) + + return ddo ? ( + <> + + + + + + + ) : error ? ( + + + + ) : ( + + + + ) +} diff --git a/src/pages/algorithm/index.tsx b/src/pages/algorithm/index.tsx new file mode 100644 index 000000000..99225b603 --- /dev/null +++ b/src/pages/algorithm/index.tsx @@ -0,0 +1,19 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import { PageProps } from 'gatsby' +import PageTemplateAlgorithmDetails from '../../components/templates/PageAlgorithmDetails' +import AlgorithmProvider from '../../providers/Algorithm' + +export default function PageGatsbyAssetDetails(props: PageProps): ReactElement { + const [did, setDid] = useState() + + useEffect(() => { + console.log('did', props.location.pathname.split('/')[2]) + setDid(props.location.pathname.split('/')[2]) + }, [props.location.pathname]) + + return ( + + + + ) +} diff --git a/src/providers/Algorithm.tsx b/src/providers/Algorithm.tsx new file mode 100644 index 000000000..5ceb00069 --- /dev/null +++ b/src/providers/Algorithm.tsx @@ -0,0 +1,205 @@ +import React, { + useContext, + useState, + useEffect, + createContext, + ReactElement, + useCallback, + ReactNode +} from 'react' +import { Logger, DDO, BestPrice } from '@oceanprotocol/lib' +import { PurgatoryData } from '@oceanprotocol/lib/dist/node/ddo/interfaces/PurgatoryData' +import { getDataTokenPrice, useOcean } from '@oceanprotocol/react' +import getAssetPurgatoryData from '../utils/purgatory' +import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper' +import axios, { CancelToken } from 'axios' +import { retrieveDDO } from '../utils/aquarius' +import { MetadataMarket } from '../@types/MetaData' + +interface AlgorithmProviderValue { + isInPurgatory: boolean + purgatoryData: PurgatoryData + ddo: DDO | undefined + did: string | undefined + metadata: MetadataMarket | undefined + title: string | undefined + owner: string | undefined + price: BestPrice | undefined + error?: string + refreshInterval: number + refreshDdo: (token?: CancelToken) => Promise + refreshPrice: () => Promise +} + +const AssetContext = createContext({} as AlgorithmProviderValue) + +const refreshInterval = 10000 // 10 sec. + +function AlgorithmProvider({ + asset, + children +}: { + asset: string | DDO + children: ReactNode +}): ReactElement { + const { ocean, status, config, networkId } = useOcean() + const [isInPurgatory, setIsInPurgatory] = useState(false) + const [purgatoryData, setPurgatoryData] = useState() + const [ddo, setDDO] = useState() + const [did, setDID] = useState() + const [metadata, setMetadata] = useState() + const [title, setTitle] = useState() + const [price, setPrice] = useState() + const [owner, setOwner] = useState() + const [error, setError] = useState() + + const refreshPrice = useCallback(async () => { + if ( + !ddo || + status !== 1 || + networkId !== (config as ConfigHelperConfig).networkId + ) + return + + const newPrice = await getDataTokenPrice( + ocean, + ddo.dataToken, + ddo?.price?.type, + ddo.price.address + ) + setPrice(newPrice) + Logger.log(`Refreshed asset price: ${newPrice?.value}`, newPrice) + }, [ocean, config, ddo, networkId, status]) + + const fetchDdo = async (token?: CancelToken) => { + Logger.log('Init asset, get ddo') + const ddo = await retrieveDDO( + asset as string, + config.metadataCacheUri, + token + ) + + if (!ddo) { + setError( + `The DDO for ${asset} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.` + ) + } else { + setError(undefined) + } + return ddo + } + + const refreshDdo = async (token?: CancelToken) => { + const ddo = await fetchDdo(token) + Logger.debug('DDO', ddo) + setDDO(ddo) + } + // + // Get and set DDO based on passed DDO or DID + // + useEffect(() => { + if (!asset || !config?.metadataCacheUri) return + + const source = axios.CancelToken.source() + let isMounted = true + Logger.log('Init asset, get ddo') + + async function init() { + const ddo = await fetchDdo(source.token) + if (!isMounted) return + Logger.debug('DDO', ddo) + setDDO(ddo) + setDID(asset as string) + } + init() + return () => { + isMounted = false + source.cancel() + } + }, [asset, config?.metadataCacheUri]) + + useEffect(() => { + // Re-fetch price periodically, triggering re-calculation of everything + let isMounted = true + + const interval = setInterval(() => { + if (!isMounted) return + refreshPrice() + }, refreshInterval) + + return () => { + clearInterval(interval) + isMounted = false + } + }, [ddo, networkId, refreshPrice]) + + const setPurgatory = useCallback(async (did: string): Promise => { + if (!did) return + try { + const result = await getAssetPurgatoryData(did) + + if (result?.did !== undefined) { + setIsInPurgatory(true) + setPurgatoryData(result) + return + } + + setIsInPurgatory(false) + } catch (error) { + Logger.error(error) + } + }, []) + + const initMetadata = useCallback( + async (ddo: DDO): Promise => { + if (!ddo) return + + Logger.log('Init metadata') + // Set price & metadata from DDO first + setPrice(ddo.price) + const { attributes } = ddo.findServiceByType('metadata') + setMetadata((attributes as unknown) as MetadataMarket) + setTitle(attributes?.main.name) + setOwner(ddo.publicKey[0].owner) + setIsInPurgatory(ddo.isInPurgatory === 'true') + + await setPurgatory(ddo.id) + await refreshPrice() + }, + [refreshPrice, setPurgatory] + ) + + useEffect(() => { + if (!ddo) return + initMetadata(ddo) + }, [ddo, initMetadata]) + + return ( + + {children} + + ) +} + +// Helper hook to access the provider values +const useAlgorithm = (): AlgorithmProviderValue => useContext(AssetContext) + +export { AlgorithmProvider, useAlgorithm, AlgorithmProviderValue, AssetContext } +export default AlgorithmProvider From c731bab782ff384bf3036699e8ef92ace129d8fd Mon Sep 17 00:00:00 2001 From: Norbi Date: Thu, 18 Feb 2021 16:32:46 +0200 Subject: [PATCH 02/29] remove created files --- .../organisms/AlgorithmContent/index.tsx | 112 ---------- .../templates/PageAlgorithmDetails.tsx | 43 ---- src/pages/algorithm/index.tsx | 19 -- src/providers/Algorithm.tsx | 205 ------------------ 4 files changed, 379 deletions(-) delete mode 100644 src/components/organisms/AlgorithmContent/index.tsx delete mode 100644 src/components/templates/PageAlgorithmDetails.tsx delete mode 100644 src/pages/algorithm/index.tsx delete mode 100644 src/providers/Algorithm.tsx diff --git a/src/components/organisms/AlgorithmContent/index.tsx b/src/components/organisms/AlgorithmContent/index.tsx deleted file mode 100644 index 8891f7131..000000000 --- a/src/components/organisms/AlgorithmContent/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import { graphql, useStaticQuery } from 'gatsby' -import Markdown from '../../atoms/Markdown' -import MetaFull from '../AssetContent/MetaFull' -import MetaSecondary from '../AssetContent/MetaSecondary' -import styles from './index.module.css' -import AssetActions from '../AssetActions' -import { useUserPreferences } from '../../../providers/UserPreferences' -import Pricing from '../AssetContent/Pricing' -import { useOcean } from '@oceanprotocol/react' -import Bookmark from '../AssetContent/Bookmark' -import { useAsset } from '../../../providers/Asset' -import Alert from '../../atoms/Alert' -import Button from '../../atoms/Button' -import Edit from '../AssetActions/Edit' -import DebugOutput from '../../atoms/DebugOutput' -import MetaMain from '../AssetContent/MetaMain' -// import EditHistory from './EditHistory' - -export interface AlgorithmContentProps { - path?: string -} - -const contentQuery = graphql` - query ContentQuery { - purgatory: allFile(filter: { relativePath: { eq: "purgatory.json" } }) { - edges { - node { - childContentJson { - asset { - title - description - } - } - } - } - } - } -` - -export default function AlgorithmContent( - props: AlgorithmContentProps -): ReactElement { - const data = useStaticQuery(contentQuery) - const content = data.purgatory.edges[0].node.childContentJson.asset - const { debug } = useUserPreferences() - const { accountId } = useOcean() - const { owner, isInPurgatory, purgatoryData } = useAsset() - const [showPricing, setShowPricing] = useState(false) - const [showEdit, setShowEdit] = useState() - const { ddo, price, metadata } = useAsset() - const isOwner = accountId === owner - - useEffect(() => { - if (!price) return - setShowPricing(isOwner && price.address === '') - }, [isOwner, price]) - - function handleEditButton() { - // move user's focus to top of screen - window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) - setShowEdit(true) - } - - return showEdit ? ( - - ) : ( -
-
- {showPricing && } -
- - - - {isInPurgatory ? ( - - ) : ( - <> - - - - - {isOwner && ( -
- -
- )} - - )} - - - {/* */} - {debug === true && } -
-
- -
- -
-
- ) -} diff --git a/src/components/templates/PageAlgorithmDetails.tsx b/src/components/templates/PageAlgorithmDetails.tsx deleted file mode 100644 index 8151ff8db..000000000 --- a/src/components/templates/PageAlgorithmDetails.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { useState, useEffect, ReactElement } from 'react' -import { Router } from '@reach/router' -import AssetContent from '../organisms/AssetContent' -import Page from './Page' -import Alert from '../atoms/Alert' -import Loader from '../atoms/Loader' -import { useAlgorithm } from '../../providers/Algorithm' - -export default function PageTemplateAlgorithmDetails({ - uri -}: { - uri: string -}): ReactElement { - const { ddo, title, error, isInPurgatory } = useAlgorithm() - const [pageTitle, setPageTitle] = useState() - - useEffect(() => { - if (!ddo || error) { - setPageTitle('Could not retrieve asset') - return - } - - setPageTitle(isInPurgatory ? '' : title) - }, [ddo, error, isInPurgatory, title]) - - return ddo ? ( - <> - - - - - - - ) : error ? ( - - - - ) : ( - - - - ) -} diff --git a/src/pages/algorithm/index.tsx b/src/pages/algorithm/index.tsx deleted file mode 100644 index 99225b603..000000000 --- a/src/pages/algorithm/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { ReactElement, useEffect, useState } from 'react' -import { PageProps } from 'gatsby' -import PageTemplateAlgorithmDetails from '../../components/templates/PageAlgorithmDetails' -import AlgorithmProvider from '../../providers/Algorithm' - -export default function PageGatsbyAssetDetails(props: PageProps): ReactElement { - const [did, setDid] = useState() - - useEffect(() => { - console.log('did', props.location.pathname.split('/')[2]) - setDid(props.location.pathname.split('/')[2]) - }, [props.location.pathname]) - - return ( - - - - ) -} diff --git a/src/providers/Algorithm.tsx b/src/providers/Algorithm.tsx deleted file mode 100644 index 5ceb00069..000000000 --- a/src/providers/Algorithm.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, { - useContext, - useState, - useEffect, - createContext, - ReactElement, - useCallback, - ReactNode -} from 'react' -import { Logger, DDO, BestPrice } from '@oceanprotocol/lib' -import { PurgatoryData } from '@oceanprotocol/lib/dist/node/ddo/interfaces/PurgatoryData' -import { getDataTokenPrice, useOcean } from '@oceanprotocol/react' -import getAssetPurgatoryData from '../utils/purgatory' -import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper' -import axios, { CancelToken } from 'axios' -import { retrieveDDO } from '../utils/aquarius' -import { MetadataMarket } from '../@types/MetaData' - -interface AlgorithmProviderValue { - isInPurgatory: boolean - purgatoryData: PurgatoryData - ddo: DDO | undefined - did: string | undefined - metadata: MetadataMarket | undefined - title: string | undefined - owner: string | undefined - price: BestPrice | undefined - error?: string - refreshInterval: number - refreshDdo: (token?: CancelToken) => Promise - refreshPrice: () => Promise -} - -const AssetContext = createContext({} as AlgorithmProviderValue) - -const refreshInterval = 10000 // 10 sec. - -function AlgorithmProvider({ - asset, - children -}: { - asset: string | DDO - children: ReactNode -}): ReactElement { - const { ocean, status, config, networkId } = useOcean() - const [isInPurgatory, setIsInPurgatory] = useState(false) - const [purgatoryData, setPurgatoryData] = useState() - const [ddo, setDDO] = useState() - const [did, setDID] = useState() - const [metadata, setMetadata] = useState() - const [title, setTitle] = useState() - const [price, setPrice] = useState() - const [owner, setOwner] = useState() - const [error, setError] = useState() - - const refreshPrice = useCallback(async () => { - if ( - !ddo || - status !== 1 || - networkId !== (config as ConfigHelperConfig).networkId - ) - return - - const newPrice = await getDataTokenPrice( - ocean, - ddo.dataToken, - ddo?.price?.type, - ddo.price.address - ) - setPrice(newPrice) - Logger.log(`Refreshed asset price: ${newPrice?.value}`, newPrice) - }, [ocean, config, ddo, networkId, status]) - - const fetchDdo = async (token?: CancelToken) => { - Logger.log('Init asset, get ddo') - const ddo = await retrieveDDO( - asset as string, - config.metadataCacheUri, - token - ) - - if (!ddo) { - setError( - `The DDO for ${asset} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.` - ) - } else { - setError(undefined) - } - return ddo - } - - const refreshDdo = async (token?: CancelToken) => { - const ddo = await fetchDdo(token) - Logger.debug('DDO', ddo) - setDDO(ddo) - } - // - // Get and set DDO based on passed DDO or DID - // - useEffect(() => { - if (!asset || !config?.metadataCacheUri) return - - const source = axios.CancelToken.source() - let isMounted = true - Logger.log('Init asset, get ddo') - - async function init() { - const ddo = await fetchDdo(source.token) - if (!isMounted) return - Logger.debug('DDO', ddo) - setDDO(ddo) - setDID(asset as string) - } - init() - return () => { - isMounted = false - source.cancel() - } - }, [asset, config?.metadataCacheUri]) - - useEffect(() => { - // Re-fetch price periodically, triggering re-calculation of everything - let isMounted = true - - const interval = setInterval(() => { - if (!isMounted) return - refreshPrice() - }, refreshInterval) - - return () => { - clearInterval(interval) - isMounted = false - } - }, [ddo, networkId, refreshPrice]) - - const setPurgatory = useCallback(async (did: string): Promise => { - if (!did) return - try { - const result = await getAssetPurgatoryData(did) - - if (result?.did !== undefined) { - setIsInPurgatory(true) - setPurgatoryData(result) - return - } - - setIsInPurgatory(false) - } catch (error) { - Logger.error(error) - } - }, []) - - const initMetadata = useCallback( - async (ddo: DDO): Promise => { - if (!ddo) return - - Logger.log('Init metadata') - // Set price & metadata from DDO first - setPrice(ddo.price) - const { attributes } = ddo.findServiceByType('metadata') - setMetadata((attributes as unknown) as MetadataMarket) - setTitle(attributes?.main.name) - setOwner(ddo.publicKey[0].owner) - setIsInPurgatory(ddo.isInPurgatory === 'true') - - await setPurgatory(ddo.id) - await refreshPrice() - }, - [refreshPrice, setPurgatory] - ) - - useEffect(() => { - if (!ddo) return - initMetadata(ddo) - }, [ddo, initMetadata]) - - return ( - - {children} - - ) -} - -// Helper hook to access the provider values -const useAlgorithm = (): AlgorithmProviderValue => useContext(AssetContext) - -export { AlgorithmProvider, useAlgorithm, AlgorithmProviderValue, AssetContext } -export default AlgorithmProvider From 14942b578556d9151f4742efc9e039143278eca0 Mon Sep 17 00:00:00 2001 From: Norbi Date: Fri, 19 Feb 2021 16:25:49 +0200 Subject: [PATCH 03/29] use action view changes by asset type --- .../organisms/AssetActions/Compute.tsx | 35 +++++++++++++------ .../organisms/AssetActions/Consume.tsx | 29 +++++++++------ src/providers/Asset.tsx | 4 +++ 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/components/organisms/AssetActions/Compute.tsx b/src/components/organisms/AssetActions/Compute.tsx index 2d31b504c..748fa0cb0 100644 --- a/src/components/organisms/AssetActions/Compute.tsx +++ b/src/components/organisms/AssetActions/Compute.tsx @@ -17,6 +17,7 @@ import Input from '../../atoms/Input' import Alert from '../../atoms/Alert' import { useSiteMetadata } from '../../../hooks/useSiteMetadata' import checkPreviousOrder from '../../../utils/checkPreviousOrder' +import { useAsset } from '../../../providers/Asset' export default function Compute({ ddo, @@ -29,6 +30,7 @@ export default function Compute({ }): ReactElement { const { marketFeeAddress } = useSiteMetadata() + const { type } = useAsset() const { ocean, accountId } = useOcean() const { compute, isLoading, computeStepText, computeError } = useCompute() const { buyDT, dtSymbol } = usePricing(ddo) @@ -129,16 +131,29 @@ export default function Compute({ - x.name)} - onChange={handleSelectChange} - /> + {type === 'algorithm' ? ( + x)} + onChange={handleSelectChange} + /> + ) : ( + x.name)} + onChange={handleSelectChange} + /> + )}
diff --git a/src/components/organisms/AssetActions/Consume.tsx b/src/components/organisms/AssetActions/Consume.tsx index 42b4b4bdb..f4cb7bd4e 100644 --- a/src/components/organisms/AssetActions/Consume.tsx +++ b/src/components/organisms/AssetActions/Consume.tsx @@ -48,7 +48,7 @@ export default function Consume({ const { marketFeeAddress } = useSiteMetadata() const [hasPreviousOrder, setHasPreviousOrder] = useState(false) const [previousOrderId, setPreviousOrderId] = useState() - const { isInPurgatory, price } = useAsset() + const { isInPurgatory, price, type } = useAsset() const { buyDT, pricingStepText, pricingError, pricingIsLoading } = usePricing( ddo ) @@ -157,16 +157,23 @@ export default function Consume({
-
- {isConsumable ? ( - - ) : ( -
- There is not enough liquidity in the pool to buy this data set. -
- )} - {!isInPurgatory && } -
+ {type === 'algorithm' ? ( +
+ This asset can not be downloaded, it can only be used in a compute + job +
+ ) : ( +
+ {isConsumable ? ( + + ) : ( +
+ There is not enough liquidity in the pool to buy this data set. +
+ )} + {!isInPurgatory && } +
+ )}