market/src/components/Asset/AssetActions/Download.tsx

290 lines
8.7 KiB
TypeScript

import React, { ReactElement, useEffect, useState } from 'react'
import FileIcon from '@shared/FileIcon'
import Price from '@shared/Price'
import { useAsset } from '@context/Asset'
import { useWeb3 } from '@context/Web3'
import ButtonBuy from '@shared/ButtonBuy'
import { secondsToString } from '@utils/ddo'
import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForCompute'
import styles from './Download.module.css'
import {
FileMetadata,
LoggerInstance,
ZERO_ADDRESS,
FixedRateExchange
} from '@oceanprotocol/lib'
import { order } from '@utils/order'
import { AssetExtended } from 'src/@types/AssetExtended'
import { buyDtFromPool } from '@utils/pool'
import { downloadFile } from '@utils/provider'
import { getCollectTokensFeedback, getOrderFeedback } from '@utils/feedback'
import { getOrderPriceAndFees } from '@utils/accessDetailsAndPricing'
import { OrderPriceAndFees } from 'src/@types/Price'
import { toast } from 'react-toastify'
import { gql, OperationResult } from 'urql'
import { fetchData, getQueryContext } from '@utils/subgraph'
import { getOceanConfig } from '@utils/ocean'
import { FixedRateExchanges } from 'src/@types/subgraph/FixedRateExchanges'
const FixedRateExchangesQuery = gql`
query FixedRateExchanges($user: String, $exchangeId: String) {
fixedRateExchanges(where: { owner: $user, exchangeId: $exchangeId }) {
id
owner {
id
}
exchangeId
baseTokenBalance
}
}
`
export default function Download({
asset,
file,
isBalanceSufficient,
dtBalance,
fileIsLoading,
consumableFeedback
}: {
asset: AssetExtended
file: FileMetadata
isBalanceSufficient: boolean
dtBalance: string
fileIsLoading?: boolean
consumableFeedback?: string
}): ReactElement {
const { accountId, web3 } = useWeb3()
const { isInPurgatory, isAssetNetwork } = useAsset()
const [isDisabled, setIsDisabled] = useState(true)
const [hasDatatoken, setHasDatatoken] = useState(false)
const [statusText, setStatusText] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [isOwned, setIsOwned] = useState(false)
const [validOrderTx, setValidOrderTx] = useState('')
const [isCollectLoading, setIsCollectLoading] = useState(false)
const [baseTokenBalance, setBaseTokenBalance] = useState(0)
const [collectStatusText, setCollectStatusText] = useState('')
const [orderPriceAndFees, setOrderPriceAndFees] =
useState<OrderPriceAndFees>()
useEffect(() => {
if (!asset?.accessDetails) return
setIsOwned(asset?.accessDetails?.isOwned)
setValidOrderTx(asset?.accessDetails?.validOrderTx)
// get full price and fees
async function init() {
if (
asset?.accessDetails?.addressOrId === ZERO_ADDRESS ||
asset?.accessDetails?.type === 'free'
)
return
setIsLoading(true)
setStatusText('Calculating price including fees.')
const orderPriceAndFees = await getOrderPriceAndFees(asset, ZERO_ADDRESS)
setOrderPriceAndFees(orderPriceAndFees)
setIsLoading(false)
}
init()
}, [asset, accountId])
useEffect(() => {
setHasDatatoken(Number(dtBalance) >= 1)
}, [dtBalance])
useEffect(() => {
if (!accountId || !asset?.accessDetails) return
setIsDisabled(
!asset?.accessDetails.isPurchasable ||
((!isBalanceSufficient || !isAssetNetwork) && !isOwned && !hasDatatoken)
)
}, [
asset?.accessDetails,
isBalanceSufficient,
isAssetNetwork,
hasDatatoken,
accountId,
isOwned
])
useEffect(() => {
if (!accountId || asset.nft.owner !== accountId) return
const queryContext = getQueryContext(Number(asset.chainId))
async function getBaseTokenBalance() {
const variables = {
user: accountId.toLowerCase(),
exchangeId: asset?.accessDetails?.addressOrId
}
const result: OperationResult<FixedRateExchanges> = await fetchData(
FixedRateExchangesQuery,
variables,
queryContext
)
result?.data?.fixedRateExchanges[0]?.baseTokenBalance
? setBaseTokenBalance(
parseInt(result?.data?.fixedRateExchanges[0]?.baseTokenBalance)
)
: setBaseTokenBalance(0)
}
getBaseTokenBalance()
}, [accountId, asset?.accessDetails?.addressOrId, asset.chainId, asset.nft])
async function handleOrderOrDownload() {
setIsLoading(true)
if (isOwned) {
setStatusText(
getOrderFeedback(
asset.accessDetails?.baseToken?.symbol,
asset.accessDetails?.datatoken?.symbol
)[3]
)
await downloadFile(web3, asset, accountId, validOrderTx)
} else {
try {
if (!hasDatatoken && asset.accessDetails.type === 'dynamic') {
setStatusText(
getOrderFeedback(
asset.accessDetails.baseToken?.symbol,
asset.accessDetails.datatoken?.symbol
)[0]
)
const tx = await buyDtFromPool(asset.accessDetails, accountId, web3)
if (!tx) {
toast.error('Failed to buy datatoken from pool!')
setIsLoading(false)
return
}
}
setStatusText(
getOrderFeedback(
asset.accessDetails.baseToken?.symbol,
asset.accessDetails.datatoken?.symbol
)[asset.accessDetails?.type === 'fixed' ? 2 : 1]
)
const orderTx = await order(web3, asset, orderPriceAndFees, accountId)
if (!orderTx) {
toast.error('Failed to buy datatoken from pool!')
setIsLoading(false)
return
}
setIsOwned(true)
setValidOrderTx(orderTx.transactionHash)
} catch (ex) {
LoggerInstance.log(ex.message)
setIsLoading(false)
}
}
setIsLoading(false)
}
async function handleCollectTokens() {
setIsCollectLoading(true)
const config = getOceanConfig(asset?.chainId)
const fixed = new FixedRateExchange(web3, config.fixedRateExchangeAddress)
try {
setCollectStatusText(
getCollectTokensFeedback(
asset.accessDetails.baseToken?.symbol,
baseTokenBalance.toString()
)
)
const tx = await fixed.collectBT(
accountId,
asset?.accessDetails?.addressOrId,
baseTokenBalance.toString()
)
if (!tx) {
setIsCollectLoading(false)
return
}
setBaseTokenBalance(0)
return tx
} catch (error) {
LoggerInstance.log(error.message)
setIsCollectLoading(false)
} finally {
setIsCollectLoading(false)
}
}
const PurchaseButton = () => (
<ButtonBuy
action="download"
disabled={isDisabled}
hasPreviousOrder={isOwned}
hasDatatoken={hasDatatoken}
dtSymbol={asset?.datatokens[0]?.symbol}
dtBalance={dtBalance}
datasetLowPoolLiquidity={!asset.accessDetails?.isPurchasable}
onClick={handleOrderOrDownload}
assetTimeout={secondsToString(asset.services[0].timeout)}
assetType={asset?.metadata?.type}
stepText={statusText}
// isLoading={pricingIsLoading || isLoading}
isLoading={isLoading}
priceType={asset.accessDetails?.type}
isConsumable={asset.accessDetails?.isPurchasable}
isBalanceSufficient={isBalanceSufficient}
consumableFeedback={consumableFeedback}
/>
)
const CollectTokensButton = () => (
<ButtonBuy
action="collect"
onClick={handleCollectTokens}
disabled={baseTokenBalance === 0 || !baseTokenBalance}
hasPreviousOrder={false}
hasDatatoken={false}
dtSymbol={asset?.accessDetails?.baseToken.symbol}
dtBalance={baseTokenBalance.toString()}
datasetLowPoolLiquidity={false}
assetType=""
stepText={collectStatusText}
assetTimeout=""
isConsumable={false}
consumableFeedback=""
isBalanceSufficient={false}
isLoading={isCollectLoading}
/>
)
return (
<aside className={styles.consume}>
<div className={styles.info}>
<div className={styles.filewrapper}>
<FileIcon file={file} isLoading={fileIsLoading} />
</div>
<div className={styles.pricewrapper}>
<Price
accessDetails={asset.accessDetails}
orderPriceAndFees={orderPriceAndFees}
conversion
size="large"
/>
{!isInPurgatory && <PurchaseButton />}
</div>
</div>
{asset?.accessDetails?.datatoken?.name !== '' &&
asset?.nft.owner === accountId && (
<div className={styles.collect}>
<CollectTokensButton />
</div>
)}
{asset?.metadata?.type === 'algorithm' && (
<AlgorithmDatasetsListForCompute
algorithmDid={asset.id}
asset={asset}
/>
)}
</aside>
)
}