From ec029703205665db5bd9a7ccd239f89f395aeb97 Mon Sep 17 00:00:00 2001 From: Corantin Noll Date: Wed, 25 May 2022 16:19:32 -0400 Subject: [PATCH 1/6] Fix ipfs warning + refactor claim to refresh data --- .../react-app/src/components/claim-list.tsx | 6 +- packages/react-app/src/components/claim.tsx | 74 ++++++++++++---- .../src/components/modals/challenge-modal.tsx | 69 +++------------ .../components/modals/execute-claim-modal.tsx | 88 +++++++------------ packages/react-app/src/hooks/use-hooks.ts | 37 ++++++++ 5 files changed, 142 insertions(+), 132 deletions(-) create mode 100644 packages/react-app/src/hooks/use-hooks.ts diff --git a/packages/react-app/src/components/claim-list.tsx b/packages/react-app/src/components/claim-list.tsx index dc2e5684..ef1195cd 100644 --- a/packages/react-app/src/components/claim-list.tsx +++ b/packages/react-app/src/components/claim-list.tsx @@ -6,7 +6,7 @@ import { ENUM_CLAIM_STATE, ENUM_TRANSACTION_STATUS } from 'src/constants'; import { TokenAmountModel } from 'src/models/token-amount.model'; import { QuestModel } from 'src/models/quest.model'; import { useEffect, useState } from 'react'; -import { getObjectFromIpfsSafe } from 'src/services/ipfs.service'; +import { getObjectFromIpfs, ipfsTheGraph } from 'src/services/ipfs.service'; import { useTransactionContext } from 'src/contexts/transaction.context'; import { HelpTooltip } from './field-input/help-tooltip'; import * as QuestService from '../services/quest.service'; @@ -83,8 +83,8 @@ export default function ClaimList({ questData, challengeDeposit, isLoading = fal const fetchClaimsUntilNew = (claimsCount?: number) => { if (!claimsCount) { - setClaims([loadingClaim, ...claims]); claimsCount = claims.length; + setClaims([loadingClaim, ...claims]); } setTimeout(async () => { const results = await QuestService.fetchQuestClaims(questData); @@ -113,7 +113,7 @@ export default function ClaimList({ questData, challengeDeposit, isLoading = fal result.map(async (claim) => ({ ...claim, evidence: claim.evidenceIpfsHash - ? await getObjectFromIpfsSafe(claim.evidenceIpfsHash) + ? await getObjectFromIpfs(claim.evidenceIpfsHash, ipfsTheGraph) : 'No evidence', })), ); diff --git a/packages/react-app/src/components/claim.tsx b/packages/react-app/src/components/claim.tsx index 0dfb337d..90a2c3a5 100644 --- a/packages/react-app/src/components/claim.tsx +++ b/packages/react-app/src/components/claim.tsx @@ -1,11 +1,13 @@ -import { useViewport } from '@1hive/1hive-ui'; -import { ReactNode, useEffect, useState } from 'react'; +import { useViewport, Timer } from '@1hive/1hive-ui'; +import { ReactNode, useEffect, useMemo, useState } from 'react'; import { ENUM_CLAIM_STATE, ENUM_DISPUTE_STATES, ENUM_TRANSACTION_STATUS } from 'src/constants'; import { useTransactionContext } from 'src/contexts/transaction.context'; import { useWallet } from 'src/contexts/wallet.context'; +import { useTimeout } from 'src/hooks/use-hooks'; import { ClaimModel } from 'src/models/claim.model'; import { QuestModel } from 'src/models/quest.model'; import { TokenAmountModel } from 'src/models/token-amount.model'; +import { compareCaseInsensitive } from 'src/utils/string.util'; import styled, { css } from 'styled-components'; import { AddressFieldInput } from './field-input/address-field-input'; import AmountFieldInput from './field-input/amount-field-input'; @@ -42,37 +44,75 @@ export default function Claim({ claim, isLoading, challengeDeposit, questData }: const { below } = useViewport(); const [waitForClose, setWaitForClose] = useState(false); const [actionButton, setActionButton] = useState(); + const [isClaimable, setIsClaimable] = useState(false); + + useTimeout(() => { + setIsClaimable(true); + }, Math.max((claim.executionTimeMs ?? 0) - Date.now(), 0)); useEffect(() => { setState(claim.state); }, [claim.state]); + const timer = useMemo( + () => + claim.executionTimeMs && + claim.executionTimeMs - Date.now() > 0 && , + [claim.executionTimeMs], + ); + + const onActionClose = () => { + setWaitForClose(false); + }; + + const executeClaimModal = useMemo( + () => ( + + ), + [claim, questData.bounty, isClaimable], + ); + + const challengeModal = useMemo( + () => ( + + ), + [claim, challengeDeposit], + ); + + const resolveChallengeModal = useMemo( + () => , + [claim], + ); + useEffect(() => { if (waitForClose || !state) return; if (state === ENUM_CLAIM_STATE.Scheduled) { - if (walletAddress === claim.playerAddress) { + if (compareCaseInsensitive(walletAddress, claim.playerAddress) || isClaimable) { setActionButton( - , + <> + {executeClaimModal} + {timer} + , ); } else { setActionButton( - , + <> + {challengeModal} + {timer} + , ); } } else if (state === ENUM_CLAIM_STATE.Challenged) { - setActionButton(); + setActionButton(resolveChallengeModal); } else { setActionButton(undefined); } - }, [state, walletAddress, waitForClose]); + }, [state, walletAddress, waitForClose, isClaimable]); useEffect(() => { // If tx completion impact Claims, update them @@ -108,10 +148,6 @@ export default function Claim({ claim, isLoading, challengeDeposit, questData }: } }, [transaction?.status, transaction?.type, transaction?.[0], claim.container]); - const onActionClose = () => { - setWaitForClose(false); - }; - return (
diff --git a/packages/react-app/src/components/modals/challenge-modal.tsx b/packages/react-app/src/components/modals/challenge-modal.tsx index bcfee623..182ddae4 100644 --- a/packages/react-app/src/components/modals/challenge-modal.tsx +++ b/packages/react-app/src/components/modals/challenge-modal.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-nested-ternary */ -import { Button, useToast, IconFlag, Timer } from '@1hive/1hive-ui'; +import { Button, useToast, IconFlag } from '@1hive/1hive-ui'; import { noop, uniqueId } from 'lodash-es'; import { useState, useRef, useEffect, useMemo } from 'react'; import styled from 'styled-components'; @@ -51,8 +51,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop const toast = useToast(); const [loading, setLoading] = useState(false); const [opened, setOpened] = useState(false); - const [challengeTimeout, setChallengedTimeout] = useState(undefined); - const [buttonLabel, setOpenButtonLabel] = useState(); + // const [challengeTimeout, setChallengedTimeout] = useState(undefined); const [isEnoughBalance, setIsEnoughBalance] = useState(false); const [isFeeDepositSameToken, setIsFeeDepositSameToken] = useState(); const [challengeFee, setChallengeFee] = useState(undefined); @@ -70,34 +69,6 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop fetchFee(); }, [walletAddress]); - useEffect(() => { - let handle: number; - const launchSetTimeoutAsync = async (execTimeMs: number) => { - const now = Date.now(); - - if (now > execTimeMs) setChallengedTimeout(true); - else { - setChallengedTimeout(false); - handle = window.setTimeout(() => { - setChallengedTimeout(true); - }, execTimeMs - now); // To ms - } - }; - if (claim.executionTimeMs) launchSetTimeoutAsync(claim.executionTimeMs); - return () => { - if (handle) clearTimeout(handle); - }; - }, [claim.executionTimeMs]); - - useEffect(() => { - if (challengeTimeout !== undefined) { - if (challengeTimeout) - // wait to load - setOpenButtonLabel('Challenge period over'); - else setOpenButtonLabel('Challenge'); - } - }, [claim.state, challengeTimeout]); - useEffect(() => { if (challengeFee) setIsFeeDepositSameToken( @@ -219,19 +190,14 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop title="Challenge quests" openButton={ - {buttonLabel && ( - } - onClick={() => setOpened(true)} - label={buttonLabel} - mode="negative" - title={challengeTimeout ? "This claim can't be challenged anymore" : buttonLabel} - disabled={!buttonLabel || loading || !walletAddress || challengeTimeout} - /> - )} - {!loading && challengeTimeout === false && claim.executionTimeMs && ( - - )} + } + onClick={() => setOpened(true)} + label="Challenge" + mode="negative" + title={"This claim can't be challenged anymore"} + disabled={loading || !walletAddress} + /> } buttons={[ @@ -276,22 +242,13 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop
diff --git a/packages/react-app/src/components/dashboard.tsx b/packages/react-app/src/components/dashboard.tsx index cf30a3e2..0987b78f 100644 --- a/packages/react-app/src/components/dashboard.tsx +++ b/packages/react-app/src/components/dashboard.tsx @@ -47,7 +47,7 @@ const LabelStyled = styled.div` export default function Dashboard() { const theme = useTheme(); const [dashboardModel, setDashboardModel] = useState(); - const { walletAddress } = useWallet(); + const { walletConnected } = useWallet(); useEffect(() => { let isSubscribed = true; @@ -84,7 +84,7 @@ export default function Dashboard() { {dashboardModel?.questCount.toLocaleString()} - {walletAddress && } + {walletConnected && } diff --git a/packages/react-app/src/components/footer.tsx b/packages/react-app/src/components/footer.tsx index feb8d4a8..e5c78663 100644 --- a/packages/react-app/src/components/footer.tsx +++ b/packages/react-app/src/components/footer.tsx @@ -76,7 +76,7 @@ const IconStyled = styled.div` export default function footer() { const theme = useTheme(); const year = new Date().getFullYear(); - const { walletAddress } = useWallet(); + const { walletConnected } = useWallet(); const { networkId } = getNetwork(); return ( @@ -101,7 +101,7 @@ export default function footer() { Quest List - {walletAddress && ( + {walletConnected && ( )} diff --git a/packages/react-app/src/components/header/header-menu.tsx b/packages/react-app/src/components/header/header-menu.tsx index 0f84fbda..1b602ba5 100644 --- a/packages/react-app/src/components/header/header-menu.tsx +++ b/packages/react-app/src/components/header/header-menu.tsx @@ -30,7 +30,7 @@ type Props = { export default function HeaderMenu({ below }: Props) { const theme = useTheme(); - const { walletAddress } = useWallet(); + const { walletConnected } = useWallet(); return ( @@ -53,7 +53,7 @@ export default function HeaderMenu({ below }: Props) { - {walletAddress && ( + {walletConnected && ( )} diff --git a/packages/react-app/src/components/modals/challenge-modal.tsx b/packages/react-app/src/components/modals/challenge-modal.tsx index 66389a03..b49276f5 100644 --- a/packages/react-app/src/components/modals/challenge-modal.tsx +++ b/packages/react-app/src/components/modals/challenge-modal.tsx @@ -68,7 +68,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop if (feeAmount && isMountedRef.current) setChallengeFee(feeAmount); }; fetchFee(); - }, [walletAddress]); + }, []); useEffect(() => { if (challengeFee) @@ -188,8 +188,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop onClick={() => setOpened(true)} label="Challenge" mode="negative" - title={"Open challenge for this quest's claim"} - disabled={!walletAddress} + title="Open challenge for this quest's claim" /> } @@ -237,8 +236,8 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop mode="negative" type="submit" form="form-challenge" - disabled={!walletAddress || !isEnoughBalance || !isFormValid} - title={!walletAddress ? 'Not ready ...' : !isFormValid ? 'Form not valid' : 'Challenge'} + disabled={!isEnoughBalance || !isFormValid} + title={!isFormValid ? 'Form not valid' : 'Challenge'} className="m-8" />, ]} diff --git a/packages/react-app/src/components/modals/create-quest-modal.tsx b/packages/react-app/src/components/modals/create-quest-modal.tsx index 18f18d9e..ac7bb46e 100644 --- a/packages/react-app/src/components/modals/create-quest-modal.tsx +++ b/packages/react-app/src/components/modals/create-quest-modal.tsx @@ -332,15 +332,13 @@ export default function QuestModal({ form="form-quest" className="m-8" title={ - !walletAddress || !questDeposit?.token + !questDeposit?.token ? 'Not ready ...' : !isFormValid ? 'Form not valid' : 'Schedule claim' } - disabled={ - !walletAddress || !questDeposit?.token || !isEnoughBalance || !isFormValid - } + disabled={!questDeposit?.token || !isEnoughBalance || !isFormValid} /> } diff --git a/packages/react-app/src/components/modals/execute-claim-modal.tsx b/packages/react-app/src/components/modals/execute-claim-modal.tsx index c432abc6..4123dd49 100644 --- a/packages/react-app/src/components/modals/execute-claim-modal.tsx +++ b/packages/react-app/src/components/modals/execute-claim-modal.tsx @@ -11,10 +11,9 @@ import { TokenAmountModel } from 'src/models/token-amount.model'; import { useWallet } from 'src/contexts/wallet.context'; import { computeTransactionErrorMessage } from 'src/utils/errors.util'; import { compareCaseInsensitive } from 'src/utils/string.util'; -import { useIsMountedRef } from 'src/hooks/use-mounted.hook'; import { TransactionModel } from 'src/models/transaction.model'; import * as QuestService from '../../services/quest.service'; -import { AmountFieldInputFormik } from '../field-input/amount-field-input'; +import AmountFieldInput from '../field-input/amount-field-input'; import { Outset } from '../utils/spacer-util'; import ModalBase, { ModalCallback } from './modal-base'; import { AddressFieldInput } from '../field-input/address-field-input'; @@ -55,12 +54,10 @@ export default function ExecuteClaimModal({ claimable, }: Props) { const [opened, setOpened] = useState(false); - const [loading, setLoading] = useState(false); const [amount, setAmount] = useState(); const { setTransaction } = useTransactionContext(); const { walletAddress } = useWallet(); const modalId = useMemo(() => uniqueId('execute-claim-modal'), []); - const isMountedRef = useIsMountedRef(); useEffect(() => { if (questTotalBounty) { @@ -76,7 +73,6 @@ export default function ExecuteClaimModal({ const claimTx = async () => { try { - setLoading(true); const txPayload = { modalId, estimatedDuration: ENUM.ENUM_ESTIMATED_TX_TIME_MS.ClaimExecuting, @@ -109,10 +105,6 @@ export default function ExecuteClaimModal({ message: computeTransactionErrorMessage(e), }, ); - } finally { - if (isMountedRef.current) { - setLoading(false); - } } }; @@ -138,7 +130,6 @@ export default function ExecuteClaimModal({ } disabled={ !questTotalBounty || - !walletAddress || claim.claimedAmount.parsedAmount > questTotalBounty.parsedAmount || !claimable } @@ -150,10 +141,8 @@ export default function ExecuteClaimModal({ onClick={claimTx} icon={} label="Claim" - disabled={loading || !walletAddress || claim.state === ENUM_CLAIM_STATE.Challenged} - title={ - loading || !walletAddress ? 'Not ready ...' : 'Trigger claim operation in the chain' - } + disabled={claim.state === ENUM_CLAIM_STATE.Challenged} + title="Trigger claim operation in the chain" mode="positive" /> } @@ -162,16 +151,10 @@ export default function ExecuteClaimModal({ size="small" > - + {!compareCaseInsensitive(claim.playerAddress, walletAddress) && ( diff --git a/packages/react-app/src/components/modals/fund-modal.tsx b/packages/react-app/src/components/modals/fund-modal.tsx index b4e7bd1b..799a6628 100644 --- a/packages/react-app/src/components/modals/fund-modal.tsx +++ b/packages/react-app/src/components/modals/fund-modal.tsx @@ -108,8 +108,8 @@ export default function FundModal({ quest, onClose = noop }: Props) { form="form-fund" label="Fund" mode="strong" - title={!walletAddress ? 'Not ready ...' : !isFormValid ? 'Form not valid' : 'Fund'} - disabled={!walletAddress || !isEnoughBalance || !isFormValid} + title={!isFormValid ? 'Form not valid' : 'Fund'} + disabled={!isEnoughBalance || !isFormValid} />, ]} onClose={closeModal} diff --git a/packages/react-app/src/components/modals/reclaim-funds-modal.tsx b/packages/react-app/src/components/modals/reclaim-funds-modal.tsx index 429775d3..0c6e245a 100644 --- a/packages/react-app/src/components/modals/reclaim-funds-modal.tsx +++ b/packages/react-app/src/components/modals/reclaim-funds-modal.tsx @@ -139,8 +139,7 @@ export default function ReclaimFundsModal({ icon={} label="Reclaim" mode="strong" - title={!walletAddress ? 'Not ready ...' : 'Reclaim remaining funds and deposit'} - disabled={!walletAddress} + title="Reclaim remaining funds and deposit" /> } onClose={closeModal} diff --git a/packages/react-app/src/components/modals/schedule-claim-modal.tsx b/packages/react-app/src/components/modals/schedule-claim-modal.tsx index 7fd96e69..45ed52c8 100644 --- a/packages/react-app/src/components/modals/schedule-claim-modal.tsx +++ b/packages/react-app/src/components/modals/schedule-claim-modal.tsx @@ -238,13 +238,7 @@ export default function ScheduleClaimModal({ type="submit" form="form-claim" className="m-8" - title={ - !walletAddress - ? 'Not ready ...' - : !isFormValid - ? 'Form not valid' - : 'Schedule claim' - } + title={!isFormValid ? 'Form not valid' : 'Schedule claim'} disabled={!isEnoughBalance} /> diff --git a/packages/react-app/src/components/quest.tsx b/packages/react-app/src/components/quest.tsx index 3dc0a344..b6710140 100644 --- a/packages/react-app/src/components/quest.tsx +++ b/packages/react-app/src/components/quest.tsx @@ -114,7 +114,7 @@ export default function Quest({ isLoading = false, isSummary = false, }: Props) { - const { walletAddress } = useWallet(); + const { walletConnected } = useWallet(); const history = useHistory(); const [bounty, setBounty] = useState(questData?.bounty); const [highlight, setHighlight] = useState(true); @@ -329,7 +329,7 @@ export default function Quest({ isLoading={isLoading} /> )} - {!isSummary && questData.address && walletAddress && ( + {!isSummary && questData.address && walletConnected && ( {questData?.state === ENUM_QUEST_STATE.Active ? ( <> diff --git a/packages/react-app/src/components/sidebar.tsx b/packages/react-app/src/components/sidebar.tsx deleted file mode 100644 index 493ee796..00000000 --- a/packages/react-app/src/components/sidebar.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ENUM_QUEST_VIEW_MODE } from '../constants'; -import { useWallet } from '../contexts/wallet.context'; -import QuestModal from './modals/create-quest-modal'; -import { Filter } from './filter'; -import { Outset } from './utils/spacer-util'; - -export default function Sidebar() { - const { walletAddress } = useWallet(); - return ( - - - {walletAddress && } - - ); -} diff --git a/packages/react-app/src/contexts/wallet.context.tsx b/packages/react-app/src/contexts/wallet.context.tsx index b621ecd1..b67704a8 100644 --- a/packages/react-app/src/contexts/wallet.context.tsx +++ b/packages/react-app/src/contexts/wallet.context.tsx @@ -6,6 +6,7 @@ import { getNetwork } from '../networks'; import { getDefaultProvider, getUseWalletConnectors } from '../utils/web3.utils'; export type WalletContextModel = { + walletConnected: boolean; walletAddress: string; deactivateWallet: Function; activateWallet: Function; @@ -30,6 +31,7 @@ function WalletAugmented({ children }: Props) { const { ethereum } = wallet; const [activationError, setActivationError] = useState<{ name: string; message: string }>(); const [activatingId, setActivating] = useState(); + const [isConnected, setIsConnected] = useState(false); useEffect(() => { const lastWalletConnected = localStorage.getItem('LAST_WALLET_CONNECTOR'); @@ -39,6 +41,7 @@ function WalletAugmented({ children }: Props) { }, []); const ethers = useMemo(() => { + setIsConnected(false); const { chainId, networkId, name } = getNetwork(); if (ethereum) { @@ -63,6 +66,8 @@ function WalletAugmented({ children }: Props) { return getDefaultProvider(); } + setIsConnected(true); + const ensRegistry = undefined; // network?.ensRegistry; return new EthersProviders.Web3Provider(ethereum, { name: networkId, @@ -76,10 +81,11 @@ function WalletAugmented({ children }: Props) { await wallet.connect(id); }; - const handleDeconnect = () => { + const handleDisconnect = () => { setActivating(undefined); wallet.reset(); localStorage.removeItem('LAST_WALLET_CONNECTOR'); + setIsConnected(false); }; const contextValue = useMemo( () => ({ @@ -88,9 +94,10 @@ function WalletAugmented({ children }: Props) { activationError, walletAddress: wallet.account, activateWallet: handleConnect, - deactivateWallet: handleDeconnect, + deactivateWallet: handleDisconnect, activatedId: wallet.connector, activatingId, + walletConnected: isConnected, }), [wallet, ethers], );