Skip to content

Commit

Permalink
AmoutFieldInput handle onBlur + errors + FundModal valiudating
Browse files Browse the repository at this point in the history
  • Loading branch information
Corantin committed Mar 31, 2022
1 parent 49b2c80 commit 9a1c660
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Button,
} from '@1hive/1hive-ui';
import { parseUnits } from 'ethers/lib/utils';
import { connect } from 'formik';
import { connect, FormikContextType } from 'formik';
import { noop } from 'lodash-es';
import React, { ReactNode, useEffect, useState, useRef, Fragment } from 'react';
import { NETWORK_TOKENS } from 'src/constants';
Expand Down Expand Up @@ -86,7 +86,7 @@ type Props = {
placeHolder?: string;
value?: TokenAmountModel;
onChange?: Function;
formik?: any;
formik?: FormikContextType<any>;
compact?: boolean;
tooltip?: string;
tooltipDetail?: ReactNode;
Expand All @@ -95,9 +95,7 @@ type Props = {
wide?: boolean;
tokenEditable?: boolean;
reversed?: boolean;
amountError?: string | false;
tokenError?: string | false;
onBlur?: Function;
error?: string | false;
};

function AmountFieldInput({
Expand All @@ -119,22 +117,27 @@ function AmountFieldInput({
wide = false,
tokenEditable = false,
reversed = false,
onBlur = noop,
amountError,
tokenError,
error,
}: Props) {
const { type } = getNetwork();
const [decimalsCount, setDecimalsCount] = useState(maxDecimals);
// const [hasFocused, setHasFocused] = useState(false);
const [tokens, setTokens] = useState<TokenModel[]>([]);
const [searchTerm, setSearchTerm] = useState<string>();
const [amount, setAmount] = useState<number | undefined>(value?.parsedAmount);
const [token, setToken] = useState<TokenModel | undefined>(value?.token);
const [availableTokens, setAvailableTokens] = useState<TokenModel[]>([]);
const [_hasFocused, _setHasFocused] = useState<boolean>();
const { walletAddress } = useWallet();
const tokenInputId = `token-${id}`;
const amountInputId = `amount-${id}`;

// Needed since the access of state in event handlers is not working
const hasFocusedRef = React.useRef(_hasFocused);
const setHasFocues = (data: boolean) => {
hasFocusedRef.current = data;
_setHasFocused(data);
};

const autoCompleteRef: React.Ref<any> = useRef(null);
const handleFocusIn = () => {
if (
Expand All @@ -143,14 +146,15 @@ function AmountFieldInput({
isEdit &&
tokenEditable
) {
// setHasFocused(true);
setHasFocues(true);
fetchAvailableTokens();
} else if (document.activeElement !== autoCompleteRef.current && hasFocusedRef.current) {
formik?.setFieldTouched(id, true);
setHasFocues(false);
}
// else if (document.activeElement !== autoCompleteRef.current && hasFocused) {
// }
};
useEffect(() => {
if (!token) document.addEventListener('focusin', handleFocusIn, true);
if (!token) document.addEventListener('focusin', handleFocusIn);
return () => document.removeEventListener('focusin', handleFocusIn);
}, [walletAddress, isEdit, tokenEditable, token]);

Expand Down Expand Up @@ -228,7 +232,7 @@ function AmountFieldInput({
};

const amountField = (
<FieldInput label={amountLabel} wide={wide} compact={compact} error={amountError}>
<FieldInput label={amountLabel} wide={wide} compact={compact}>
<AmountTokenWrapperStyled isEdit={isEdit} wide={wide}>
{amount !== undefined &&
(isEdit ? (
Expand All @@ -237,7 +241,7 @@ function AmountFieldInput({
title={!token ? 'Set token first' : undefined}
onChange={onAmountChange}
placeHolder={placeHolder}
onBlur={onBlur}
onBlur={() => formik?.setFieldTouched(id, true)}
type="number"
value={amount}
wide={wide}
Expand All @@ -254,7 +258,6 @@ function AmountFieldInput({
<FieldInput
label={tokenLabel}
wide={wide}
error={tokenError}
compact={compact}
tooltip="Token"
tooltipDetail="Select a token between the list or paste the token address"
Expand All @@ -268,7 +271,7 @@ function AmountFieldInput({
onChange={setSearchTerm}
onSelect={onTokenChange}
ref={autoCompleteRef}
// onBlur={onBlur}
onBlur={() => formik?.setFieldTouched(id, true)}
placeholder="Search name or paste address"
wide={wide}
renderSelected={(i: number) => (
Expand Down Expand Up @@ -304,6 +307,7 @@ function AmountFieldInput({
wide={wide}
compact
direction={!!amountLabel || !!tokenLabel ? 'column' : 'row'}
error={error}
>
{reversed ? [tokenField, amountField] : [amountField, tokenField]}
</FieldInput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import { GUpx } from 'src/utils/style.util';
import styled from 'styled-components';
import { IconTooltip } from './icon-tooltip';

// eslint-disable-next-line no-unused-vars
export type FormError<TModel> = { [_key in keyof TModel]: any | false };
// eslint-disable-next-line no-unused-vars
export type FormTouched<TModel> = { [_key in keyof TModel]: any | false };
const FieldStyled = styled.div`
${({ compact }: any) => (!compact ? `margin-bottom:${GUpx(1)}` : '')};
${({ isLoading, wide }: any) => (isLoading || wide ? `width: 100%;` : 'max-width: 100%;')};
`;
const ErrorStyled = styled.span`
color: ${(props: any) => props.theme.negative};
font-size: small;
margin-left: ${GUpx(1)};
margin-left: ${GUpx(2)};
`;
const LabelStyled = styled.label`
color: ${(props: any) => props.color};
Expand Down
6 changes: 3 additions & 3 deletions packages/react-app/src/components/modals/challenge-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable no-undef */
import { Button, useToast, IconFlag, Timer } from '@1hive/1hive-ui';
import { noop, uniqueId } from 'lodash-es';
import { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { Formik, Form } from 'formik';
import { Formik, Form, FormikErrors } from 'formik';
import { ClaimModel } from 'src/models/claim.model';
import { ENUM, ENUM_TRANSACTION_STATUS } from 'src/constants';
import { useTransactionContext } from 'src/contexts/transaction.context';
Expand All @@ -21,7 +22,6 @@ import TextFieldInput from '../field-input/text-field-input';
import { Outset } from '../utils/spacer-util';
import { IconTooltip } from '../field-input/icon-tooltip';
import { WalletBallance } from '../wallet-balance';
import { FormError } from '../field-input/field-input';

// #region StyledComponents

Expand Down Expand Up @@ -257,7 +257,7 @@ export default function ChallengeModal({ claim, challengeDeposit, onClose = noop
}
};
const validate = (values: ChallengeModel) => {
const errors = {} as FormError<ChallengeModel>;
const errors = {} as FormikErrors<ChallengeModel>;
if (!values.reason) errors.reason = 'Validation : Challenge reason is required';
return errors;
};
Expand Down
31 changes: 20 additions & 11 deletions packages/react-app/src/components/modals/fund-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { useTransactionContext } from 'src/contexts/transaction.context';
import { GUpx } from 'src/utils/style.util';
import { QuestModel } from 'src/models/quest.model';
import { useWallet } from 'src/contexts/wallet.context';
import { TokenAmountModel } from 'src/models/token-amount.model';
import { computeTransactionErrorMessage } from 'src/utils/errors.util';
import { FundModel } from 'src/models/fund.model';
import { FormErrors } from 'src/models/form-errors';
import * as QuestService from '../../services/quest.service';
import { AmountFieldInputFormik } from '../field-input/amount-field-input';
import { Outset } from '../utils/spacer-util';
Expand Down Expand Up @@ -97,23 +98,30 @@ export default function FundModal({ quest, onClose = noop }: Props) {
}
};

const validate = (values: FundModel) => {
const errors = {} as FormErrors<FundModel>;
if (!values.fundAmount?.parsedAmount || values.fundAmount.parsedAmount <= 0)
errors.fundAmount = 'Amount invalid';
return errors;
};

return (
<Formik
initialValues={{
fundAmount: { parsedAmount: 0, token: quest.rewardToken } as TokenAmountModel,
}}
initialValues={
{
fundAmount: { parsedAmount: 0, token: quest.rewardToken },
} as FundModel
}
onSubmit={(values, { setSubmitting }) => {
const errors = [];
if (!values.fundAmount?.parsedAmount || values.fundAmount.parsedAmount <= 0)
errors.push('Validation : Amount invalid');
if (errors.length) {
errors.forEach(toast);
} else {
const errors = validate(values);
// IsValid check
if (!Object.keys(errors).length) {
fundModalTx(values, setSubmitting);
}
}}
validate={validate}
>
{({ values, handleSubmit, handleChange }) => (
{({ values, handleSubmit, handleChange, touched, errors }) => (
<ModalBase
id="fund-modal"
title="Fund quest"
Expand Down Expand Up @@ -162,6 +170,7 @@ export default function FundModal({ quest, onClose = noop }: Props) {
onChange={handleChange}
isLoading={loading}
value={values.fundAmount}
error={touched.fundAmount && (errors.fundAmount as string)}
/>
</Outset>
</FormStyled>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useWallet } from 'src/contexts/wallet.context';
import { toChecksumAddress } from 'web3-utils';
import { computeTransactionErrorMessage } from 'src/utils/errors.util';

import { FormErrors } from 'src/models/form-errors';
import ModalBase, { ModalCallback } from './modal-base';
import * as QuestService from '../../services/quest.service';
import { AmountFieldInputFormik } from '../field-input/amount-field-input';
Expand All @@ -22,7 +23,6 @@ import { ChildSpacer, Outset } from '../utils/spacer-util';
import CheckboxFieldInput from '../field-input/checkbox-field-input';
import { AddressFieldInput } from '../field-input/address-field-input';
import { WalletBallance } from '../wallet-balance';
import { FormError } from '../field-input/field-input';

// #region StyledComponents

Expand Down Expand Up @@ -68,13 +68,10 @@ export default function ScheduleClaimModal({
onClose(succeed);
};
const validate = (values: ClaimModel & { claimAll: boolean }) => {
const errors = {} as FormError<ClaimModel>;
const errors = {} as FormErrors<ClaimModel>;
if (!values.evidence) errors.evidence = 'Validation : Evidence of completion is required';
if (!values.claimedAmount) errors.claimedAmount = 'Validation : Claim amount is required';
if (values.claimAll) {
values.claimedAmount.parsedAmount = 0;
values.claimedAmount.token.amount = '0';
} else if (values.claimedAmount.parsedAmount > questTotalBounty.parsedAmount)
if (!values.claimAll && values.claimedAmount.parsedAmount > questTotalBounty.parsedAmount)
errors.claimedAmount = 'Validation : Claim amount should not be higher than available bounty';
if (values.playerAddress) {
try {
Expand All @@ -88,6 +85,10 @@ export default function ScheduleClaimModal({
const onClaimSubmit = (values: ClaimModel & { claimAll: boolean }, setSubmitting: Function) => {
const errors = validate(values);
if (!Object.keys(errors).length) {
if (values.claimAll) {
values.claimedAmount.parsedAmount = 0;
values.claimedAmount.token.amount = '0';
}
scheduleClaimTx(values, setSubmitting);
}
};
Expand Down Expand Up @@ -283,6 +284,7 @@ export default function ScheduleClaimModal({
tooltipDetail="The expected amount to claim considering the quest agreement. Set it to 0 if you want to claim the whole bounty."
isLoading={loading}
value={values.claimAll ? questTotalBounty : values.claimedAmount}
error={touched.claimedAmount && (errors.claimedAmount as string)}
disabled={values.claimAll}
/>
</Outset>
Expand All @@ -295,7 +297,7 @@ export default function ScheduleClaimModal({
tooltip="Player address"
error={touched.playerAddress && errors.playerAddress}
onBlur={handleBlur}
tooltipDetail="Most of time it may be be the connected wallet but can also be set to another wallet address"
tooltipDetail="Most of time it may be the connected wallet but can also be set to another wallet address"
isEdit
onChange={handleChange}
wide
Expand Down
Loading

0 comments on commit 9a1c660

Please sign in to comment.