gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: fix: #7753


From: gnunet
Subject: [taler-wallet-core] branch master updated: fix: #7753
Date: Sat, 11 Mar 2023 22:23:13 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new c67d94c56 fix: #7753
c67d94c56 is described below

commit c67d94c56e154be4b2cf91572cdc2d8d2da7f8e4
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sat Mar 11 18:19:38 2023 -0300

    fix: #7753
---
 packages/demobank-ui/src/declaration.d.ts          |  8 ++-
 packages/demobank-ui/src/hooks/access.ts           | 17 +++++-
 packages/demobank-ui/src/hooks/circuit.ts          |  6 --
 packages/demobank-ui/src/index.tsx                 |  1 -
 packages/demobank-ui/src/pages/AccountPage.tsx     | 67 +++++++++-------------
 packages/demobank-ui/src/pages/BusinessAccount.tsx | 20 ++++---
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  7 ++-
 .../src/pages/PaytoWireTransferForm.tsx            | 35 ++++++-----
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   | 25 ++++----
 packages/web-util/src/components/utils.ts          | 47 +++++++++++++++
 10 files changed, 146 insertions(+), 87 deletions(-)

diff --git a/packages/demobank-ui/src/declaration.d.ts 
b/packages/demobank-ui/src/declaration.d.ts
index 8dc5fd8b2..4d8484a4f 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -169,6 +169,8 @@ namespace SandboxBackend {
       balance: Balance;
       // payto://-URI of the account. (New)
       paytoUri: string;
+      // Number indicating the max debit allowed for the requesting user.
+      debitThreshold: Amount;
     }
     interface BankAccountCreateWithdrawalRequest {
       // Amount to withdraw.
@@ -369,6 +371,9 @@ namespace SandboxBackend {
       // Contains ratios and fees related to buying
       // and selling the circuit currency.
       ratios_and_fees: RatiosAndFees;
+      // Fiat currency.  That is the currency in which
+      // cash-out operations ultimately wire money.
+      fiat_currency: string;
     }
     interface RatiosAndFees {
       // Exchange rate to buy the circuit currency from fiat.
@@ -379,9 +384,6 @@ namespace SandboxBackend {
       buy_in_fee: float;
       // Fee to subtract after applying the sell ratio.
       sell_out_fee: float;
-      // Fiat currency.  That is the currency in which
-      // cash-out operations ultimately wire money.
-      fiat_currency: string;
     }
     interface Cashouts {
       // Every string represents a cash-out operation UUID.
diff --git a/packages/demobank-ui/src/hooks/access.ts 
b/packages/demobank-ui/src/hooks/access.ts
index 8282210d4..750b95fa0 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -31,6 +31,7 @@ import {
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import _useSWR, { SWRHook } from "swr";
+import { Amounts } from "@gnu-taler/taler-util";
 const useSWR = _useSWR as unknown as SWRHook;
 
 export function useAccessAPI(): AccessAPI {
@@ -180,7 +181,21 @@ export function useAccountDetails(
     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;
   return { loading: true };
 }
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index 423ed1a5b..548862d85 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -299,12 +299,6 @@ export function useRatiosAndFeeConfig(): HttpResponse<
     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 (error) return error.info;
   return { loading: true };
diff --git a/packages/demobank-ui/src/index.tsx 
b/packages/demobank-ui/src/index.tsx
index a0ce8cb59..2e0f740fe 100644
--- a/packages/demobank-ui/src/index.tsx
+++ b/packages/demobank-ui/src/index.tsx
@@ -15,7 +15,6 @@
  */
 
 import App from "./components/app.js";
-
 import { h, render } from "preact";
 import "./scss/main.scss";
 
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>
-                  &nbsp;
-                  <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>
+              &nbsp;
+              <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}
               />
               &nbsp;
               <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}
           />
           &nbsp;
           <input
diff --git a/packages/web-util/src/components/utils.ts 
b/packages/web-util/src/components/utils.ts
index 71824e14f..34693f7d7 100644
--- a/packages/web-util/src/components/utils.ts
+++ b/packages/web-util/src/components/utils.ts
@@ -34,3 +34,50 @@ export function compose<SType extends { status: string }, 
PType>(
     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);
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]