diff options
-rw-r--r-- | packages/taler-wallet-webextension/src/components/AmountField.stories.tsx | 11 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/components/AmountField.tsx | 152 |
2 files changed, 81 insertions, 82 deletions
diff --git a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx index f253d1996..9ac17155c 100644 --- a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx +++ b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx @@ -31,7 +31,11 @@ export default { }; function RenderAmount(): VNode { - const [value, setValue] = useState<AmountJson | undefined>(undefined); + const [value, setValue] = useState<AmountJson | undefined>({ + currency: "USD", + value: 1, + fraction: 0, + }); const error = value === undefined ? undefined : undefined; @@ -53,7 +57,10 @@ function RenderAmount(): VNode { handler={handler} /> <p> - <pre>{JSON.stringify(value, undefined, 2)}</pre> + <pre> + value : {value?.value} <br /> + fraction : {value?.fraction} + </pre> </p> </Fragment> ); diff --git a/packages/taler-wallet-webextension/src/components/AmountField.tsx b/packages/taler-wallet-webextension/src/components/AmountField.tsx index 786244433..88ac71dd8 100644 --- a/packages/taler-wallet-webextension/src/components/AmountField.tsx +++ b/packages/taler-wallet-webextension/src/components/AmountField.tsx @@ -25,6 +25,7 @@ import { } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useTranslationContext } from "../context/translation.js"; import { AmountFieldHandler } from "../mui/handlers.js"; import { TextField } from "../mui/TextField.js"; @@ -47,80 +48,49 @@ export function AmountField({ required?: boolean; handler: AmountFieldHandler; }): VNode { + const { i18n } = useTranslationContext(); const [unit, setUnit] = useState(1); - const [decimalPlaces, setDecimalPlaces] = useState<number | undefined>( - undefined, - ); - const currency = handler.value.currency; + const [error, setError] = useState<string>(""); - let hd = Math.floor(Math.log10(highestDenom || 1) / 3); - let ld = Math.ceil((-1 * Math.log10(lowestDenom || 1)) / 3); + const normal = normalize(handler.value, unit); + const previousValue = Amounts.stringifyValue(normal); - const currencyLabels: Array<{ name: string; unit: number }> = [ - { - name: currency, - unit: 1, - }, - ]; + const [textValue, setTextValue] = useState<string>(previousValue); - while (hd > 0) { - currencyLabels.push({ - name: `${HIGH_DENOM_SYMBOL[hd]}${currency}`, - unit: Math.pow(10, hd * 3), - }); - hd--; - } - while (ld > 0) { - currencyLabels.push({ - name: `${LOW_DENOM_SYMBOL[ld]}${currency}`, - unit: Math.pow(10, -1 * ld * 3), - }); - ld--; + function updateUnit(newUnit: number) { + setUnit(newUnit); + const newNorm = normalize(handler.value, newUnit); + setTextValue(Amounts.stringifyValue(newNorm)); } - const previousValue = Amounts.stringifyValue(handler.value, decimalPlaces); - - const normal = normalize(handler.value, unit) ?? handler.value; + const currency = handler.value.currency; - let textValue = Amounts.stringifyValue(normal, decimalPlaces); - if (decimalPlaces === 0) { - textValue += "."; - } + const currencyLabels = buildLabelsForCurrency( + currency, + lowestDenom, + highestDenom, + ); function positiveAmount(value: string): string { - // setDotAtTheEnd(value.endsWith(".")); - // const dotAtTheEnd = value.endsWith("."); if (!value) { if (handler.onInput) { handler.onInput(Amounts.zeroOfCurrency(currency)); } - return ""; - } - try { - //remove all but last dot - const withoutDots = value.replace(/(\.)(?=.*\1)/g, ""); - const parts = withoutDots.split("."); - setDecimalPlaces(parts.length === 1 ? undefined : parts[1].length); - - //FIXME: should normalize before parsing - //parsing first add some restriction on the rage of the values - const parsed = parseValue(currency, withoutDots); - - if (!parsed || parsed.value < 0) { - return previousValue; - } + } else + try { + const parsed = Amounts.parseOrThrow(`${currency}:${value.trim()}`); - const realValue = denormalize(parsed, unit); + const realValue = denormalize(parsed, unit); - // console.log(real, unit, normal); - if (realValue && handler.onInput) { - handler.onInput(realValue); + if (handler.onInput) { + handler.onInput(realValue); + } + setError(""); + } catch (e) { + setError(i18n.str`Amount is not valid`); } - return withoutDots; - } catch (e) { - // do nothing - } - return previousValue; + setTextValue(value); + return value; } return ( @@ -149,7 +119,7 @@ export function AmountField({ disabled={!handler.onInput} onChange={(e) => { const unit = Number.parseFloat(e.currentTarget.value); - setUnit(unit); + updateUnit(unit); }} value={String(unit)} style={{ @@ -171,29 +141,11 @@ export function AmountField({ disabled={!handler.onInput} onInput={positiveAmount} /> + {error && <div style={{ color: "red" }}>{error}</div>} </Fragment> ); } -function parseValue(currency: string, s: string): AmountJson | undefined { - const [intPart, fractPart] = s.split("."); - const tailPart = !fractPart - ? "0" - : fractPart.substring(0, amountFractionalLength); - - const value = Number.parseInt(intPart, 10); - const parsedTail = Number.parseFloat(`.${tailPart}`); - if (Number.isNaN(value) || Number.isNaN(parsedTail)) { - return undefined; - } - if (value > amountMaxValue) { - return undefined; - } - - const fraction = Math.round(amountFractionalBase * parsedTail); - return { currency, fraction, value }; -} - /** * Return the real value of a normalized unit * If the value is 20 and the unit is kilo == 1000 the returned value will be amount * 1000 @@ -201,7 +153,7 @@ function parseValue(currency: string, s: string): AmountJson | undefined { * @param unit * @returns */ -function denormalize(amount: AmountJson, unit: number): AmountJson | undefined { +function denormalize(amount: AmountJson, unit: number): AmountJson { if (unit === 1 || Amounts.isZero(amount)) return amount; const result = unit < 1 @@ -218,7 +170,7 @@ function denormalize(amount: AmountJson, unit: number): AmountJson | undefined { * @param unit * @returns */ -function normalize(amount: AmountJson, unit: number): AmountJson | undefined { +function normalize(amount: AmountJson, unit: number): AmountJson { if (unit === 1 || Amounts.isZero(amount)) return amount; const result = unit < 1 @@ -226,3 +178,43 @@ function normalize(amount: AmountJson, unit: number): AmountJson | undefined { : Amounts.divide(amount, unit); return result; } + +/** + * Take every label in HIGH_DENOM_SYMBOL and LOW_DENOM_SYMBOL and create + * which create the corresponding unit multiplier + * @param currency + * @param lowestDenom + * @param highestDenom + * @returns + */ +function buildLabelsForCurrency( + currency: string, + lowestDenom: number, + highestDenom: number, +): Array<{ name: string; unit: number }> { + let hd = Math.floor(Math.log10(highestDenom || 1) / 3); + let ld = Math.ceil((-1 * Math.log10(lowestDenom || 1)) / 3); + + const result: Array<{ name: string; unit: number }> = [ + { + name: currency, + unit: 1, + }, + ]; + + while (hd > 0) { + result.push({ + name: `${HIGH_DENOM_SYMBOL[hd]}${currency}`, + unit: Math.pow(10, hd * 3), + }); + hd--; + } + while (ld > 0) { + result.push({ + name: `${LOW_DENOM_SYMBOL[ld]}${currency}`, + unit: Math.pow(10, -1 * ld * 3), + }); + ld--; + } + return result; +} |