import React, { ReactElement, useCallback, useEffect, useState } from 'react' import Time from '@shared/atoms/Time' import Table from '@shared/atoms/Table' import AssetTitle from '@shared/AssetList/AssetListTitle' import { useUserPreferences } from '@context/UserPreferences' import { gql } from 'urql' import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/subgraph/TransactionHistory' import { fetchDataForMultipleChains } from '@utils/subgraph' import { useSiteMetadata } from '@hooks/useSiteMetadata' import NetworkName from '@shared/NetworkName' import { getAssetsFromDtList } from '@utils/aquarius' import { CancelToken } from 'axios' import Title from './Title' import styles from './index.module.css' import { Asset, LoggerInstance } from '@oceanprotocol/lib' import { useCancelToken } from '@hooks/useCancelToken' const REFETCH_INTERVAL = 20000 const txHistoryQueryByPool = gql` query TransactionHistoryByPool($user: String, $pool: String) { poolTransactions( orderBy: timestamp orderDirection: desc where: { user: $user, pool: $pool } first: 1000 ) { baseToken { symbol address } baseTokenValue datatoken { symbol address } datatokenValue type tx timestamp pool { id } } } ` const txHistoryQuery = gql` query TransactionHistory($user: String) { poolTransactions( orderBy: timestamp orderDirection: desc where: { user: $user } first: 1000 ) { baseToken { symbol address } baseTokenValue datatoken { symbol address } datatokenValue type tx timestamp pool { id } } } ` export interface PoolTransaction extends TransactionHistoryPoolTransactions { networkId: number asset: Asset } const columns = [ { name: 'Title', selector: function getTitleRow(row: PoolTransaction) { return } }, { name: 'Data Set', selector: function getAssetRow(row: PoolTransaction) { return <AssetTitle asset={row.asset} /> } }, { name: 'Network', selector: function getNetwork(row: PoolTransaction) { return <NetworkName networkId={row.networkId} /> }, maxWidth: '12rem' }, { name: 'Time', selector: function getTimeRow(row: PoolTransaction) { return ( <Time className={styles.time} date={row.timestamp.toString()} relative isUnix /> ) }, maxWidth: '10rem' } ] // hack! if we use a function to omit one field this will display a strange refresh to the enduser for each row const columnsMinimal = [columns[0], columns[3]] export default function PoolTransactions({ poolAddress, poolChainId, minimal, accountId }: { poolAddress?: string poolChainId?: number[] minimal?: boolean accountId: string }): ReactElement { const { chainIds } = useUserPreferences() const { appConfig } = useSiteMetadata() const cancelToken = useCancelToken() const [transactions, setTransactions] = useState<PoolTransaction[]>() const [isLoading, setIsLoading] = useState<boolean>(false) const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>() const [data, setData] = useState<PoolTransaction[]>() const getPoolTransactionData = useCallback(async () => { const variables = { user: accountId?.toLowerCase(), pool: poolAddress?.toLowerCase() } const transactions: PoolTransaction[] = [] const result = await fetchDataForMultipleChains( poolAddress ? txHistoryQueryByPool : txHistoryQuery, variables, poolAddress ? poolChainId : chainIds ) for (let i = 0; i < result.length; i++) { result[i].poolTransactions.forEach((poolTransaction: PoolTransaction) => { transactions.push(poolTransaction) }) } if (JSON.stringify(data) !== JSON.stringify(transactions)) { setData(transactions) } }, [accountId, chainIds, data, poolAddress, poolChainId]) const getPoolTransactions = useCallback( async (cancelToken: CancelToken) => { if (!data) { return } const poolTransactions: PoolTransaction[] = [] const dtList: string[] = [] for (let i = 0; i < data.length; i++) { dtList.push(data[i]?.datatoken?.address) } if (dtList.length === 0) { setIsLoading(false) return } const ddoList = await getAssetsFromDtList(dtList, chainIds, cancelToken) for (let i = 0; i < data.length; i++) { poolTransactions.push({ ...data[i], networkId: ddoList[i]?.chainId, asset: ddoList[i] }) } const sortedTransactions = poolTransactions.sort( (a, b) => b.timestamp - a.timestamp ) setTransactions(sortedTransactions) setIsLoading(false) }, [data, chainIds, setIsLoading] ) // // Get data, periodically // useEffect(() => { if (!appConfig?.metadataCacheUri) return async function getTransactions() { try { await getPoolTransactionData() if (dataFetchInterval) return const interval = setInterval(async () => { await getPoolTransactionData() }, REFETCH_INTERVAL) setDataFetchInterval(interval) } catch (error) { LoggerInstance.error( 'Error fetching pool transactions: ', error.message ) } } getTransactions() return () => { clearInterval(dataFetchInterval) } }, [getPoolTransactionData, dataFetchInterval, appConfig.metadataCacheUri]) // // Transform to final transactions // useEffect(() => { if (!cancelToken()) return async function transformData() { try { setIsLoading(true) await getPoolTransactions(cancelToken()) } catch (error) { LoggerInstance.error( 'Error fetching pool transactions: ', error.message ) } } transformData() return () => { cancelToken() } }, [cancelToken, getPoolTransactions]) return accountId ? ( <Table columns={minimal ? columnsMinimal : columns} data={transactions} isLoading={isLoading} noTableHead={minimal} dense={minimal} pagination={ minimal ? transactions?.length >= 4 : transactions?.length >= 9 } paginationPerPage={minimal ? 5 : 10} /> ) : ( <div>Please connect your Web3 wallet.</div> ) }