From 85b66c29b04dc527f59e8381d0f005cc935c4d7c Mon Sep 17 00:00:00 2001 From: EnzoVezzaro Date: Mon, 10 Oct 2022 10:55:37 -0400 Subject: [PATCH] fix loading results on compute jobs (#1716) * added interval and block spinner after initial loading * refactor fetch jobs to fix loading issues * restore compute jobs in profile Co-authored-by: mihaisc --- .../Asset/AssetActions/Compute/History.tsx | 7 +- .../Asset/AssetActions/Compute/index.tsx | 58 ++++++++++++-- .../Profile/History/ComputeJobs/index.tsx | 48 ++--------- src/components/Profile/History/index.tsx | 79 ++++++++++++++++++- 4 files changed, 140 insertions(+), 52 deletions(-) diff --git a/src/components/Asset/AssetActions/Compute/History.tsx b/src/components/Asset/AssetActions/Compute/History.tsx index 9fa285476..805ba8585 100644 --- a/src/components/Asset/AssetActions/Compute/History.tsx +++ b/src/components/Asset/AssetActions/Compute/History.tsx @@ -5,14 +5,17 @@ import Caret from '@images/caret.svg' export default function ComputeHistory({ title, - children + children, + refetchJobs }: { title: string children: ReactNode + refetchJobs?: any }): ReactElement { const [open, setOpen] = useState(false) - function handleClick() { + async function handleClick() { + await refetchJobs(true) setOpen(!open) } diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 34804bfe8..cab80cfdb 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, ReactElement, useEffect } from 'react' +import React, { useState, ReactElement, useEffect, useCallback } from 'react' import { Asset, DDO, @@ -29,7 +29,8 @@ import { isOrderable, getAlgorithmAssetSelectionList, getAlgorithmsForAsset, - getComputeEnviroment + getComputeEnviroment, + getComputeJobs } from '@utils/compute' import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute' @@ -43,7 +44,9 @@ import { handleComputeOrder } from '@utils/order' import { getComputeFeedback } from '@utils/feedback' import { getDummyWeb3 } from '@utils/web3' import { initializeProviderForCompute } from '@utils/provider' +import { useUserPreferences } from '@context/UserPreferences' +const refreshInterval = 10000 // 10 sec. export default function Compute({ asset, dtBalance, @@ -58,6 +61,7 @@ export default function Compute({ consumableFeedback?: string }): ReactElement { const { accountId, web3 } = useWeb3() + const { chainIds } = useUserPreferences() const newAbortController = useAbortController() const newCancelToken = useCancelToken() @@ -91,6 +95,8 @@ export default function Compute({ const [isRequestingAlgoOrderPrice, setIsRequestingAlgoOrderPrice] = useState(false) const [refetchJobs, setRefetchJobs] = useState(false) + const [isLoadingJobs, setIsLoadingJobs] = useState(false) + const [jobs, setJobs] = useState([]) const hasDatatoken = Number(dtBalance) >= 1 const isComputeButtonDisabled = @@ -243,6 +249,44 @@ export default function Compute({ }) }, [asset, isUnsupportedPricing]) + const fetchJobs = useCallback( + async (type: string) => { + if (!chainIds || chainIds.length === 0 || !accountId) { + return + } + + try { + type === 'init' && setIsLoadingJobs(true) + const computeJobs = await getComputeJobs( + [asset?.chainId] || chainIds, + accountId, + asset, + newCancelToken() + ) + setJobs(computeJobs.computeJobs) + setIsLoadingJobs(!computeJobs.isLoaded) + } catch (error) { + LoggerInstance.error(error.message) + setIsLoadingJobs(false) + } + }, + [accountId, asset, chainIds, isLoadingJobs, newCancelToken] + ) + + useEffect(() => { + fetchJobs('init') + + // init periodic refresh for jobs + const balanceInterval = setInterval( + () => fetchJobs('repeat'), + refreshInterval + ) + + return () => { + clearInterval(balanceInterval) + } + }, [refetchJobs]) + // Output errors in toast UI useEffect(() => { const newError = error @@ -443,11 +487,15 @@ export default function Compute({ )} {accountId && asset?.accessDetails?.datatoken && ( - + setRefetchJobs(!refetchJobs)} + > setRefetchJobs(!refetchJobs)} /> )} diff --git a/src/components/Profile/History/ComputeJobs/index.tsx b/src/components/Profile/History/ComputeJobs/index.tsx index 405b50809..a1fd24cc7 100644 --- a/src/components/Profile/History/ComputeJobs/index.tsx +++ b/src/components/Profile/History/ComputeJobs/index.tsx @@ -1,6 +1,5 @@ -import React, { ReactElement, useEffect, useState, useCallback } from 'react' +import React, { ReactElement, useState } from 'react' import Time from '@shared/atoms/Time' -import { LoggerInstance } from '@oceanprotocol/lib' import Table, { TableOceanColumn } from '@shared/atoms/Table' import Button from '@shared/atoms/Button' import { useWeb3 } from '@context/Web3' @@ -8,11 +7,7 @@ import Details from './Details' import Refresh from '@images/refresh.svg' import { useUserPreferences } from '@context/UserPreferences' import NetworkName from '@shared/NetworkName' -import { getComputeJobs } from '@utils/compute' import styles from './index.module.css' -import { useAsset } from '@context/Asset' -import { useIsMounted } from '@hooks/useIsMounted' -import { useCancelToken } from '@hooks/useCancelToken' import AssetListTitle from '@shared/AssetList/AssetListTitle' export function Status({ children }: { children: string }): ReactElement { @@ -51,48 +46,19 @@ const columns: TableOceanColumn[] = [ export default function ComputeJobs({ minimal, - assetChainIds, + jobs, + isLoading, refetchJobs }: { minimal?: boolean - assetChainIds?: number[] - refetchJobs?: boolean + jobs?: ComputeJobMetaData[] + isLoading?: boolean + refetchJobs?: any }): ReactElement { const { accountId } = useWeb3() - const { asset } = useAsset() const { chainIds } = useUserPreferences() - const isMounted = useIsMounted() - const newCancelToken = useCancelToken() - - const [isLoading, setIsLoading] = useState(false) - const [jobs, setJobs] = useState([]) const [columnsMinimal] = useState([columns[4], columns[5], columns[3]]) - const fetchJobs = useCallback(async () => { - if (!chainIds || chainIds.length === 0 || !accountId) { - setJobs([]) - setIsLoading(false) - return - } - try { - setIsLoading(true) - const jobs = await getComputeJobs( - assetChainIds || chainIds, - accountId, - asset, - newCancelToken() - ) - isMounted() && setJobs(jobs.computeJobs) - setIsLoading(!jobs.isLoaded) - } catch (error) { - LoggerInstance.error(error.message) - } - }, [chainIds, accountId, asset, isMounted, assetChainIds, newCancelToken]) - - useEffect(() => { - fetchJobs() - }, [fetchJobs, refetchJobs]) - return accountId ? ( <> {jobs?.length >= 0 && !minimal && ( @@ -100,7 +66,7 @@ export default function ComputeJobs({ style="text" size="small" title="Refresh compute jobs" - onClick={async () => await fetchJobs()} + onClick={async () => await refetchJobs(true)} disabled={isLoading} className={styles.refresh} > diff --git a/src/components/Profile/History/index.tsx b/src/components/Profile/History/index.tsx index 10af47f11..8be94572a 100644 --- a/src/components/Profile/History/index.tsx +++ b/src/components/Profile/History/index.tsx @@ -1,17 +1,30 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useCallback, useEffect, useState } from 'react' import Tabs from '@shared/atoms/Tabs' import PublishedList from './PublishedList' import Downloads from './Downloads' import ComputeJobs from './ComputeJobs' import styles from './index.module.css' import { useWeb3 } from '@context/Web3' +import { chainIds } from 'app.config' +import { getComputeJobs } from '@utils/compute' +import { useUserPreferences } from '@context/UserPreferences' +import { useCancelToken } from '@hooks/useCancelToken' +import { LoggerInstance } from '@oceanprotocol/lib' interface HistoryTab { title: string content: JSX.Element } -function getTabs(accountId: string, userAccountId: string): HistoryTab[] { +const refreshInterval = 10000 // 10 sec. +function getTabs( + accountId: string, + userAccountId: string, + jobs: ComputeJobMetaData[], + isLoadingJobs: boolean, + refetchJobs: boolean, + setRefetchJobs: any +): HistoryTab[] { const defaultTabs: HistoryTab[] = [ { title: 'Published', @@ -24,7 +37,13 @@ function getTabs(accountId: string, userAccountId: string): HistoryTab[] { ] const computeTab: HistoryTab = { title: 'Compute Jobs', - content: + content: ( + setRefetchJobs(!refetchJobs)} + /> + ) } if (accountId === userAccountId) { defaultTabs.push(computeTab) @@ -38,10 +57,62 @@ export default function HistoryPage({ accountIdentifier: string }): ReactElement { const { accountId } = useWeb3() + const { chainIds } = useUserPreferences() + const newCancelToken = useCancelToken() const url = new URL(location.href) const defaultTab = url.searchParams.get('defaultTab') - const tabs = getTabs(accountIdentifier, accountId) + + const [refetchJobs, setRefetchJobs] = useState(false) + const [isLoadingJobs, setIsLoadingJobs] = useState(false) + const [jobs, setJobs] = useState([]) + + const fetchJobs = useCallback( + async (type: string) => { + if (!chainIds || chainIds.length === 0 || !accountId) { + return + } + + try { + type === 'init' && setIsLoadingJobs(true) + const computeJobs = await getComputeJobs( + chainIds, + accountId, + null, + newCancelToken() + ) + setJobs(computeJobs.computeJobs) + setIsLoadingJobs(!computeJobs.isLoaded) + } catch (error) { + LoggerInstance.error(error.message) + setIsLoadingJobs(false) + } + }, + [accountId, chainIds, isLoadingJobs, newCancelToken] + ) + + useEffect(() => { + fetchJobs('init') + + // init periodic refresh for jobs + const balanceInterval = setInterval( + () => fetchJobs('repeat'), + refreshInterval + ) + + return () => { + clearInterval(balanceInterval) + } + }, [refetchJobs]) + + const tabs = getTabs( + accountIdentifier, + accountId, + jobs, + isLoadingJobs, + refetchJobs, + setRefetchJobs + ) let defaultTabIndex = 0 defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)