diff options
author | Sebastian <sebasjm@gmail.com> | 2023-03-11 18:19:38 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-03-11 18:20:16 -0300 |
commit | c67d94c56e154be4b2cf91572cdc2d8d2da7f8e4 (patch) | |
tree | fbb9444857d4e11f348c051b9c470e9295990096 /packages/demobank-ui/src/pages | |
parent | b72729f06535f12af974035b141a30320e75575c (diff) |
fix: #7753
Diffstat (limited to 'packages/demobank-ui/src/pages')
5 files changed, 78 insertions, 76 deletions
diff --git a/packages/demobank-ui/src/pages/AccountPage.tsx b/packages/demobank-ui/src/pages/AccountPage.tsx index bd9a5acd7..c6ec7c88e 100644 --- a/packages/demobank-ui/src/pages/AccountPage.tsx +++ b/packages/demobank-ui/src/pages/AccountPage.tsx @@ -20,8 +20,6 @@ import { useTranslationContext, } from "@gnu-taler/web-util/lib/index.browser"; import { Fragment, h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { Cashouts } from "../components/Cashouts/index.js"; import { Transactions } from "../components/Transactions/index.js"; import { useAccountDetails } from "../hooks/access.js"; import { PaymentOptions } from "./PaymentOptions.js"; @@ -44,8 +42,8 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode { } const { data } = result; - const balance = Amounts.parse(data.balance.amount); - const errorParsingBalance = !balance; + const balance = Amounts.parseOrThrow(data.balance.amount); + const debitThreshold = Amounts.parseOrThrow(data.debitThreshold); const payto = parsePaytoUri(data.paytoUri); if (!payto || !payto.isKnown || payto.targetType !== "iban") { return ( @@ -54,7 +52,9 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode { } const accountNumber = payto.iban; const balanceIsDebit = data.balance.credit_debit_indicator == "debit"; - + const limit = balanceIsDebit + ? Amounts.sub(debitThreshold, balance).amount + : Amounts.add(balance, debitThreshold).amount; return ( <Fragment> <div> @@ -66,44 +66,29 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode { </h1> </div> - {errorParsingBalance ? ( - <div class="informational informational-fail" style={{ marginTop: 8 }}> - <div style={{ display: "flex", justifyContent: "space-between" }}> - <p> - <b>Server Error: invalid balance</b> - </p> - </div> - <p>Your account is in an invalid state.</p> - </div> - ) : ( - <Fragment> - <section id="assets"> - <div class="asset-summary"> - <h2>{i18n.str`Bank account balance`}</h2> - {!balance ? ( - <div class="large-amount" style={{ color: "gray" }}> - Waiting server response... - </div> - ) : ( - <div class="large-amount amount"> - {balanceIsDebit ? <b>-</b> : null} - <span class="value">{`${Amounts.stringifyValue( - balance, - )}`}</span> - - <span class="currency">{`${balance.currency}`}</span> - </div> - )} + <section id="assets"> + <div class="asset-summary"> + <h2>{i18n.str`Bank account balance`}</h2> + {!balance ? ( + <div class="large-amount" style={{ color: "gray" }}> + Waiting server response... </div> - </section> - <section id="payments"> - <div class="payments"> - <h2>{i18n.str`Payments`}</h2> - <PaymentOptions currency={balance.currency} /> + ) : ( + <div class="large-amount amount"> + {balanceIsDebit ? <b>-</b> : null} + <span class="value">{`${Amounts.stringifyValue(balance)}`}</span> + + <span class="currency">{`${balance.currency}`}</span> </div> - </section> - </Fragment> - )} + )} + </div> + </section> + <section id="payments"> + <div class="payments"> + <h2>{i18n.str`Payments`}</h2> + <PaymentOptions limit={limit} /> + </div> + </section> <section style={{ marginTop: "2em" }}> <div class="active"> diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx index 9bd799746..128b47114 100644 --- a/packages/demobank-ui/src/pages/BusinessAccount.tsx +++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx @@ -212,8 +212,7 @@ function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse< oldResult.ratios_and_fees.sell_at_ratio || result.data.ratios_and_fees.sell_out_fee !== oldResult.ratios_and_fees.sell_out_fee || - result.data.ratios_and_fees.fiat_currency !== - oldResult.ratios_and_fees.fiat_currency); + result.data.fiat_currency !== oldResult.fiat_currency); return { ...result, @@ -238,16 +237,19 @@ function CreateCashout({ if (!result.ok) return onLoadNotOk(result); if (!ratiosResult.ok) return onLoadNotOk(ratiosResult); const config = ratiosResult.data; - const maybeBalance = Amounts.parse(result.data.balance.amount); - if (!maybeBalance) return <div>error</div>; - const balance = maybeBalance; + const balance = Amounts.parseOrThrow(result.data.balance.amount); + const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold); const zero = Amounts.zeroOfCurrency(balance.currency); + const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit"; + const limit = balanceIsDebit + ? Amounts.sub(debitThreshold, balance).amount + : Amounts.add(balance, debitThreshold).amount; const sellRate = config.ratios_and_fees.sell_at_ratio; const sellFee = !config.ratios_and_fees.sell_out_fee ? zero : Amounts.fromFloat(config.ratios_and_fees.sell_out_fee, balance.currency); - const fiatCurrency = config.ratios_and_fees.fiat_currency; + const fiatCurrency = config.fiat_currency; if (!sellRate || sellRate < 0) return <div>error rate</div>; @@ -278,12 +280,12 @@ function CreateCashout({ ? i18n.str`required` : !amount ? i18n.str`could not be parsed` - : Amounts.cmp(balance, amount_debit) === -1 + : Amounts.cmp(limit, amount_debit) === -1 ? i18n.str`balance is not enough` : Amounts.cmp(credit_before_fee, sellFee) === -1 - ? i18n.str`amount is not enough` + ? i18n.str`the total amount to transfer does not cover the fees` : Amounts.isZero(amount_credit) - ? i18n.str`amount is not enough` + ? i18n.str`the total transfer at destination will be zero` : undefined, channel: !form.channel ? i18n.str`required` : undefined, }); diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx index 610efafc0..291f2aa9e 100644 --- a/packages/demobank-ui/src/pages/PaymentOptions.tsx +++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx @@ -14,6 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +import { AmountJson } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -25,7 +26,7 @@ import { WalletWithdrawForm } from "./WalletWithdrawForm.js"; * Let the user choose a payment option, * then specify the details trigger the action. */ -export function PaymentOptions({ currency }: { currency: string }): VNode { +export function PaymentOptions({ limit }: { limit: AmountJson }): VNode { const { i18n } = useTranslationContext(); const { pageStateSetter } = usePageContext(); @@ -62,7 +63,7 @@ export function PaymentOptions({ currency }: { currency: string }): VNode { <h3>{i18n.str`Obtain digital cash`}</h3> <WalletWithdrawForm focus - currency={currency} + limit={limit} onSuccess={(data) => { pageStateSetter((prevState: PageStateType) => ({ ...prevState, @@ -80,7 +81,7 @@ export function PaymentOptions({ currency }: { currency: string }): VNode { <h3>{i18n.str`Transfer to bank account`}</h3> <PaytoWireTransferForm focus - currency={currency} + limit={limit} onSuccess={() => { pageStateSetter((prevState: PageStateType) => ({ ...prevState, diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index f25680481..027f8e25a 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -15,6 +15,7 @@ */ import { + AmountJson, Amounts, buildPayto, HttpStatusCode, @@ -30,7 +31,11 @@ import { h, VNode } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; import { PageStateType } from "../context/pageState.js"; import { useAccessAPI } from "../hooks/access.js"; -import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js"; +import { + buildRequestErrorMessage, + undefinedIfEmpty, + validateIBAN, +} from "../utils.js"; import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js"; const logger = new Logger("PaytoWireTransferForm"); @@ -39,12 +44,12 @@ export function PaytoWireTransferForm({ focus, onError, onSuccess, - currency, + limit, }: { focus?: boolean; onError: (e: PageStateType["error"]) => void; onSuccess: () => void; - currency: string; + limit: AmountJson; }): VNode { // const backend = useBackendContext(); // const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button? @@ -65,7 +70,8 @@ export function PaytoWireTransferForm({ if (focus) ref.current?.focus(); }, [focus, isRawPayto]); - let parsedAmount = undefined; + const trimmedAmountStr = amount?.trim(); + const parsedAmount = Amounts.parse(`${limit.currency}:${trimmedAmountStr}`); const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/; const errorsWire = undefinedIfEmpty({ @@ -73,14 +79,16 @@ export function PaytoWireTransferForm({ ? i18n.str`Missing IBAN` : !IBAN_REGEX.test(iban) ? i18n.str`IBAN should have just uppercased letters and numbers` - : undefined, + : validateIBAN(iban, i18n), subject: !subject ? i18n.str`Missing subject` : undefined, - amount: !amount + amount: !trimmedAmountStr ? i18n.str`Missing amount` - : !(parsedAmount = Amounts.parse(`${currency}:${amount}`)) + : !parsedAmount ? i18n.str`Amount is not valid` : Amounts.isZero(parsedAmount) ? i18n.str`Should be greater than 0` + : Amounts.cmp(limit, parsedAmount) === -1 + ? i18n.str`balance is not enough` : undefined, }); @@ -143,10 +151,10 @@ export function PaytoWireTransferForm({ type="text" readonly class="currency-indicator" - size={currency?.length} - maxLength={currency?.length} + size={limit.currency.length} + maxLength={limit.currency.length} tabIndex={-1} - value={currency} + value={limit.currency} /> <input @@ -185,7 +193,7 @@ export function PaytoWireTransferForm({ try { await createTransaction({ paytoUri, - amount: `${currency}:${amount}`, + amount: `${limit.currency}:${amount}`, }); onSuccess(); setAmount(undefined); @@ -257,7 +265,7 @@ export function PaytoWireTransferForm({ ? i18n.str`only "IBAN" target are supported` : !IBAN_REGEX.test(parsed.iban) ? i18n.str`IBAN should have just uppercased letters and numbers` - : undefined, + : validateIBAN(parsed.iban, i18n), }); return ( @@ -296,7 +304,8 @@ export function PaytoWireTransferForm({ <div style={{ fontSize: "small", marginTop: 4 }}> Hint: <code> - payto://iban/[receiver-iban]?message=[subject]&amount=[{currency} + payto://iban/[receiver-iban]?message=[subject]&amount=[ + {limit.currency} :X.Y] </code> </div> diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx index c1ad2f0cf..8bbfe0713 100644 --- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx @@ -14,7 +14,12 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Amounts, HttpStatusCode, Logger } from "@gnu-taler/taler-util"; +import { + AmountJson, + Amounts, + HttpStatusCode, + Logger, +} from "@gnu-taler/taler-util"; import { RequestError, useTranslationContext, @@ -30,11 +35,11 @@ const logger = new Logger("WalletWithdrawForm"); export function WalletWithdrawForm({ focus, - currency, + limit, onError, onSuccess, }: { - currency: string; + limit: AmountJson; focus?: boolean; onError: (e: PageStateType["error"]) => void; onSuccess: ( @@ -52,20 +57,20 @@ export function WalletWithdrawForm({ if (focus) ref.current?.focus(); }, [focus]); - // Beware: We never ever want to treat the amount as a float! - const trimmedAmountStr = amountStr?.trim(); const parsedAmount = trimmedAmountStr - ? Amounts.parse(`${currency}:${trimmedAmountStr}`) + ? Amounts.parse(`${limit.currency}:${trimmedAmountStr}`) : undefined; const errors = undefinedIfEmpty({ amount: trimmedAmountStr == null ? i18n.str`required` - : parsedAmount == null + : !parsedAmount ? i18n.str`invalid` + : Amounts.cmp(limit, parsedAmount) === -1 + ? i18n.str`balance is not enough` : undefined, }); return ( @@ -87,10 +92,10 @@ export function WalletWithdrawForm({ type="text" readonly class="currency-indicator" - size={currency.length} - maxLength={currency.length} + size={limit.currency.length} + maxLength={limit.currency.length} tabIndex={-1} - value={currency} + value={limit.currency} /> <input |