import React, { useContext, useState, useEffect, createContext, ReactElement, useCallback, ReactNode } from 'react' import { Asset, Config, LoggerInstance, Purgatory } from '@oceanprotocol/lib' import { CancelToken } from 'axios' import { retrieveAsset } from '@utils/aquarius' import { useWeb3 } from './Web3' import { useSiteMetadata } from '@hooks/useSiteMetadata' import { useCancelToken } from '@hooks/useCancelToken' import { getOceanConfig, getDevelopmentConfig } from '@utils/ocean' import { AssetExtended } from 'src/@types/AssetExtended' import { getAccessDetails } from '@utils/accessDetailsAndPricing' interface AssetProviderValue { isInPurgatory: boolean purgatoryData: Purgatory assetExtended: AssetExtended title: string owner: string accessDetails: AccessDetails error?: string refreshInterval: number isAssetNetwork: boolean oceanConfig: Config loading: boolean refreshAsset: (token?: CancelToken) => Promise } const AssetContext = createContext({} as AssetProviderValue) const refreshInterval = 10000 // 10 sec. function AssetProvider({ did, children }: { did: string children: ReactNode }): ReactElement { const { appConfig } = useSiteMetadata() const { networkId, accountId } = useWeb3() const [isInPurgatory, setIsInPurgatory] = useState(false) const [purgatoryData, setPurgatoryData] = useState() const [assetExtended, setAssetExtended] = useState() const [title, setTitle] = useState() const [accessDetails, setConsumeDetails] = useState() const [owner, setOwner] = useState() const [error, setError] = useState() const [loading, setLoading] = useState(false) const [isAssetNetwork, setIsAssetNetwork] = useState() const [oceanConfig, setOceanConfig] = useState() const newCancelToken = useCancelToken() const fetchAsset = useCallback( async (token?: CancelToken) => { LoggerInstance.log('[asset] Init asset, get assetExtended') setLoading(true) const assetExtended = await retrieveAsset(did, token) if (!assetExtended) { setError( `[asset] The assetExtended for ${did} was not found in MetadataCache. If you just published a new data set, wait some seconds and refresh this page.` ) } else { setError(undefined) } setLoading(false) return assetExtended }, [did] ) const refreshAsset = async (token?: CancelToken) => { setLoading(true) const assetExtended = await fetchAsset(token) LoggerInstance.debug('[asset] Got assetExtended', assetExtended) setAssetExtended(assetExtended) setLoading(false) } // ----------------------------------- // Get and set assetExtended based on passed DID // ----------------------------------- useEffect(() => { if (!did || !appConfig.metadataCacheUri) return let isMounted = true async function init() { const assetExtended = await fetchAsset(newCancelToken()) if (!isMounted || !assetExtended) return LoggerInstance.debug('[asset] Got assetExtended', assetExtended) setAssetExtended(assetExtended) setTitle(assetExtended.metadata.name) setOwner(assetExtended.nft.owner) setIsInPurgatory( (assetExtended.purgatory?.state as unknown as string) === 'true' ) setPurgatoryData(assetExtended.purgatory) } init() return () => { isMounted = false } }, [did, appConfig.metadataCacheUri, fetchAsset, newCancelToken]) // ----------------------------------- // Attach price to asset // ----------------------------------- const initPrice = useCallback( async (assetExtended: AssetExtended, accountId: string): Promise => { if (!assetExtended) return const accessDetails = await getAccessDetails( assetExtended.chainId, assetExtended.services[0].datatokenAddress, assetExtended.services[0].timeout, accountId ) assetExtended.accessDetails = accessDetails setConsumeDetails({ ...accessDetails }) setAssetExtended(assetExtended) }, [] ) useEffect(() => { if (!assetExtended) return initPrice(assetExtended, accountId) }, [accountId, assetExtended, initPrice]) // ----------------------------------- // Check user network against asset network // ----------------------------------- useEffect(() => { if (!networkId || !assetExtended) return const isAssetNetwork = networkId === assetExtended?.chainId setIsAssetNetwork(isAssetNetwork) }, [networkId, assetExtended]) // ----------------------------------- // Load ocean config based on asset network // ----------------------------------- useEffect(() => { if (!assetExtended?.chainId) return const oceanConfig = { ...getOceanConfig(assetExtended?.chainId), // add local dev values ...(assetExtended?.chainId === 8996 && { ...getDevelopmentConfig() }) } setOceanConfig(oceanConfig) }, [assetExtended]) return ( {children} ) } // Helper hook to access the provider values const useAsset = (): AssetProviderValue => useContext(AssetContext) export { AssetProvider, useAsset, AssetContext } export default AssetProvider