import React, { ReactElement, useEffect, useState } from 'react' import FileIcon from '@shared/FileIcon' import Price from '@shared/Price' import { useAsset } from '@context/Asset' import ButtonBuy from '../ButtonBuy' import { secondsToString } from '@utils/ddo' import AlgorithmDatasetsListForCompute from '../Compute/AlgorithmDatasetsListForCompute' import styles from './index.module.css' import { FileInfo, LoggerInstance, UserCustomParameters, ZERO_ADDRESS } from '@oceanprotocol/lib' import { order } from '@utils/order' import { downloadFile } from '@utils/provider' import { getOrderFeedback } from '@utils/feedback' import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing' import { toast } from 'react-toastify' import { useIsMounted } from '@hooks/useIsMounted' import { useMarketMetadata } from '@context/MarketMetadata' import Alert from '@shared/atoms/Alert' import Loader from '@shared/atoms/Loader' import { useAccount, useSigner } from 'wagmi' import useNetworkMetadata from '@hooks/useNetworkMetadata' import ConsumerParameters, { parseConsumerParameterValues } from '../ConsumerParameters' import { Form, Formik, useFormikContext } from 'formik' import { getDownloadValidationSchema } from './_validation' import { getDefaultValues } from '../ConsumerParameters/FormConsumerParameters' export default function Download({ asset, file, isBalanceSufficient, dtBalance, fileIsLoading, consumableFeedback }: { asset: AssetExtended file: FileInfo isBalanceSufficient: boolean dtBalance: string fileIsLoading?: boolean consumableFeedback?: string }): ReactElement { const { address: accountId, isConnected } = useAccount() const { data: signer } = useSigner() const { isSupportedOceanNetwork } = useNetworkMetadata() const { getOpcFeeForToken } = useMarketMetadata() const { isInPurgatory, isAssetNetwork } = useAsset() const isMounted = useIsMounted() const [isDisabled, setIsDisabled] = useState(true) const [hasDatatoken, setHasDatatoken] = useState(false) const [statusText, setStatusText] = useState('') const [isLoading, setIsLoading] = useState(false) const [isPriceLoading, setIsPriceLoading] = useState(false) const [isOwned, setIsOwned] = useState(false) const [validOrderTx, setValidOrderTx] = useState('') const [isOrderDisabled, setIsOrderDisabled] = useState(false) const [orderPriceAndFees, setOrderPriceAndFees] = useState() const [retry, setRetry] = useState(false) const isUnsupportedPricing = !asset?.accessDetails || !asset.services.length || asset?.stats?.price?.value === undefined || asset?.accessDetails?.type === 'NOT_SUPPORTED' || (asset?.accessDetails?.type === 'fixed' && !asset?.accessDetails?.baseToken?.symbol) useEffect(() => { Number(asset?.nft.state) === 4 && setIsOrderDisabled(true) }, [asset?.nft.state]) useEffect(() => { if (isUnsupportedPricing) return setIsOwned(asset?.accessDetails?.isOwned || false) setValidOrderTx(asset?.accessDetails?.validOrderTx || '') // get full price and fees async function init() { if ( asset.accessDetails.addressOrId === ZERO_ADDRESS || asset.accessDetails.type === 'free' ) return try { !orderPriceAndFees && setIsPriceLoading(true) const _orderPriceAndFees = await getOrderPriceAndFees( asset, ZERO_ADDRESS ) setOrderPriceAndFees(_orderPriceAndFees) !orderPriceAndFees && setIsPriceLoading(false) } catch (error) { LoggerInstance.error('getOrderPriceAndFees', error) setIsPriceLoading(false) } } init() /** * we listen to the assets' changes to get the most updated price * based on the asset and the poolData's information. * Not adding isLoading and getOpcFeeForToken because we set these here. It is a compromise */ // eslint-disable-next-line react-hooks/exhaustive-deps }, [asset, getOpcFeeForToken, isUnsupportedPricing]) useEffect(() => { setHasDatatoken(Number(dtBalance) >= 1) }, [dtBalance]) useEffect(() => { if ( (asset?.accessDetails?.type === 'fixed' && !orderPriceAndFees) || !isMounted || !accountId || isUnsupportedPricing ) return /** * disabled in these cases: * - if the asset is not purchasable * - if the user is on the wrong network * - if user balance is not sufficient * - if user has no datatokens */ const isDisabled = !asset?.accessDetails.isPurchasable || !isAssetNetwork || ((!isBalanceSufficient || !isAssetNetwork) && !isOwned && !hasDatatoken) setIsDisabled(isDisabled) }, [ isMounted, asset?.accessDetails, isBalanceSufficient, isAssetNetwork, hasDatatoken, accountId, isOwned, isUnsupportedPricing, orderPriceAndFees ]) async function handleOrderOrDownload(dataParams?: UserCustomParameters) { setIsLoading(true) try { if (isOwned) { setStatusText( getOrderFeedback( asset.accessDetails.baseToken?.symbol, asset.accessDetails.datatoken?.symbol )[3] ) await downloadFile(signer, asset, accountId, validOrderTx, dataParams) } else { setStatusText( getOrderFeedback( asset.accessDetails.baseToken?.symbol, asset.accessDetails.datatoken?.symbol )[asset.accessDetails.type === 'fixed' ? 2 : 1] ) const orderTx = await order( signer, asset, orderPriceAndFees, accountId, hasDatatoken ) const tx = await orderTx.wait() if (!tx) { throw new Error() } setIsOwned(true) setValidOrderTx(tx.transactionHash) } } catch (error) { LoggerInstance.error(error) setRetry(true) const message = isOwned ? 'Failed to download file!' : 'An error occurred, please retry. Check console for more information.' toast.error(message) } setIsLoading(false) } const PurchaseButton = ({ isValid }: { isValid?: boolean }) => ( ) const AssetAction = ({ asset }: { asset: AssetExtended }) => { const { isValid } = useFormikContext() return (
{isOrderDisabled ? ( ) : ( <> {isUnsupportedPricing ? ( ) : ( <> {asset && } {!isInPurgatory && (
)} )} )}
) } return ( { const dataServiceParams = parseConsumerParameterValues( values?.dataServiceParams, asset.services[0].consumerParameters ) await handleOrderOrDownload(dataServiceParams) }} >
) }