fix: #7753
This commit is contained in:
parent
b72729f065
commit
c67d94c56e
8
packages/demobank-ui/src/declaration.d.ts
vendored
8
packages/demobank-ui/src/declaration.d.ts
vendored
@ -169,6 +169,8 @@ namespace SandboxBackend {
|
|||||||
balance: Balance;
|
balance: Balance;
|
||||||
// payto://-URI of the account. (New)
|
// payto://-URI of the account. (New)
|
||||||
paytoUri: string;
|
paytoUri: string;
|
||||||
|
// Number indicating the max debit allowed for the requesting user.
|
||||||
|
debitThreshold: Amount;
|
||||||
}
|
}
|
||||||
interface BankAccountCreateWithdrawalRequest {
|
interface BankAccountCreateWithdrawalRequest {
|
||||||
// Amount to withdraw.
|
// Amount to withdraw.
|
||||||
@ -369,6 +371,9 @@ namespace SandboxBackend {
|
|||||||
// Contains ratios and fees related to buying
|
// Contains ratios and fees related to buying
|
||||||
// and selling the circuit currency.
|
// and selling the circuit currency.
|
||||||
ratios_and_fees: RatiosAndFees;
|
ratios_and_fees: RatiosAndFees;
|
||||||
|
// Fiat currency. That is the currency in which
|
||||||
|
// cash-out operations ultimately wire money.
|
||||||
|
fiat_currency: string;
|
||||||
}
|
}
|
||||||
interface RatiosAndFees {
|
interface RatiosAndFees {
|
||||||
// Exchange rate to buy the circuit currency from fiat.
|
// Exchange rate to buy the circuit currency from fiat.
|
||||||
@ -379,9 +384,6 @@ namespace SandboxBackend {
|
|||||||
buy_in_fee: float;
|
buy_in_fee: float;
|
||||||
// Fee to subtract after applying the sell ratio.
|
// Fee to subtract after applying the sell ratio.
|
||||||
sell_out_fee: float;
|
sell_out_fee: float;
|
||||||
// Fiat currency. That is the currency in which
|
|
||||||
// cash-out operations ultimately wire money.
|
|
||||||
fiat_currency: string;
|
|
||||||
}
|
}
|
||||||
interface Cashouts {
|
interface Cashouts {
|
||||||
// Every string represents a cash-out operation UUID.
|
// Every string represents a cash-out operation UUID.
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
|
|
||||||
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
|
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
|
||||||
import _useSWR, { SWRHook } from "swr";
|
import _useSWR, { SWRHook } from "swr";
|
||||||
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
const useSWR = _useSWR as unknown as SWRHook;
|
const useSWR = _useSWR as unknown as SWRHook;
|
||||||
|
|
||||||
export function useAccessAPI(): AccessAPI {
|
export function useAccessAPI(): AccessAPI {
|
||||||
@ -180,7 +181,21 @@ export function useAccountDetails(
|
|||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data) return data;
|
//FIXME: remove optional when libeufin sandbox has implemented the feature
|
||||||
|
if (data && typeof data.data.debitThreshold === "undefined") {
|
||||||
|
data.data.debitThreshold = "100";
|
||||||
|
}
|
||||||
|
//FIXME: sandbox server should return amount string
|
||||||
|
if (data) {
|
||||||
|
const d = structuredClone(data);
|
||||||
|
const { currency } = Amounts.parseOrThrow(data.data.balance.amount);
|
||||||
|
d.data.debitThreshold = Amounts.stringify({
|
||||||
|
currency,
|
||||||
|
value: Number.parseInt(d.data.debitThreshold, 10),
|
||||||
|
fraction: 0,
|
||||||
|
});
|
||||||
|
return d;
|
||||||
|
}
|
||||||
if (error) return error.info;
|
if (error) return error.info;
|
||||||
return { loading: true };
|
return { loading: true };
|
||||||
}
|
}
|
||||||
|
@ -299,12 +299,6 @@ export function useRatiosAndFeeConfig(): HttpResponse<
|
|||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data) {
|
|
||||||
// data.data.ratios_and_fees.sell_out_fee = 2
|
|
||||||
if (!data.data.ratios_and_fees.fiat_currency) {
|
|
||||||
data.data.ratios_and_fees.fiat_currency = "FIAT";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data) return data;
|
if (data) return data;
|
||||||
if (error) return error.info;
|
if (error) return error.info;
|
||||||
return { loading: true };
|
return { loading: true };
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import App from "./components/app.js";
|
import App from "./components/app.js";
|
||||||
|
|
||||||
import { h, render } from "preact";
|
import { h, render } from "preact";
|
||||||
import "./scss/main.scss";
|
import "./scss/main.scss";
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@ import {
|
|||||||
useTranslationContext,
|
useTranslationContext,
|
||||||
} from "@gnu-taler/web-util/lib/index.browser";
|
} from "@gnu-taler/web-util/lib/index.browser";
|
||||||
import { Fragment, h, VNode } from "preact";
|
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 { Transactions } from "../components/Transactions/index.js";
|
||||||
import { useAccountDetails } from "../hooks/access.js";
|
import { useAccountDetails } from "../hooks/access.js";
|
||||||
import { PaymentOptions } from "./PaymentOptions.js";
|
import { PaymentOptions } from "./PaymentOptions.js";
|
||||||
@ -44,8 +42,8 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { data } = result;
|
const { data } = result;
|
||||||
const balance = Amounts.parse(data.balance.amount);
|
const balance = Amounts.parseOrThrow(data.balance.amount);
|
||||||
const errorParsingBalance = !balance;
|
const debitThreshold = Amounts.parseOrThrow(data.debitThreshold);
|
||||||
const payto = parsePaytoUri(data.paytoUri);
|
const payto = parsePaytoUri(data.paytoUri);
|
||||||
if (!payto || !payto.isKnown || payto.targetType !== "iban") {
|
if (!payto || !payto.isKnown || payto.targetType !== "iban") {
|
||||||
return (
|
return (
|
||||||
@ -54,7 +52,9 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode {
|
|||||||
}
|
}
|
||||||
const accountNumber = payto.iban;
|
const accountNumber = payto.iban;
|
||||||
const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
|
const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
|
||||||
|
const limit = balanceIsDebit
|
||||||
|
? Amounts.sub(debitThreshold, balance).amount
|
||||||
|
: Amounts.add(balance, debitThreshold).amount;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div>
|
<div>
|
||||||
@ -66,44 +66,29 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode {
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{errorParsingBalance ? (
|
<section id="assets">
|
||||||
<div class="informational informational-fail" style={{ marginTop: 8 }}>
|
<div class="asset-summary">
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<h2>{i18n.str`Bank account balance`}</h2>
|
||||||
<p>
|
{!balance ? (
|
||||||
<b>Server Error: invalid balance</b>
|
<div class="large-amount" style={{ color: "gray" }}>
|
||||||
</p>
|
Waiting server response...
|
||||||
</div>
|
</div>
|
||||||
<p>Your account is in an invalid state.</p>
|
) : (
|
||||||
|
<div class="large-amount amount">
|
||||||
|
{balanceIsDebit ? <b>-</b> : null}
|
||||||
|
<span class="value">{`${Amounts.stringifyValue(balance)}`}</span>
|
||||||
|
|
||||||
|
<span class="currency">{`${balance.currency}`}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</section>
|
||||||
<Fragment>
|
<section id="payments">
|
||||||
<section id="assets">
|
<div class="payments">
|
||||||
<div class="asset-summary">
|
<h2>{i18n.str`Payments`}</h2>
|
||||||
<h2>{i18n.str`Bank account balance`}</h2>
|
<PaymentOptions limit={limit} />
|
||||||
{!balance ? (
|
</div>
|
||||||
<div class="large-amount" style={{ color: "gray" }}>
|
</section>
|
||||||
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>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section id="payments">
|
|
||||||
<div class="payments">
|
|
||||||
<h2>{i18n.str`Payments`}</h2>
|
|
||||||
<PaymentOptions currency={balance.currency} />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<section style={{ marginTop: "2em" }}>
|
<section style={{ marginTop: "2em" }}>
|
||||||
<div class="active">
|
<div class="active">
|
||||||
|
@ -212,8 +212,7 @@ function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
|
|||||||
oldResult.ratios_and_fees.sell_at_ratio ||
|
oldResult.ratios_and_fees.sell_at_ratio ||
|
||||||
result.data.ratios_and_fees.sell_out_fee !==
|
result.data.ratios_and_fees.sell_out_fee !==
|
||||||
oldResult.ratios_and_fees.sell_out_fee ||
|
oldResult.ratios_and_fees.sell_out_fee ||
|
||||||
result.data.ratios_and_fees.fiat_currency !==
|
result.data.fiat_currency !== oldResult.fiat_currency);
|
||||||
oldResult.ratios_and_fees.fiat_currency);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
@ -238,16 +237,19 @@ function CreateCashout({
|
|||||||
if (!result.ok) return onLoadNotOk(result);
|
if (!result.ok) return onLoadNotOk(result);
|
||||||
if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
|
if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
|
||||||
const config = ratiosResult.data;
|
const config = ratiosResult.data;
|
||||||
const maybeBalance = Amounts.parse(result.data.balance.amount);
|
const balance = Amounts.parseOrThrow(result.data.balance.amount);
|
||||||
if (!maybeBalance) return <div>error</div>;
|
const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
|
||||||
const balance = maybeBalance;
|
|
||||||
const zero = Amounts.zeroOfCurrency(balance.currency);
|
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 sellRate = config.ratios_and_fees.sell_at_ratio;
|
||||||
const sellFee = !config.ratios_and_fees.sell_out_fee
|
const sellFee = !config.ratios_and_fees.sell_out_fee
|
||||||
? zero
|
? zero
|
||||||
: Amounts.fromFloat(config.ratios_and_fees.sell_out_fee, balance.currency);
|
: 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>;
|
if (!sellRate || sellRate < 0) return <div>error rate</div>;
|
||||||
|
|
||||||
@ -278,12 +280,12 @@ function CreateCashout({
|
|||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: !amount
|
: !amount
|
||||||
? i18n.str`could not be parsed`
|
? i18n.str`could not be parsed`
|
||||||
: Amounts.cmp(balance, amount_debit) === -1
|
: Amounts.cmp(limit, amount_debit) === -1
|
||||||
? i18n.str`balance is not enough`
|
? i18n.str`balance is not enough`
|
||||||
: Amounts.cmp(credit_before_fee, sellFee) === -1
|
: 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)
|
: Amounts.isZero(amount_credit)
|
||||||
? i18n.str`amount is not enough`
|
? i18n.str`the total transfer at destination will be zero`
|
||||||
: undefined,
|
: undefined,
|
||||||
channel: !form.channel ? i18n.str`required` : undefined,
|
channel: !form.channel ? i18n.str`required` : undefined,
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
import { h, VNode } from "preact";
|
import { h, VNode } from "preact";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
@ -25,7 +26,7 @@ import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
|
|||||||
* Let the user choose a payment option,
|
* Let the user choose a payment option,
|
||||||
* then specify the details trigger the action.
|
* 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 { i18n } = useTranslationContext();
|
||||||
const { pageStateSetter } = usePageContext();
|
const { pageStateSetter } = usePageContext();
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ export function PaymentOptions({ currency }: { currency: string }): VNode {
|
|||||||
<h3>{i18n.str`Obtain digital cash`}</h3>
|
<h3>{i18n.str`Obtain digital cash`}</h3>
|
||||||
<WalletWithdrawForm
|
<WalletWithdrawForm
|
||||||
focus
|
focus
|
||||||
currency={currency}
|
limit={limit}
|
||||||
onSuccess={(data) => {
|
onSuccess={(data) => {
|
||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
@ -80,7 +81,7 @@ export function PaymentOptions({ currency }: { currency: string }): VNode {
|
|||||||
<h3>{i18n.str`Transfer to bank account`}</h3>
|
<h3>{i18n.str`Transfer to bank account`}</h3>
|
||||||
<PaytoWireTransferForm
|
<PaytoWireTransferForm
|
||||||
focus
|
focus
|
||||||
currency={currency}
|
limit={limit}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
buildPayto,
|
buildPayto,
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
@ -30,7 +31,11 @@ import { h, VNode } from "preact";
|
|||||||
import { useEffect, useRef, useState } from "preact/hooks";
|
import { useEffect, useRef, useState } from "preact/hooks";
|
||||||
import { PageStateType } from "../context/pageState.js";
|
import { PageStateType } from "../context/pageState.js";
|
||||||
import { useAccessAPI } from "../hooks/access.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";
|
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
|
||||||
|
|
||||||
const logger = new Logger("PaytoWireTransferForm");
|
const logger = new Logger("PaytoWireTransferForm");
|
||||||
@ -39,12 +44,12 @@ export function PaytoWireTransferForm({
|
|||||||
focus,
|
focus,
|
||||||
onError,
|
onError,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
currency,
|
limit,
|
||||||
}: {
|
}: {
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
onError: (e: PageStateType["error"]) => void;
|
onError: (e: PageStateType["error"]) => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
currency: string;
|
limit: AmountJson;
|
||||||
}): VNode {
|
}): VNode {
|
||||||
// const backend = useBackendContext();
|
// const backend = useBackendContext();
|
||||||
// const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button?
|
// const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button?
|
||||||
@ -65,7 +70,8 @@ export function PaytoWireTransferForm({
|
|||||||
if (focus) ref.current?.focus();
|
if (focus) ref.current?.focus();
|
||||||
}, [focus, isRawPayto]);
|
}, [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 IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
|
||||||
|
|
||||||
const errorsWire = undefinedIfEmpty({
|
const errorsWire = undefinedIfEmpty({
|
||||||
@ -73,14 +79,16 @@ export function PaytoWireTransferForm({
|
|||||||
? i18n.str`Missing IBAN`
|
? i18n.str`Missing IBAN`
|
||||||
: !IBAN_REGEX.test(iban)
|
: !IBAN_REGEX.test(iban)
|
||||||
? i18n.str`IBAN should have just uppercased letters and numbers`
|
? i18n.str`IBAN should have just uppercased letters and numbers`
|
||||||
: undefined,
|
: validateIBAN(iban, i18n),
|
||||||
subject: !subject ? i18n.str`Missing subject` : undefined,
|
subject: !subject ? i18n.str`Missing subject` : undefined,
|
||||||
amount: !amount
|
amount: !trimmedAmountStr
|
||||||
? i18n.str`Missing amount`
|
? i18n.str`Missing amount`
|
||||||
: !(parsedAmount = Amounts.parse(`${currency}:${amount}`))
|
: !parsedAmount
|
||||||
? i18n.str`Amount is not valid`
|
? i18n.str`Amount is not valid`
|
||||||
: Amounts.isZero(parsedAmount)
|
: Amounts.isZero(parsedAmount)
|
||||||
? i18n.str`Should be greater than 0`
|
? i18n.str`Should be greater than 0`
|
||||||
|
: Amounts.cmp(limit, parsedAmount) === -1
|
||||||
|
? i18n.str`balance is not enough`
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -143,10 +151,10 @@ export function PaytoWireTransferForm({
|
|||||||
type="text"
|
type="text"
|
||||||
readonly
|
readonly
|
||||||
class="currency-indicator"
|
class="currency-indicator"
|
||||||
size={currency?.length}
|
size={limit.currency.length}
|
||||||
maxLength={currency?.length}
|
maxLength={limit.currency.length}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
value={currency}
|
value={limit.currency}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@ -185,7 +193,7 @@ export function PaytoWireTransferForm({
|
|||||||
try {
|
try {
|
||||||
await createTransaction({
|
await createTransaction({
|
||||||
paytoUri,
|
paytoUri,
|
||||||
amount: `${currency}:${amount}`,
|
amount: `${limit.currency}:${amount}`,
|
||||||
});
|
});
|
||||||
onSuccess();
|
onSuccess();
|
||||||
setAmount(undefined);
|
setAmount(undefined);
|
||||||
@ -257,7 +265,7 @@ export function PaytoWireTransferForm({
|
|||||||
? i18n.str`only "IBAN" target are supported`
|
? i18n.str`only "IBAN" target are supported`
|
||||||
: !IBAN_REGEX.test(parsed.iban)
|
: !IBAN_REGEX.test(parsed.iban)
|
||||||
? i18n.str`IBAN should have just uppercased letters and numbers`
|
? i18n.str`IBAN should have just uppercased letters and numbers`
|
||||||
: undefined,
|
: validateIBAN(parsed.iban, i18n),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -296,7 +304,8 @@ export function PaytoWireTransferForm({
|
|||||||
<div style={{ fontSize: "small", marginTop: 4 }}>
|
<div style={{ fontSize: "small", marginTop: 4 }}>
|
||||||
Hint:
|
Hint:
|
||||||
<code>
|
<code>
|
||||||
payto://iban/[receiver-iban]?message=[subject]&amount=[{currency}
|
payto://iban/[receiver-iban]?message=[subject]&amount=[
|
||||||
|
{limit.currency}
|
||||||
:X.Y]
|
:X.Y]
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,12 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
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 {
|
import {
|
||||||
RequestError,
|
RequestError,
|
||||||
useTranslationContext,
|
useTranslationContext,
|
||||||
@ -30,11 +35,11 @@ const logger = new Logger("WalletWithdrawForm");
|
|||||||
|
|
||||||
export function WalletWithdrawForm({
|
export function WalletWithdrawForm({
|
||||||
focus,
|
focus,
|
||||||
currency,
|
limit,
|
||||||
onError,
|
onError,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
currency: string;
|
limit: AmountJson;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
onError: (e: PageStateType["error"]) => void;
|
onError: (e: PageStateType["error"]) => void;
|
||||||
onSuccess: (
|
onSuccess: (
|
||||||
@ -52,20 +57,20 @@ export function WalletWithdrawForm({
|
|||||||
if (focus) ref.current?.focus();
|
if (focus) ref.current?.focus();
|
||||||
}, [focus]);
|
}, [focus]);
|
||||||
|
|
||||||
// Beware: We never ever want to treat the amount as a float!
|
|
||||||
|
|
||||||
const trimmedAmountStr = amountStr?.trim();
|
const trimmedAmountStr = amountStr?.trim();
|
||||||
|
|
||||||
const parsedAmount = trimmedAmountStr
|
const parsedAmount = trimmedAmountStr
|
||||||
? Amounts.parse(`${currency}:${trimmedAmountStr}`)
|
? Amounts.parse(`${limit.currency}:${trimmedAmountStr}`)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const errors = undefinedIfEmpty({
|
const errors = undefinedIfEmpty({
|
||||||
amount:
|
amount:
|
||||||
trimmedAmountStr == null
|
trimmedAmountStr == null
|
||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: parsedAmount == null
|
: !parsedAmount
|
||||||
? i18n.str`invalid`
|
? i18n.str`invalid`
|
||||||
|
: Amounts.cmp(limit, parsedAmount) === -1
|
||||||
|
? i18n.str`balance is not enough`
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
@ -87,10 +92,10 @@ export function WalletWithdrawForm({
|
|||||||
type="text"
|
type="text"
|
||||||
readonly
|
readonly
|
||||||
class="currency-indicator"
|
class="currency-indicator"
|
||||||
size={currency.length}
|
size={limit.currency.length}
|
||||||
maxLength={currency.length}
|
maxLength={limit.currency.length}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
value={currency}
|
value={limit.currency}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
@ -34,3 +34,50 @@ export function compose<SType extends { status: string }, PType>(
|
|||||||
return h();
|
return h();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param obj VNode
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function saveVNodeForInspection<T>(obj: T): T {
|
||||||
|
// @ts-ignore
|
||||||
|
window["showVNodeInfo"] = function showVNodeInfo() {
|
||||||
|
inspect(obj);
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
function inspect(obj: any) {
|
||||||
|
if (!obj) return;
|
||||||
|
if (obj.__c && obj.__c.__H) {
|
||||||
|
const componentName = obj.__c.constructor.name;
|
||||||
|
const hookState = obj.__c.__H;
|
||||||
|
const stateList = hookState.__ as Array<any>;
|
||||||
|
console.log("==============", componentName);
|
||||||
|
stateList.forEach((hook) => {
|
||||||
|
const { __: value, c: context, __h: factory, __H: args } = hook;
|
||||||
|
if (typeof context !== "undefined") {
|
||||||
|
const { __c: contextId } = context;
|
||||||
|
console.log("context:", contextId, hook);
|
||||||
|
} else if (typeof factory === "function") {
|
||||||
|
console.log("memo:", value, "deps:", args);
|
||||||
|
} else if (typeof value === "function") {
|
||||||
|
const effectName = value.name;
|
||||||
|
console.log("effect:", effectName, "deps:", args);
|
||||||
|
} else if (typeof value.current !== "undefined") {
|
||||||
|
const ref = value.current;
|
||||||
|
console.log("ref:", ref instanceof Element ? ref.outerHTML : ref);
|
||||||
|
} else if (value instanceof Array) {
|
||||||
|
console.log("state:", value[0]);
|
||||||
|
} else {
|
||||||
|
console.log(hook);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const children = obj.__k;
|
||||||
|
if (children instanceof Array) {
|
||||||
|
children.forEach((e) => inspect(e));
|
||||||
|
} else {
|
||||||
|
inspect(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user