Skip to content

Commit

Permalink
[send] Use debounce to fix the send form (#3855)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgptr authored and alexlyp committed Apr 6, 2023
1 parent 81abac6 commit 23a2bb3
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 65 deletions.
103 changes: 61 additions & 42 deletions app/components/shared/SendTransaction/SendTransaction.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { FormattedMessage as T } from "react-intl";
import { useSendTransaction, useOutputs } from "./hooks";
import { useState, useEffect, useCallback } from "react";
import { useState, useEffect, useCallback, useMemo } from "react";
import { useMountEffect } from "hooks";
import { spring, presets } from "react-motion";
import { baseOutput } from "./helpers";
import { usePrevious } from "hooks";
import Form from "./Form";
import { debounce } from "lodash";

const SendTransaction = ({
onlySendSelfAllowed,
Expand Down Expand Up @@ -102,6 +103,9 @@ const SendTransaction = ({
/>
);
}
if (error) {
onClearTransaction();
}
const ref = { ...outputs[index] };
ref.data.amount = atomValue;
ref.data.error.amount = error;
Expand Down Expand Up @@ -191,22 +195,30 @@ const SendTransaction = ({
onSetOutputs(newOutputs);
};

const hasError = useCallback(() => {
const hasError = (outputs) => {
const outputHasError = (o) =>
!o.data.amount ||
o.data.destination.length === 0 ||
o.data.error.amount ||
o.data.error.address;
return outputs.some(outputHasError);
}, [outputs]);
};

const isValid = () =>
!!(
!hasError() &&
unsignedTransaction &&
!isConstructingTransaction &&
!constructTxLowBalance
);
const isValid = useCallback(
() =>
!!(
!hasError(outputs) &&
unsignedTransaction &&
!isConstructingTransaction &&
!constructTxLowBalance
),
[
outputs,
unsignedTransaction,
isConstructingTransaction,
constructTxLowBalance
]
);

const willEnter = () => ({
opacity: 0
Expand Down Expand Up @@ -268,36 +280,43 @@ const SendTransaction = ({
}));
};

const onAttemptConstructTransaction = useCallback(() => {
const confirmations = 0;
setSendAllAmount(account.spendable);
if (hasError()) return;
if (!isSendAll) {
return attemptConstructTransaction(
account.value,
confirmations,
outputs.map(({ data }) => ({
amount: data.amount,
destination: data.destination
}))
);
} else {
return attemptConstructTransaction(
account.value,
confirmations,
outputs,
true
);
}
}, [
setSendAllAmount,
hasError,
isSendAll,
attemptConstructTransaction,
account.spendable,
account.value,
outputs
]);
const onAttemptConstructTransaction = useCallback(
(outputs) => {
const confirmations = 0;
setSendAllAmount(account.spendable);
if (hasError(outputs)) return;
if (!isSendAll) {
return attemptConstructTransaction(
account.value,
confirmations,
outputs.map(({ data }) => ({
amount: data.amount,
destination: data.destination
}))
);
} else {
return attemptConstructTransaction(
account.value,
confirmations,
outputs,
true
);
}
},
[
setSendAllAmount,
isSendAll,
attemptConstructTransaction,
account.spendable,
account.value
]
);

const onAttemptConstructTransactionCallback = useMemo(() => {
return debounce((outputs) => {
onAttemptConstructTransaction(outputs);
}, 300);
}, [onAttemptConstructTransaction]);

// Executes on component updates
useEffect(() => {
Expand Down Expand Up @@ -330,7 +349,7 @@ const SendTransaction = ({
}

if (prevOutputs != outputs || prevAccount != account) {
onAttemptConstructTransaction();
onAttemptConstructTransactionCallback(outputs);
}
}, [
publishTxResponse,
Expand All @@ -344,7 +363,7 @@ const SendTransaction = ({
outputs,
prevOutputs,
onSetOutputs,
onAttemptConstructTransaction,
onAttemptConstructTransactionCallback,
onGetNextAddressAttempt,
account,
prevAccount,
Expand Down
62 changes: 39 additions & 23 deletions test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,18 @@ beforeEach(() => {
selectors.getNotMixedAcctIfAllowed = jest.fn(() => [0, 2]);
selectors.isTrezor = jest.fn(() => false);
selectors.isWatchingOnly = jest.fn(() => false);
selectors.isConstructingTransaction = jest.fn(() => false);
selectors.constructTxRequestAttempt = jest.fn(() => false);
selectors.getRunningIndicator = jest.fn(() => false);
selectors.currencyDisplay = jest.fn(() => DCR);
mockConstructTransactionAttempt = controlActions.constructTransactionAttempt =
jest.fn(() => () => {});
jest.fn(() => (dispatch) => {
dispatch({
type: controlActions.CONSTRUCTTX_ATTEMPT,
constructTxRequestAttempt: true
});
dispatch({
type: controlActions.CONSTRUCTTX_SUCCESS
});
});
mockValidateAddress = controlActions.validateAddress = jest.fn(() => () => {
return {
error: "ERR_INVALID_ADDR_TOOSHORT",
Expand Down Expand Up @@ -270,8 +276,10 @@ test("test amount input", async () => {
// click on send all amount button
user.click(sendAllButton);
expect(queryAmountInput()).not.toBeInTheDocument();
expect(screen.getByText("Amount").nextElementSibling.textContent).toBe(
`${mockMixedAccount.spendableAndUnit}100% of Account Balance`
await wait(() =>
expect(screen.getByText("Amount").nextElementSibling.textContent).toBe(
`${mockMixedAccount.spendableAndUnit}100% of Account Balance`
)
);
expect(addOutputButton.disabled).toBe(true);

Expand Down Expand Up @@ -359,11 +367,14 @@ test("test `send to` input", async () => {
});
await wait(() => expect(sendToInput.value).toBe(mockValidAddress));
expect(screen.queryByText(expectedErrorMsg)).not.toBeInTheDocument();
expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
[{ amount: validAmount * 100000000, destination: mockValidAddress }],
undefined

await wait(() =>
expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
[{ amount: validAmount * 100000000, destination: mockValidAddress }],
undefined
)
);

// test clear button
Expand Down Expand Up @@ -441,8 +452,10 @@ test("`Sending from unmixed account` is allowed", async () => {
// changing account while sending all mode is on
// should change the amount accordingly click on send all amount button
user.click(getSendAllButton());
expect(screen.getByText("Amount").nextElementSibling.textContent).toBe(
`${mockEmptyAccount.spendableAndUnit}100% of Account Balance`
await wait(() =>
expect(screen.getByText("Amount").nextElementSibling.textContent).toBe(
`${mockEmptyAccount.spendableAndUnit}100% of Account Balance`
)
);
user.click(screen.getByText(mockEmptyAccount.name));
user.click(screen.getByText(mockAccount2.name));
Expand Down Expand Up @@ -470,11 +483,13 @@ const fillOutputForm = async (index) => {
});
}

expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
expectedOutputs,
undefined
await wait(() =>
expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
expectedOutputs,
undefined
)
);
};

Expand Down Expand Up @@ -523,16 +538,17 @@ test("send funds to another account", async () => {
user.click(sendSelfButton);
user.click(screen.getAllByRole("combobox")[1]);
selectors.nextAddressAccount = jest.fn(() => mockAccount2);
selectors.publishTxResponse = jest.fn(() => "mocknewpublishtxresponse");
user.click(screen.getByText(mockAccount2.name));
await wait(() =>
expect(screen.queryByText(mockDefaultAccount.name)).not.toBeInTheDocument()
);
expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
[{ amount: validAmount * 100000000, destination: mockNextAddress }],
undefined
await wait(() =>
expect(mockConstructTransactionAttempt).toHaveBeenCalledWith(
mockMixedAccountValue,
0,
[{ amount: validAmount * 100000000, destination: mockNextAddress }],
undefined
)
);
expect(mockGetNextAddressAttempt).toHaveBeenCalled();

Expand Down

0 comments on commit 23a2bb3

Please sign in to comment.