From 1a9743e6394ee2f94753f8dedaf80a97a7a353a5 Mon Sep 17 00:00:00 2001 From: Franco Montenegro Date: Sat, 23 Jul 2022 18:33:00 -0400 Subject: [PATCH] Properly handle decimals in supports liquidate component. (#7648) --- ui/component/supportsLiquidate/view.jsx | 103 ++++++++++++++++-------- 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/ui/component/supportsLiquidate/view.jsx b/ui/component/supportsLiquidate/view.jsx index 3c5771b99..059d11ccf 100644 --- a/ui/component/supportsLiquidate/view.jsx +++ b/ui/component/supportsLiquidate/view.jsx @@ -1,6 +1,6 @@ // @flow import * as ICONS from 'constants/icons'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import CreditAmount from 'component/common/credit-amount'; import Button from 'component/button'; import { Form, FormField } from 'component/common/form'; @@ -24,67 +24,100 @@ type Props = { const SupportsLiquidate = (props: Props) => { const { claim, abandonSupportForClaim, handleClose, abandonClaimError } = props; const [previewBalance, setPreviewBalance] = useState(undefined); - const [amount, setAmount] = useState(-1); + const [defaultValueAssigned, setDefaultValueAssigned] = useState(false); + const [unlockTextAmount, setUnlockTextAmount] = useState(''); const [error, setError] = useState(false); const initialMessage = __('How much would you like to unlock?'); const [message, setMessage] = useState(initialMessage); + const amount = Number(unlockTextAmount) || 0; + const defaultValue = previewBalance ? previewBalance * 0.25 : 0; const keep = amount >= 0 - ? Boolean(previewBalance) && Number.parseFloat(String(Number(previewBalance) - Number(amount))).toFixed(8) - : Boolean(previewBalance) && Number.parseFloat(String((Number(previewBalance) / 4) * 3)).toFixed(8); // default unlock 25% + ? Boolean(previewBalance) && Number.parseFloat(String(Number(previewBalance) - amount)).toFixed(8) + : Boolean(previewBalance) && Number.parseFloat(String(defaultValue * 3)).toFixed(8); const claimId = claim && claim.claim_id; const type = claim.value_type; useEffect(() => { if (claimId && abandonSupportForClaim) { - abandonSupportForClaim(claimId, type, false, true).then(r => { + abandonSupportForClaim(claimId, type, false, true).then((r) => { setPreviewBalance(r.total_input); }); } }, [abandonSupportForClaim, claimId, type, setPreviewBalance]); function handleSubmit() { - abandonSupportForClaim(claimId, type, keep, false).then(r => { + abandonSupportForClaim(claimId, type, keep, false).then((r) => { if (r) { handleClose(); } }); } - function handleChange(a) { - if (a === undefined || isNaN(Number(a))) { + const handleRangeChange = useCallback( + (newValue) => { + setUnlockTextAmount(String(newValue)); + }, + [setUnlockTextAmount] + ); + + const handleChangeUnlockText = useCallback( + (newValue) => { + // Get rid of all characters except digits, commas and periods. + const onlyValidAmount = newValue.replace(/[^0-9.,]+/, ''); + setUnlockTextAmount(onlyValidAmount); + }, + [setUnlockTextAmount] + ); + + const handleUnlockTextFocus = useCallback(() => { + // Get rid of empty zero when user starts typing (small ux improvement) + if (Number(unlockTextAmount) === 0) { + setUnlockTextAmount(''); + } + }, [unlockTextAmount, setUnlockTextAmount]); + + const handleUnlockTextBlur = useCallback(() => { + if (!unlockTextAmount || isNaN(Number(unlockTextAmount))) { + setUnlockTextAmount(previewBalance ? String(defaultValue) : '0'); + } + }, [unlockTextAmount, setUnlockTextAmount, previewBalance, defaultValue]); + + useEffect(() => { + if (defaultValueAssigned || !previewBalance || unlockTextAmount) { + return; + } + setUnlockTextAmount(String(defaultValue)); + setDefaultValueAssigned(true); + }, [defaultValueAssigned, previewBalance, unlockTextAmount, setUnlockTextAmount, setDefaultValueAssigned]); + + // Update message & error based on unlock amount. + useEffect(() => { + const unlockAmount = Number(unlockTextAmount); + const previewBalanceNumber = Number(previewBalance); + if (unlockTextAmount && isNaN(unlockAmount)) { setMessage(__('Amount must be a number')); setError(true); - setAmount(0); - } else if (a === '') { - setAmount(0); - setError(true); - setMessage(__('Amount cannot be blank')); - } else if (Number(a) > Number(previewBalance)) { + } else if (unlockAmount > previewBalanceNumber) { setMessage(__('Amount cannot be more than available')); - setError(false); - } else if (Number(a) === Number(previewBalance)) { - setMessage(__(`She's about to close up the library!`)); - setAmount(a); - setError(false); - } else if (Number(a) > Number(previewBalance) / 2) { - setMessage(__('Your content will do better with more staked on it')); - setAmount(a); - setError(false); - } else if (Number(a) === 0) { - setMessage(__('Amount cannot be zero')); - setAmount(0); setError(true); - } else if (Number(a) <= Number(previewBalance) / 2) { + } else if (Math.abs(unlockAmount - previewBalanceNumber) <= Number.EPSILON) { + setMessage(__(`She's about to close up the library!`)); + setError(false); + } else if (unlockAmount > previewBalanceNumber / 2) { + setMessage(__('Your content will do better with more staked on it')); + setError(false); + } else if (unlockAmount === 0) { + setMessage(__('Amount cannot be zero')); + setError(true); + } else if (unlockAmount <= previewBalanceNumber / 2) { setMessage(__('A prudent choice')); - setAmount(Number(a)); setError(false); } else { setMessage(initialMessage); - setAmount(a); setError(false); } - } + }, [unlockTextAmount, previewBalance, setMessage, setError]); return ( { min={0} step={0.01} max={previewBalance} - value={Number(amount) >= 0 ? amount : previewBalance / 4} // by default, set it to 25% of available - onChange={e => handleChange(e.target.value)} + value={amount} + onChange={(e) => handleRangeChange(e.target.value)} /> = 0 ? amount || '' : previewBalance && previewBalance / 4} + value={unlockTextAmount} helper={message} - onChange={e => handleChange(e.target.value)} + onFocus={handleUnlockTextFocus} + onChange={(e) => handleChangeUnlockText(e.target.value)} + onBlur={handleUnlockTextBlur} /> )}