diff --git a/packages/react-app/src/components/modals/challenge-modal.tsx b/packages/react-app/src/components/modals/challenge-modal.tsx index 07030d59..a48f3357 100644 --- a/packages/react-app/src/components/modals/challenge-modal.tsx +++ b/packages/react-app/src/components/modals/challenge-modal.tsx @@ -1,7 +1,7 @@ /* eslint-disable no-nested-ternary */ import { Button, useToast, IconFlag } from '@1hive/1hive-ui'; -import { noop, uniqueId } from 'lodash-es'; -import { useState, useRef, useEffect, useMemo } from 'react'; +import { debounce, noop, uniqueId } from 'lodash-es'; +import { useState, useRef, useEffect, useMemo, useCallback } from 'react'; import styled from 'styled-components'; import { Formik, Form, FormikErrors } from 'formik'; import { ClaimModel } from 'src/models/claim.model'; @@ -64,10 +64,18 @@ const ButtonLinkStyled = styled(Button)` type Props = { claim: ClaimModel; challengeDeposit: TokenAmountModel; + challengeData?: ChallengeModel; onClose?: ModalCallback; }; -export default function ChallengeModal({ claim, challengeDeposit, onClose = noop }: Props) { +const emptyChallengeData = {} as ChallengeModel; + +export default function ChallengeModal({ + claim, + challengeData = emptyChallengeData, + challengeDeposit, + onClose = noop, +}: Props) { const toast = useToast(); const [opened, setOpened] = useState(false); const [isEnoughBalance, setIsEnoughBalance] = useState(false); @@ -75,6 +83,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop const [challengeFee, setChallengeFee] = useState(undefined); const [isFormValid, setIsFormValid] = useState(false); const [showPreview, setShowPreview] = useState(false); + const [challengeDataState, setChallengeDataState] = useState(challengeData); const { setTransaction } = useTransactionContext(); const formRef = useRef(null); const { walletAddress } = useWallet(); @@ -88,6 +97,10 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop }; fetchFee(); }, []); + const debounceSave = useCallback( + debounce((data: ChallengeModel) => setChallengeDataState(data), 500), + [], + ); useEffect(() => { if (challengeFee) @@ -175,6 +188,9 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop : ENUM_TRANSACTION_STATUS.Failed, }); if (!challengeTxReceipt?.status) throw new Error('Failed to challenge the quest'); + if (isMountedRef.current) { + setChallengeDataState(emptyChallengeData); + } } catch (e: any) { setTransaction( (oldTx) => @@ -192,7 +208,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop const validate = (values: ChallengeModel) => { const errors = {} as FormikErrors; if (!values.reason) errors.reason = 'Challenge reason is required'; - + debounceSave(values); setIsFormValid(Object.keys(errors).length === 0); return errors; }; @@ -270,7 +286,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop isOpen={opened} > { validate(values); // validate one last time before submiting if (isFormValid) { 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 4956480d..7c52f977 100644 --- a/packages/react-app/src/components/modals/create-quest-modal.tsx +++ b/packages/react-app/src/components/modals/create-quest-modal.tsx @@ -177,7 +177,6 @@ export default function QuestModal({ if (data.expireTime.getTime() < Date.now()) errors.expireTime = 'Expiration have to be later than now'; - debounceSave(data); setIsFormValid(Object.keys(errors).length === 0); 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 dded740a..993d4f7f 100644 --- a/packages/react-app/src/components/modals/schedule-claim-modal.tsx +++ b/packages/react-app/src/components/modals/schedule-claim-modal.tsx @@ -1,10 +1,15 @@ /* eslint-disable no-nested-ternary */ import { Button } from '@1hive/1hive-ui'; -import { noop, uniqueId } from 'lodash-es'; -import { useState, useRef, useMemo } from 'react'; +import { debounce, noop, uniqueId } from 'lodash-es'; +import { useState, useRef, useMemo, useCallback } from 'react'; import styled from 'styled-components'; import { Formik, Form } from 'formik'; -import { ENUM_TRANSACTION_STATUS, ENUM, DEFAULT_CLAIM_EXECUTION_DELAY_MS } from 'src/constants'; +import { + ENUM_TRANSACTION_STATUS, + ENUM, + DEFAULT_CLAIM_EXECUTION_DELAY_MS, + ENUM_QUEST_STATE, +} from 'src/constants'; import { TokenAmountModel } from 'src/models/token-amount.model'; import { ClaimModel } from 'src/models/claim.model'; import { useTransactionContext } from 'src/contexts/transaction.context'; @@ -74,13 +79,17 @@ type Props = { questAddress: string; questTotalBounty?: TokenAmountModel | null; claimDeposit: TokenAmountModel; + claimData?: ClaimModel; onClose?: ModalCallback; }; - +const emptyClaimData = { + state: ENUM_QUEST_STATE.Draft, +} as ClaimModel; export default function ScheduleClaimModal({ questAddress, questTotalBounty, claimDeposit, + claimData = emptyClaimData, onClose = noop, }: Props) { const { walletAddress } = useWallet(); @@ -88,6 +97,7 @@ export default function ScheduleClaimModal({ const [isFormValid, setIsFormValid] = useState(false); const [isEnoughBalance, setIsEnoughBalance] = useState(false); const [showPreview, setShowPreview] = useState(false); + const [claimDataState, setClaimDataState] = useState(claimData); const formRef = useRef(null); const { setTransaction } = useTransactionContext(); const modalId = useMemo(() => uniqueId('schedule-claim-modal'), []); @@ -97,8 +107,12 @@ export default function ScheduleClaimModal({ setOpened(false); onClose(succeed); }; + const debounceSave = useCallback( + debounce((data: ClaimModel) => setClaimDataState(data), 500), + [], // will be created only once initially + ); - const validate = (values: ClaimModel & { claimAll: boolean }) => { + const validate = (values: ClaimModel) => { const errors = {} as FormErrors; if (!values.evidence) errors.evidence = 'Evidence of completion is required'; if (!values.claimAll) { @@ -114,11 +128,12 @@ export default function ScheduleClaimModal({ errors.playerAddress = 'Player address is not valid'; } } + debounceSave(values); setIsFormValid(Object.keys(errors).length === 0); return errors; }; - const onClaimSubmit = (values: ClaimModel & { claimAll: boolean }) => { + const onClaimSubmit = (values: ClaimModel) => { validate(values); // Validate one last time before submitting if (isFormValid) { if (values.claimAll) { @@ -176,6 +191,9 @@ export default function ScheduleClaimModal({ }); if (!scheduleReceipt?.status) throw new Error('Failed to schedule the claim, please retry in a few seconds'); + if (isMountedRef.current) { + setClaimDataState(emptyClaimData); + } } catch (e: any) { if (isMountedRef.current) { setTransaction( @@ -210,11 +228,13 @@ export default function ScheduleClaimModal({ {