Skip to content

Commit 27fe090

Browse files
authored
Merge pull request #1253 from satoshipay/bugfix/fix-trading-form-input-field-issues
Fix trading form input field issues
2 parents cd4b436 + d432bf8 commit 27fe090

File tree

9 files changed

+62
-30
lines changed

9 files changed

+62
-30
lines changed

i18n/locales/en/trading.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@
5555
"validation": {
5656
"primary-amount-missing": "No amount specified.",
5757
"primary-asset-missing": "No asset selected.",
58-
"secondary-asset-missing": "No asset selected.",
5958
"invalid-amount": "Invalid amount specified.",
60-
"invalid-price": "Invalid price"
59+
"invalid-price": "Invalid price",
60+
"not-enough-balance": "Exceeds your spendable balance",
61+
"secondary-asset-missing": "No asset selected."
6162
},
6263
"warning": {
6364
"title": "Warning",

src/Assets/components/CustomTrustline.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function CustomTrustlineDialog(props: Props) {
5656
/>
5757
<TextField
5858
inputProps={{
59-
pattern: "[0-9]*",
59+
pattern: "^[0-9]*(.[0-9]+)?$",
6060
inputMode: "decimal"
6161
}}
6262
fullWidth

src/Generic/components/FormFields.tsx

-4
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@ export const PriceInput = React.memo(function PriceInput(props: PriceInputProps)
7575
),
7676
...textfieldProps.InputProps
7777
}}
78-
type={
79-
// Prevent `The specified value (...) is not a valid number` warning
80-
readOnly ? undefined : "number"
81-
}
8278
style={{
8379
pointerEvents: props.readOnly ? "none" : undefined,
8480
...textfieldProps.style

src/Generic/lib/form.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import BigNumber, { BigSource } from "big.js"
2+
3+
export const isValidAmount = (amount: string) => /^[0-9]*([\.,][0-9]+)?$/.test(amount)
4+
5+
// replaces ',' with '.' in a string
6+
export function replaceCommaWithDot(input: string) {
7+
return input.replace(/,/g, ".")
8+
}
9+
10+
// should be used when creating a Big from user input because there are issues with
11+
// parsing a Big from number-strings with a comma on iOS
12+
export function FormBigNumber(value: BigSource) {
13+
if (typeof value === "string") {
14+
return BigNumber(replaceCommaWithDot(value))
15+
} else {
16+
return BigNumber(value)
17+
}
18+
}

src/Payment/components/PaymentForm.tsx

+11-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PriceInput, QRReader } from "~Generic/components/FormFields"
2222
import { formatBalance } from "~Generic/lib/balances"
2323
import { HorizontalLayout } from "~Layout/components/Box"
2424
import Portal from "~Generic/components/Portal"
25+
import { FormBigNumber, isValidAmount, replaceCommaWithDot } from "~Generic/lib/form"
2526

2627
export interface PaymentFormValues {
2728
amount: string
@@ -211,8 +212,15 @@ const PaymentForm = React.memo(function PaymentForm(props: PaymentFormProps) {
211212
error={Boolean(form.errors.amount)}
212213
inputRef={form.register({
213214
required: t<string>("payment.validation.no-price"),
214-
pattern: { value: /^[0-9]+(\.[0-9]+)?$/, message: t<string>("payment.validation.invalid-price") },
215-
validate: value => BigNumber(value).lte(spendableBalance) || t<string>("payment.validation.not-enough-funds")
215+
validate: value => {
216+
if (!isValidAmount(value) || FormBigNumber(value).eq(0)) {
217+
return t<string>("payment.validation.invalid-price")
218+
} else if (FormBigNumber(value).gt(spendableBalance)) {
219+
return t<string>("payment.validation.not-enough-funds")
220+
} else {
221+
return undefined
222+
}
223+
}
216224
})}
217225
label={form.errors.amount ? form.errors.amount.message : t("payment.inputs.price.label")}
218226
margin="normal"
@@ -355,7 +363,7 @@ function PaymentFormContainer(props: Props) {
355363

356364
const payment = await createPaymentOperation({
357365
asset: asset || Asset.native(),
358-
amount: formValues.amount,
366+
amount: replaceCommaWithDot(formValues.amount),
359367
destination,
360368
horizon
361369
})

src/Trading/components/TradingForm.tsx

+21-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import BigNumber from "big.js"
21
import React from "react"
32
import { Controller, useForm } from "react-hook-form"
43
import { useTranslation } from "react-i18next"
@@ -32,9 +31,10 @@ import {
3231
getAccountMinimumBalance,
3332
getSpendableBalance
3433
} from "~Generic/lib/stellar"
34+
import { FormBigNumber, isValidAmount } from "~Generic/lib/form"
3535
import { createTransaction } from "~Generic/lib/transaction"
3636
import { Box, HorizontalLayout, VerticalLayout } from "~Layout/components/Box"
37-
import { bigNumberToInputValue, isValidAmount, TradingFormValues, useCalculation } from "../hooks/form"
37+
import { bigNumberToInputValue, TradingFormValues, useCalculation } from "../hooks/form"
3838
import TradingPrice from "./TradingPrice"
3939

4040
const useStyles = makeStyles({
@@ -141,8 +141,8 @@ function TradingForm(props: Props) {
141141
}
142142

143143
const validateManualPrice = React.useCallback(() => {
144-
const value = BigNumber(manualPrice).gt(0) ? manualPrice : defaultPrice
145-
const valid = isValidAmount(value) && BigNumber(value).gt(0)
144+
const value = FormBigNumber(manualPrice).gt(0) ? manualPrice : defaultPrice
145+
const valid = isValidAmount(value) && FormBigNumber(value).gt(0)
146146
if (!valid) {
147147
if (!expanded) {
148148
setExpanded(true)
@@ -272,17 +272,23 @@ function TradingForm(props: Props) {
272272
inputRef={form.register({
273273
required: t<string>("trading.validation.primary-amount-missing"),
274274
validate: value => {
275-
const amountInvalid =
276-
primaryAmount.lt(0) ||
277-
(value.length > 0 && primaryAmount.eq(0)) ||
275+
const amountInvalid = primaryAmount.lt(0) || (value.length > 0 && primaryAmount.eq(0))
276+
const exceedsBalance =
278277
(props.primaryAction === "sell" && primaryBalance && primaryAmount.gt(spendablePrimaryBalance)) ||
279278
(props.primaryAction === "buy" && secondaryBalance && secondaryAmount.gt(spendableSecondaryBalance))
280-
return !amountInvalid || t<string>("trading.validation.invalid-amount")
279+
280+
if (amountInvalid) {
281+
return t<string>("trading.validation.invalid-amount")
282+
} else if (exceedsBalance) {
283+
return t<string>("trading.validation.not-enough-balance")
284+
} else {
285+
return true
286+
}
281287
}
282288
})}
283289
error={Boolean(form.errors.primaryAmountString)}
284290
inputProps={{
285-
pattern: "[0-9]*",
291+
pattern: "^[0-9]*(.[0-9]+)?$",
286292
inputMode: "decimal",
287293
min: "0.0000001",
288294
max: maxPrimaryAmount.toFixed(7),
@@ -320,7 +326,6 @@ function TradingForm(props: Props) {
320326
)}
321327
required
322328
style={{ flexGrow: 1, flexShrink: 1, width: "55%" }}
323-
type="number"
324329
/>
325330
</HorizontalLayout>
326331
<HorizontalLayout margin="8px 0 32px">
@@ -394,6 +399,12 @@ function TradingForm(props: Props) {
394399
}
395400
control={form.control}
396401
name="manualPrice"
402+
rules={{
403+
validate: value => {
404+
const valid = isValidAmount(value)
405+
return valid || t<string>("trading.validation.invalid-price")
406+
}
407+
}}
397408
valueName="manualPrice"
398409
/>
399410
</ExpansionPanelDetails>

src/Trading/components/TradingPrice.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const TradingPrice = React.forwardRef(function TradingPrice(props: TradingPriceP
5252
return (
5353
<TextField
5454
inputProps={{
55-
pattern: "[0-9]*",
55+
pattern: "^[0-9]*(.[0-9]+)?$",
5656
inputMode: "decimal",
5757
min: "0.0000001"
5858
}}
@@ -64,7 +64,6 @@ const TradingPrice = React.forwardRef(function TradingPrice(props: TradingPriceP
6464
onChange={props.onChange}
6565
onFocus={props.selectOnFocus ? event => event.target.select() : undefined}
6666
style={props.style}
67-
type="number"
6867
value={props.defaultPrice ? props.defaultPrice : props.manualPrice}
6968
/>
7069
)

src/Trading/hooks/form.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ import BigNumber from "big.js"
22
import { Asset, Horizon } from "stellar-sdk"
33
import { AccountData } from "~Generic/lib/account"
44
import { formatBalance, BalanceFormattingOptions } from "~Generic/lib/balances"
5+
import { FormBigNumber, isValidAmount } from "~Generic/lib/form"
56
import { calculateSpread, FixedOrderbookRecord } from "~Generic/lib/orderbook"
67
import { BASE_RESERVE, balancelineToAsset, getAccountMinimumBalance, getSpendableBalance } from "~Generic/lib/stellar"
78
import { useConversionOffers } from "./conversion"
89

910
export const bigNumberToInputValue = (bignum: BigNumber, overrides?: BalanceFormattingOptions) =>
10-
formatBalance(bignum, { minimumSignificants: 3, maximumSignificants: 9, ...overrides })
11-
12-
export const isValidAmount = (amount: string) => /^[0-9]+([\.,][0-9]+)?$/.test(amount)
11+
formatBalance(bignum, { minimumSignificants: 3, maximumSignificants: 9, groupThousands: false, ...overrides })
1312

1413
function findMatchingBalance(balances: AccountData["balances"], asset: Asset) {
1514
return balances.find(balance => balancelineToAsset(balance).equals(asset))
@@ -61,14 +60,14 @@ export function useCalculation(parameters: CalculationParameters): CalculationRe
6160
const price =
6261
manualPrice && isValidAmount(manualPrice)
6362
? priceMode === "secondary"
64-
? BigNumber(manualPrice)
65-
: BigNumber(manualPrice).eq(0) // prevent division by zero
63+
? FormBigNumber(manualPrice)
64+
: FormBigNumber(manualPrice).eq(0) // prevent division by zero
6665
? BigNumber(0)
67-
: BigNumber(1).div(manualPrice)
66+
: BigNumber(1).div(FormBigNumber(manualPrice))
6867
: BigNumber(0)
6968

7069
const primaryAmount =
71-
primaryAmountString && isValidAmount(primaryAmountString) ? BigNumber(primaryAmountString) : BigNumber(0)
70+
primaryAmountString && isValidAmount(primaryAmountString) ? FormBigNumber(primaryAmountString) : BigNumber(0)
7271

7372
const primaryBalance = primaryAsset ? findMatchingBalance(accountData.balances, primaryAsset) : undefined
7473
const secondaryBalance = secondaryAsset ? findMatchingBalance(accountData.balances, secondaryAsset) : undefined

src/Transaction/stories/SubmissionProgress.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ storiesOf("SubmissionProgress", module)
1818
<SubmissionProgress
1919
type={SubmissionType.default}
2020
promise={Promise.reject(new Error("Test error"))}
21-
onRetry={() => undefined}
21+
onRetry={() => Promise.resolve()}
2222
/>
2323
))

0 commit comments

Comments
 (0)