chore(deposits): persistence of deposit form state (#3348)
This commit is contained in:
parent
dcb79e70d3
commit
70943c523c
@ -61,6 +61,7 @@ beforeEach(() => {
|
|||||||
submitDeposit: jest.fn(),
|
submitDeposit: jest.fn(),
|
||||||
submitFaucet: jest.fn(),
|
submitFaucet: jest.fn(),
|
||||||
onDisconnect: jest.fn(),
|
onDisconnect: jest.fn(),
|
||||||
|
handleAmountChange: jest.fn(),
|
||||||
approveTxId: null,
|
approveTxId: null,
|
||||||
faucetTxId: null,
|
faucetTxId: null,
|
||||||
isFaucetable: true,
|
isFaucetable: true,
|
||||||
|
@ -25,11 +25,9 @@ import {
|
|||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
import type { ButtonHTMLAttributes, ChangeEvent, ReactNode } from 'react';
|
||||||
import { useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useWatch, Controller, useForm } from 'react-hook-form';
|
||||||
import { useWatch } from 'react-hook-form';
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { DepositLimits } from './deposit-limits';
|
import { DepositLimits } from './deposit-limits';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import {
|
import {
|
||||||
@ -40,6 +38,7 @@ import {
|
|||||||
import type { DepositBalances } from './use-deposit-balances';
|
import type { DepositBalances } from './use-deposit-balances';
|
||||||
import { FaucetNotification } from './faucet-notification';
|
import { FaucetNotification } from './faucet-notification';
|
||||||
import { ApproveNotification } from './approve-notification';
|
import { ApproveNotification } from './approve-notification';
|
||||||
|
import { usePersistentDeposit } from './use-persistent-deposit';
|
||||||
|
|
||||||
interface FormFields {
|
interface FormFields {
|
||||||
asset: string;
|
asset: string;
|
||||||
@ -53,6 +52,7 @@ export interface DepositFormProps {
|
|||||||
selectedAsset?: Asset;
|
selectedAsset?: Asset;
|
||||||
balances: DepositBalances | null;
|
balances: DepositBalances | null;
|
||||||
onSelectAsset: (assetId: string) => void;
|
onSelectAsset: (assetId: string) => void;
|
||||||
|
handleAmountChange: (amount: string) => void;
|
||||||
onDisconnect: () => void;
|
onDisconnect: () => void;
|
||||||
submitApprove: () => void;
|
submitApprove: () => void;
|
||||||
approveTxId: number | null;
|
approveTxId: number | null;
|
||||||
@ -71,6 +71,7 @@ export const DepositForm = ({
|
|||||||
selectedAsset,
|
selectedAsset,
|
||||||
balances,
|
balances,
|
||||||
onSelectAsset,
|
onSelectAsset,
|
||||||
|
handleAmountChange,
|
||||||
onDisconnect,
|
onDisconnect,
|
||||||
submitApprove,
|
submitApprove,
|
||||||
submitDeposit,
|
submitDeposit,
|
||||||
@ -85,6 +86,8 @@ export const DepositForm = ({
|
|||||||
const { pubKey, pubKeys: _pubKeys } = useVegaWallet();
|
const { pubKey, pubKeys: _pubKeys } = useVegaWallet();
|
||||||
const [approveNotificationIntent, setApproveNotificationIntent] =
|
const [approveNotificationIntent, setApproveNotificationIntent] =
|
||||||
useState<Intent>(Intent.Warning);
|
useState<Intent>(Intent.Warning);
|
||||||
|
const [persistedDeposit] = usePersistentDeposit(selectedAsset?.id);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -95,7 +98,8 @@ export const DepositForm = ({
|
|||||||
} = useForm<FormFields>({
|
} = useForm<FormFields>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
to: pubKey ? pubKey : undefined,
|
to: pubKey ? pubKey : undefined,
|
||||||
asset: selectedAsset?.id || '',
|
asset: selectedAsset?.id,
|
||||||
|
amount: persistedDeposit.amount,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -333,6 +337,9 @@ export const DepositForm = ({
|
|||||||
return maxSafe(balances?.balance || new BigNumber(0))(v);
|
return maxSafe(balances?.balance || new BigNumber(0))(v);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
handleAmountChange(e.target.value || '');
|
||||||
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.amount?.message && (
|
{errors.amount?.message && (
|
||||||
@ -343,10 +350,9 @@ export const DepositForm = ({
|
|||||||
{selectedAsset && balances && (
|
{selectedAsset && balances && (
|
||||||
<UseButton
|
<UseButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setValue(
|
const amount = balances.balance.toFixed(selectedAsset.decimals);
|
||||||
'amount',
|
setValue('amount', amount);
|
||||||
balances.balance.toFixed(selectedAsset.decimals)
|
handleAmountChange(amount);
|
||||||
);
|
|
||||||
clearErrors('amount');
|
clearErrors('amount');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -5,7 +5,7 @@ import { prepend0x } from '@vegaprotocol/smart-contracts';
|
|||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import { useSubmitApproval } from './use-submit-approval';
|
import { useSubmitApproval } from './use-submit-approval';
|
||||||
import { useSubmitFaucet } from './use-submit-faucet';
|
import { useSubmitFaucet } from './use-submit-faucet';
|
||||||
import { useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useDepositBalances } from './use-deposit-balances';
|
import { useDepositBalances } from './use-deposit-balances';
|
||||||
import { useDepositDialog } from './deposit-dialog';
|
import { useDepositDialog } from './deposit-dialog';
|
||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
@ -14,6 +14,7 @@ import {
|
|||||||
useBridgeContract,
|
useBridgeContract,
|
||||||
useEthereumConfig,
|
useEthereumConfig,
|
||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
|
import { usePersistentDeposit } from './use-persistent-deposit';
|
||||||
|
|
||||||
interface DepositManagerProps {
|
interface DepositManagerProps {
|
||||||
assetId?: string;
|
assetId?: string;
|
||||||
@ -28,7 +29,9 @@ export const DepositManager = ({
|
|||||||
}: DepositManagerProps) => {
|
}: DepositManagerProps) => {
|
||||||
const createEthTransaction = useEthTransactionStore((state) => state.create);
|
const createEthTransaction = useEthTransactionStore((state) => state.create);
|
||||||
const { config } = useEthereumConfig();
|
const { config } = useEthereumConfig();
|
||||||
const [assetId, setAssetId] = useState(initialAssetId);
|
const [persistentDeposit, savePersistentDeposit] =
|
||||||
|
usePersistentDeposit(initialAssetId);
|
||||||
|
const [assetId, setAssetId] = useState(persistentDeposit?.assetId);
|
||||||
const asset = assets.find((a) => a.id === assetId);
|
const asset = assets.find((a) => a.id === assetId);
|
||||||
const bridgeContract = useBridgeContract();
|
const bridgeContract = useBridgeContract();
|
||||||
const closeDepositDialog = useDepositDialog((state) => state.close);
|
const closeDepositDialog = useDepositDialog((state) => state.close);
|
||||||
@ -65,17 +68,26 @@ export const DepositManager = ({
|
|||||||
closeDepositDialog();
|
closeDepositDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAmountChange = useCallback(
|
||||||
|
(amount: string) => {
|
||||||
|
savePersistentDeposit({ ...persistentDeposit, amount });
|
||||||
|
},
|
||||||
|
[savePersistentDeposit, persistentDeposit]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DepositForm
|
<DepositForm
|
||||||
selectedAsset={asset}
|
selectedAsset={asset}
|
||||||
onDisconnect={reset}
|
onDisconnect={reset}
|
||||||
onSelectAsset={(id) => {
|
onSelectAsset={(id) => {
|
||||||
setAssetId(id);
|
setAssetId(id);
|
||||||
|
savePersistentDeposit({ assetId: id });
|
||||||
// When we change asset, also clear the tracked faucet/approve transactions so
|
// When we change asset, also clear the tracked faucet/approve transactions so
|
||||||
// we dont render stale UI
|
// we dont render stale UI
|
||||||
approve.reset();
|
approve.reset();
|
||||||
faucet.reset();
|
faucet.reset();
|
||||||
}}
|
}}
|
||||||
|
handleAmountChange={onAmountChange}
|
||||||
assets={sortBy(assets, 'name')}
|
assets={sortBy(assets, 'name')}
|
||||||
submitApprove={approve.perform}
|
submitApprove={approve.perform}
|
||||||
submitDeposit={submitDeposit}
|
submitDeposit={submitDeposit}
|
||||||
|
21
libs/deposits/src/lib/use-persistent-deposit.spec.ts
Normal file
21
libs/deposits/src/lib/use-persistent-deposit.spec.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||||
|
import { usePersistentDeposit } from './use-persistent-deposit';
|
||||||
|
|
||||||
|
describe('usePersistenDeposit', () => {
|
||||||
|
it('should return empty data', () => {
|
||||||
|
const { result } = renderHook(() => usePersistentDeposit());
|
||||||
|
expect(result.current).toEqual([{ assetId: '' }, expect.any(Function)]);
|
||||||
|
});
|
||||||
|
it('should return empty and properly saved data', async () => {
|
||||||
|
const aId = 'test';
|
||||||
|
const retObj = { assetId: 'test', amount: '1.00000' };
|
||||||
|
const { result } = renderHook(() => usePersistentDeposit(aId));
|
||||||
|
expect(result.current).toEqual([{ assetId: 'test' }, expect.any(Function)]);
|
||||||
|
await act(() => {
|
||||||
|
result.current[1](retObj);
|
||||||
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current[0]).toEqual(retObj);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
42
libs/deposits/src/lib/use-persistent-deposit.ts
Normal file
42
libs/deposits/src/lib/use-persistent-deposit.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'vega_deposit_store';
|
||||||
|
interface PersistedDeposit {
|
||||||
|
assetId: string;
|
||||||
|
amount?: string;
|
||||||
|
}
|
||||||
|
type PersistedDepositData = Record<string, PersistedDeposit>;
|
||||||
|
|
||||||
|
const usePersistentDepositStore = create<{
|
||||||
|
deposits: PersistedDepositData;
|
||||||
|
saveValue: (entry: PersistedDeposit) => void;
|
||||||
|
lastVisited?: PersistedDeposit;
|
||||||
|
}>()(
|
||||||
|
persist(
|
||||||
|
immer((set) => ({
|
||||||
|
deposits: {},
|
||||||
|
saveValue: (entry) =>
|
||||||
|
set((state) => {
|
||||||
|
const oldValue = state.deposits[entry.assetId] || null;
|
||||||
|
state.deposits[entry.assetId] = { ...oldValue, ...entry };
|
||||||
|
state.lastVisited = { ...oldValue, ...entry };
|
||||||
|
return state;
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
{ name: STORAGE_KEY }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const usePersistentDeposit = (
|
||||||
|
assetId?: string
|
||||||
|
): [PersistedDeposit, (entry: PersistedDeposit) => void] => {
|
||||||
|
const { deposits, lastVisited, saveValue } = usePersistentDepositStore();
|
||||||
|
const discoveredData = useMemo(() => {
|
||||||
|
return deposits[assetId || ''] || lastVisited || { assetId: assetId || '' };
|
||||||
|
}, [deposits, lastVisited, assetId]);
|
||||||
|
|
||||||
|
return [discoveredData, saveValue];
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user