From 222915fb078f6f350608b7075b319912794a234e Mon Sep 17 00:00:00 2001 From: wjrjerome Date: Mon, 12 Aug 2024 16:11:47 +1000 Subject: [PATCH] fix: issue with fee UI infinite react rendering loop --- .github/workflows/ci.yml | 4 +- .github/workflows/publish.yaml | 8 +-- .../components/Staking/Form/StakingAmount.tsx | 49 +++++++++---- .../components/Staking/Form/StakingFee.tsx | 23 ++++--- src/app/components/Staking/Staking.tsx | 69 +++++++++---------- src/utils/isStakingSignReady.ts | 6 ++ tests/utils/isStakingSignReady.test.ts | 14 +++- 7 files changed, 105 insertions(+), 68 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7083d434..b3976930 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,11 +3,11 @@ name: ci on: pull_request: branches: - - '**' + - "**" jobs: lint_test: uses: babylonlabs-io/.github/.github/workflows/reusable_node_lint_test.yml@v0.3.0 with: run-build: true - run-unit-tests: true \ No newline at end of file + run-unit-tests: true diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 96ed64c2..2c9f4030 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -3,10 +3,10 @@ name: docker_publish on: push: branches: - - 'main' - - 'dev' + - "main" + - "dev" tags: - - '*' + - "*" jobs: lint_test: @@ -14,7 +14,7 @@ jobs: with: run-build: true run-unit-tests: true - + docker_build: needs: [lint_test] runs-on: ubuntu-22.04 diff --git a/src/app/components/Staking/Form/StakingAmount.tsx b/src/app/components/Staking/Form/StakingAmount.tsx index a680754b..3f49d514 100644 --- a/src/app/components/Staking/Form/StakingAmount.tsx +++ b/src/app/components/Staking/Form/StakingAmount.tsx @@ -12,6 +12,7 @@ interface StakingAmountProps { btcWalletBalanceSat?: number; onStakingAmountSatChange: (inputAmountSat: number) => void; reset: boolean; + isSufficientBalance: boolean; } export const StakingAmount: React.FC = ({ @@ -20,11 +21,13 @@ export const StakingAmount: React.FC = ({ btcWalletBalanceSat, onStakingAmountSatChange, reset, + isSufficientBalance, }) => { const [value, setValue] = useState(""); const [error, setError] = useState(""); // Track if the input field has been interacted with const [touched, setTouched] = useState(false); + const [blurred, setBlurred] = useState(false); const errorLabel = "Staking amount"; const generalErrorMessage = "You should input staking amount"; @@ -51,16 +54,8 @@ export const StakingAmount: React.FC = ({ } }; - const handleBlur = (_e: FocusEvent) => { - if (!btcWalletBalanceSat) return; - setTouched(true); - - if (value === "") { - onStakingAmountSatChange(0); - setError(generalErrorMessage); - return; - } - + useEffect(() => { + if (!btcWalletBalanceSat || value === "") return; const numValue = parseFloat(value); const satoshis = btcToSatoshi(numValue); @@ -71,7 +66,7 @@ export const StakingAmount: React.FC = ({ message: `${errorLabel} must be a valid number.`, }, { - valid: numValue !== 0, + valid: numValue > 0, message: `${errorLabel} must be greater than 0.`, }, { @@ -91,18 +86,46 @@ export const StakingAmount: React.FC = ({ message: `${errorLabel} must have no more than 8 decimal points.`, }, ]; - // Find the first failing validation const firstInvalid = validations.find((validation) => !validation.valid); - if (firstInvalid) { onStakingAmountSatChange(0); setError(firstInvalid.message); + return; } else { setError(""); onStakingAmountSatChange(satoshis); setValue(maxDecimals(satoshiToBtc(satoshis), 8).toString()); } + // The fee + amount check can only be placed after onStakingAmountSatChange is called + // This is due to isSufficientBalance being dependent on the value of + // stakingAmountSat in parent component + if (!isSufficientBalance && validateDecimalPoints(value)) { + return setError( + "Not enough balance to cover the staking value and its fees", + ); + } + }, [ + isSufficientBalance, + blurred, + value, + btcWalletBalanceSat, + minStakingAmountSat, + maxStakingAmountSat, + onStakingAmountSatChange, + coinName, + ]); + + const handleBlur = (_e: FocusEvent) => { + if (!btcWalletBalanceSat) return; + setTouched(true); + + if (value === "") { + onStakingAmountSatChange(0); + setError(generalErrorMessage); + return; + } + setBlurred(!blurred); }; const minStakeAmount = maxDecimals(satoshiToBtc(minStakingAmountSat), 8); diff --git a/src/app/components/Staking/Form/StakingFee.tsx b/src/app/components/Staking/Form/StakingFee.tsx index d2da2131..3eed286d 100644 --- a/src/app/components/Staking/Form/StakingFee.tsx +++ b/src/app/components/Staking/Form/StakingFee.tsx @@ -10,7 +10,7 @@ import { LoadingSmall } from "../../Loading/Loading"; interface StakingFeeProps { stakingFeeSat: number; selectedFeeRate: number; - onSelectedFeeRateChange: (fee: number) => void; + onSelectedFeeRateChange: (feeRate: number) => void; reset: boolean; // optional as component shows loading state mempoolFeeRates?: Fees; @@ -46,6 +46,11 @@ export const StakingFee: React.FC = ({ } }; + // If staking fee has not been calculated, return null + if (!stakingFeeSat) { + return null; + } + const defaultModeRender = () => { return (
@@ -57,16 +62,12 @@ export const StakingFee: React.FC = ({ ) : ( )} - {stakingFeeSat ? ( -

- Transaction fee amount:{" "} - - {satoshiToBtc(stakingFeeSat)} {coinName} - -

- ) : ( - - )} +

+ Transaction fee amount:{" "} + + {satoshiToBtc(stakingFeeSat)} {coinName} + +