gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: standard Amount field and add


From: gnunet
Subject: [taler-wallet-core] branch master updated: standard Amount field and add more validation (neg values)
Date: Mon, 07 Nov 2022 18:38:50 +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 6f3cd1634 standard Amount field and add more validation (neg values)
6f3cd1634 is described below

commit 6f3cd163431fecfa126f740ebfec1b5c5c74f5b7
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Nov 7 14:38:42 2022 -0300

    standard Amount field and add more validation (neg values)
---
 .../src/components/AmountField.tsx                 | 67 ++++++++++++++++++++++
 .../src/mui/TextField.tsx                          |  2 +
 .../taler-wallet-webextension/src/mui/handlers.ts  |  2 +-
 .../src/mui/input/InputBase.tsx                    | 30 +++++++++-
 .../src/wallet/CreateManualWithdraw.test.ts        |  4 ++
 .../src/wallet/CreateManualWithdraw.tsx            |  2 +-
 .../src/wallet/DepositPage/state.ts                | 11 +++-
 .../src/wallet/DepositPage/test.ts                 | 36 ++++++++++++
 .../src/wallet/DepositPage/views.tsx               | 65 +++++++++------------
 .../src/wallet/DestinationSelection.tsx            | 48 +++++-----------
 .../src/wallet/ManageAccount/views.tsx             | 19 ++++--
 11 files changed, 203 insertions(+), 83 deletions(-)

diff --git a/packages/taler-wallet-webextension/src/components/AmountField.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.tsx
new file mode 100644
index 000000000..79c510d2f
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/AmountField.tsx
@@ -0,0 +1,67 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { Fragment, h, VNode } from "preact";
+import { TextFieldHandler } from "../mui/handlers.js";
+import { TextField } from "../mui/TextField.js";
+import { ErrorText } from "./styled/index.js";
+
+export function AmountField({
+  label,
+  handler,
+  currency,
+  required,
+}: {
+  label: VNode;
+  required?: boolean;
+  currency: string;
+  handler: TextFieldHandler;
+}): VNode {
+  function positiveAmount(value: string): string {
+    if (!value) return "";
+    try {
+      const num = Number.parseFloat(value);
+      if (Number.isNaN(num) || num < 0) return handler.value;
+      if (handler.onInput) {
+        handler.onInput(value);
+      }
+      return value;
+    } catch (e) {
+      // do nothing
+    }
+    return handler.value;
+  }
+  return (
+    <Fragment>
+      <TextField
+        label={label}
+        type="number"
+        min="0"
+        step="0.1"
+        variant="filled"
+        error={!!handler.error}
+        required={required}
+        startAdornment={
+          <div style={{ padding: "25px 12px 8px 12px" }}>{currency}</div>
+        }
+        value={handler.value}
+        disabled={!handler.onInput}
+        onInput={positiveAmount}
+      />
+      {handler.error && <ErrorText>{handler.error}</ErrorText>}
+    </Fragment>
+  );
+}
diff --git a/packages/taler-wallet-webextension/src/mui/TextField.tsx 
b/packages/taler-wallet-webextension/src/mui/TextField.tsx
index 1c1f5cc49..7c6eb40a2 100644
--- a/packages/taler-wallet-webextension/src/mui/TextField.tsx
+++ b/packages/taler-wallet-webextension/src/mui/TextField.tsx
@@ -40,7 +40,9 @@ export interface Props {
   minRows?: number;
   multiline?: boolean;
   onChange?: (s: string) => void;
+  onInput?: (s: string) => string;
   min?: string;
+  step?: string;
   placeholder?: string;
   required?: boolean;
 
diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts 
b/packages/taler-wallet-webextension/src/mui/handlers.ts
index aa66e75a8..9d393e5b7 100644
--- a/packages/taler-wallet-webextension/src/mui/handlers.ts
+++ b/packages/taler-wallet-webextension/src/mui/handlers.ts
@@ -16,7 +16,7 @@
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 
 export interface TextFieldHandler {
-  onInput: (value: string) => Promise<void>;
+  onInput?: (value: string) => Promise<void>;
   value: string;
   error?: string;
 }
diff --git a/packages/taler-wallet-webextension/src/mui/input/InputBase.tsx 
b/packages/taler-wallet-webextension/src/mui/input/InputBase.tsx
index 80f5bd44e..e1c6e7af1 100644
--- a/packages/taler-wallet-webextension/src/mui/input/InputBase.tsx
+++ b/packages/taler-wallet-webextension/src/mui/input/InputBase.tsx
@@ -189,6 +189,7 @@ export function InputBase({
   Root = InputBaseRoot,
   Input,
   onChange,
+  onInput,
   name,
   placeholder,
   readOnly,
@@ -254,6 +255,19 @@ export function InputBase({
     }
   };
 
+  const handleInput = (
+    event: JSX.TargetedEvent<HTMLElement & { value?: string }>,
+  ): void => {
+    // if (inputPropsProp.onChange) {
+    //   inputPropsProp.onChange(event, ...args);
+    // }
+
+    // Perform in the willUpdate
+    if (onInput) {
+      event.currentTarget.value = onInput(event.currentTarget.value);
+    }
+  };
+
   const handleClick = (
     event: JSX.TargetedMouseEvent<HTMLElement & { value?: string }>,
   ): void => {
@@ -290,6 +304,7 @@ export function InputBase({
           onKeyDown={onKeyDown}
           onKeyUp={onKeyUp}
           type={type}
+          onInput={handleInput}
           onChange={handleChange}
           onBlur={handleBlur}
           onFocus={handleFocus}
@@ -345,6 +360,7 @@ export function TextareaAutoSize({
   // disabled,
   // size,
   onChange,
+  onInput,
   value,
   multiline,
   focused,
@@ -480,7 +496,18 @@ export function TextareaAutoSize({
     }
 
     if (onChange) {
-      onChange(event);
+      onChange(event.target.value);
+    }
+  };
+  const handleInput = (event: any): void => {
+    renders.current = 0;
+
+    if (!isControlled) {
+      syncHeight();
+    }
+
+    if (onInput) {
+      event.currentTarget.value = onInput(event.currentTarget.value);
     }
   };
 
@@ -498,6 +525,7 @@ export function TextareaAutoSize({
         ].join(" ")}
         value={value}
         onChange={handleChange}
+        onInput={handleInput}
         ref={inputRef}
         // Apply the rows prop to get a "correct" first SSR paint
         rows={minRows}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts
index c757610fc..37c50285b 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts
@@ -129,6 +129,8 @@ describe("CreateManualWithdraw states", () => {
 
       expect(parsedAmount).equal(undefined);
 
+      expect(amount.onInput).not.undefined;
+      if (!amount.onInput) return;
       amount.onInput("12");
     }
 
@@ -188,6 +190,8 @@ async function defaultTestForInputText(
     const field = getField();
     const initialValue = field.value;
     nextValue = `${initialValue} something else`;
+    expect(field.onInput).not.undefined;
+    if (!field.onInput) return;
     field.onInput(nextValue);
   }
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
index 5320c6fe2..dd80faccd 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
@@ -260,7 +260,7 @@ export function CreateManualWithdraw({
                 <input
                   type="number"
                   value={state.amount.value}
-                  onInput={(e) => state.amount.onInput(e.currentTarget.value)}
+                  // onInput={(e) => 
state.amount.onInput(e.currentTarget.value)}
                 />
               </div>
             </InputWithLabel>
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index 2693db79e..d8b752d44 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -49,7 +49,6 @@ export function useComponentState(
     parsed !== undefined ? Amounts.stringifyValue(parsed) : "0";
   // const [accountIdx, setAccountIdx] = useState<number>(0);
   const [amount, setAmount] = useState(initialValue);
-
   const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
 
   const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
@@ -124,6 +123,16 @@ export function useComponentState(
   const firstAccount = accounts[0].uri
   const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
 
+  if (fee === undefined && parsedAmount) {
+    getFeeForAmount(currentAccount, parsedAmount, api).then(initialFee => {
+      setFee(initialFee)
+    })
+    return {
+      status: "loading",
+      error: undefined,
+    };
+  }
+
   const accountMap = createLabelsForBankAccount(accounts);
 
   async function updateAccountFromList(accountStr: string): Promise<void> {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 9f336ac1a..17e17d185 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -167,6 +167,11 @@ describe("DepositPage states", () => {
         accounts: [ibanPayto],
       },
     );
+    handler.addWalletCallResponse(
+      WalletApiOperation.GetFeeForDeposit,
+      undefined,
+      withoutFee(),
+    );
 
     const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
       mountHook(() => useComponentState(props, mock));
@@ -176,6 +181,11 @@ describe("DepositPage states", () => {
       expect(status).equal("loading");
     }
 
+    expect(await waitForStateUpdate()).true;
+    {
+      const { status } = pullLastResultOrThrow();
+      expect(status).equal("loading");
+    }
     expect(await waitForStateUpdate()).true;
 
     {
@@ -214,6 +224,12 @@ describe("DepositPage states", () => {
         accounts: [talerBankPayto, ibanPayto],
       },
     );
+    handler.addWalletCallResponse(
+      WalletApiOperation.GetFeeForDeposit,
+      undefined,
+      withoutFee(),
+    );
+
     handler.addWalletCallResponse(
       WalletApiOperation.GetFeeForDeposit,
       undefined,
@@ -238,6 +254,12 @@ describe("DepositPage states", () => {
       expect(status).equal("loading");
     }
 
+    expect(await waitForStateUpdate()).true;
+    {
+      const { status } = pullLastResultOrThrow();
+      expect(status).equal("loading");
+    }
+
     expect(await waitForStateUpdate()).true;
     const accountSelected = stringifyPaytoUri(ibanPayto.uri);
 
@@ -361,6 +383,11 @@ describe("DepositPage states", () => {
         accounts: [talerBankPayto, ibanPayto],
       },
     );
+    handler.addWalletCallResponse(
+      WalletApiOperation.GetFeeForDeposit,
+      undefined,
+      withoutFee(),
+    );
     handler.addWalletCallResponse(
       WalletApiOperation.GetFeeForDeposit,
       undefined,
@@ -380,6 +407,13 @@ describe("DepositPage states", () => {
       expect(status).equal("loading");
     }
 
+    expect(await waitForStateUpdate()).true;
+
+    {
+      const { status } = pullLastResultOrThrow();
+      expect(status).equal("loading");
+    }
+
     expect(await waitForStateUpdate()).true;
     const accountSelected = stringifyPaytoUri(ibanPayto.uri);
 
@@ -409,6 +443,8 @@ describe("DepositPage states", () => {
       expect(r.depositHandler.onClick).undefined;
       expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
 
+      expect(r.amount.onInput).not.undefined;
+      if (!r.amount.onInput) return;
       r.amount.onInput("10");
     }
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
index e864c8413..771db828d 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
@@ -16,6 +16,7 @@
 
 import { Amounts, PaytoUri } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
+import { AmountField } from "../../components/AmountField.js";
 import { ErrorMessage } from "../../components/ErrorMessage.js";
 import { LoadingError } from "../../components/LoadingError.js";
 import { SelectList } from "../../components/SelectList.js";
@@ -28,6 +29,7 @@ import {
 } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
+import { Grid } from "../../mui/Grid.js";
 import { State } from "./index.js";
 
 export function LoadingErrorView({ error }: State.LoadingUriError): VNode {
@@ -167,48 +169,33 @@ export function ReadyView(state: State.Ready): VNode {
         <p>
           <AccountDetails account={state.currentAccount} />
         </p>
-        <InputWithLabel invalid={!!state.amount.error}>
-          <label>
-            <i18n.Translate>Amount</i18n.Translate>
-          </label>
-          <div>
-            <span>{state.currency}</span>
-            <input
-              type="number"
-              value={state.amount.value}
-              onInput={(e) => state.amount.onInput(e.currentTarget.value)}
+        <Grid container spacing={2} columns={1}>
+          <Grid item xs={1}>
+            <AmountField
+              label={<i18n.Translate>Amount</i18n.Translate>}
+              currency={state.currency}
+              handler={state.amount}
             />
-          </div>
-          {state.amount.error && <ErrorText>{state.amount.error}</ErrorText>}
-        </InputWithLabel>
-
-        <InputWithLabel>
-          <label>
-            <i18n.Translate>Deposit fee</i18n.Translate>
-          </label>
-          <div>
-            <span>{state.currency}</span>
-            <input
-              type="number"
-              disabled
-              value={Amounts.stringifyValue(state.totalFee)}
+          </Grid>
+          <Grid item xs={1}>
+            <AmountField
+              label={<i18n.Translate>Deposit fee</i18n.Translate>}
+              currency={state.currency}
+              handler={{
+                value: Amounts.stringifyValue(state.totalFee),
+              }}
             />
-          </div>
-        </InputWithLabel>
-
-        <InputWithLabel>
-          <label>
-            <i18n.Translate>Total deposit</i18n.Translate>
-          </label>
-          <div>
-            <span>{state.currency}</span>
-            <input
-              type="number"
-              disabled
-              value={Amounts.stringifyValue(state.totalToDeposit)}
+          </Grid>
+          <Grid item xs={1}>
+            <AmountField
+              label={<i18n.Translate>Total deposit</i18n.Translate>}
+              currency={state.currency}
+              handler={{
+                value: Amounts.stringifyValue(state.totalToDeposit),
+              }}
             />
-          </div>
-        </InputWithLabel>
+          </Grid>
+        </Grid>
       </section>
       <footer>
         <Button
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
index c584f2aae..ba1a560ef 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
@@ -19,6 +19,7 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
+import { AmountField } from "../components/AmountField.js";
 import { Loading } from "../components/Loading.js";
 import { LoadingError } from "../components/LoadingError.js";
 import { SelectList } from "../components/SelectList.js";
@@ -32,6 +33,7 @@ import { useTranslationContext } from 
"../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { Grid } from "../mui/Grid.js";
+import { TextFieldHandler } from "../mui/handlers.js";
 import { Paper } from "../mui/Paper.js";
 import { TextField } from "../mui/TextField.js";
 import { Pages } from "../NavigationBar.js";
@@ -283,11 +285,6 @@ export function DestinationSelectionGetCash({
   const [currency, setCurrency] = useState(parsedInitialAmount?.currency);
 
   const [amount, setAmount] = useState(parsedInitialAmountValue);
-  function positiveSetAmount(e: string):void {
-    const value = Number.parseInt(e, 10);
-    if (value < 0) return
-    setAmount(String(value))
-  }
   const { i18n } = useTranslationContext();
   const previous1: Contact[] = [];
   const previous2: Contact[] = [
@@ -326,19 +323,13 @@ export function DestinationSelectionGetCash({
         <i18n.Translate>Specify the amount and the origin</i18n.Translate>
       </h1>
       <Grid container columns={2} justifyContent="space-between">
-        <TextField
-          label="Amount"
-          type="number"
-          min="0"
-          variant="filled"
-          error={invalid}
+        <AmountField
+          label={<i18n.Translate>Amount</i18n.Translate>}
+          currency={currency}
           required
-          startAdornment={
-            <div style={{ padding: "25px 12px 8px 12px" }}>{currency}</div>
-          }
-          value={amount}
-          onChange={(e) => {
-            setAmount(e);
+          handler={{
+            onInput: async (s) => setAmount(s),
+            value: amount,
           }}
         />
         <Button onClick={async () => setCurrency(undefined)}>
@@ -431,11 +422,6 @@ export function DestinationSelectionSendCash({
   const currency = parsedInitialAmount?.currency;
 
   const [amount, setAmount] = useState(parsedInitialAmountValue);
-  function positiveSetAmount(e: string):void {
-    const value = Number.parseInt(e, 10);
-    if (value < 0) return
-    setAmount(String(value))
-  }
   const { i18n } = useTranslationContext();
   const previous1: Contact[] = [];
   const previous2: Contact[] = [
@@ -474,19 +460,13 @@ export function DestinationSelectionSendCash({
       </h1>
 
       <div>
-        <TextField
-          label="Amount"
-          type="number"
-          min="0"
-          variant="filled"
+        <AmountField
+          label={<i18n.Translate>Amount</i18n.Translate>}
+          currency={currency}
           required
-          error={invalid}
-          startAdornment={
-            <div style={{ padding: "25px 12px 8px 12px" }}>{currency}</div>
-          }
-          value={amount}
-          onChange={(e) => {
-            positiveSetAmount(e);
+          handler={{
+            onInput: async (s) => setAmount(s),
+            value: amount,
           }}
         />
       </div>
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
index 832ca91b7..326e078f4 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
@@ -416,9 +416,10 @@ function BitcoinAddressAccount({ field }: { field: 
TextFieldHandler }): VNode {
         fullWidth
         value={value}
         error={value !== undefined && !!errors?.value}
+        disabled={!field.onInput}
         onChange={(v) => {
           setValue(v);
-          if (!errors) {
+          if (!errors && field.onInput) {
             field.onInput(`payto://bitcoin/${v}`);
           }
         }}
@@ -456,9 +457,10 @@ function TalerBankAddressAccount({
         fullWidth
         value={host}
         error={host !== undefined && !!errors?.host}
+        disabled={!field.onInput}
         onChange={(v) => {
           setHost(v);
-          if (!errors) {
+          if (!errors && field.onInput) {
             field.onInput(`payto://x-taler-bank/${v}/${account}`);
           }
         }}
@@ -470,11 +472,12 @@ function TalerBankAddressAccount({
         label="Bank account"
         variant="standard"
         fullWidth
+        disabled={!field.onInput}
         value={account}
         error={account !== undefined && !!errors?.account}
         onChange={(v) => {
           setAccount(v || "");
-          if (!errors) {
+          if (!errors && field.onInput) {
             field.onInput(`payto://x-taler-bank/${host}/${v}`);
           }
         }}
@@ -502,9 +505,10 @@ function IbanAddressAccount({ field }: { field: 
TextFieldHandler }): VNode {
         fullWidth
         value={number}
         error={number !== undefined && !!errors?.number}
+        disabled={!field.onInput}
         onChange={(v) => {
           setNumber(v);
-          if (!errors) {
+          if (!errors && field.onInput) {
             field.onInput(`payto://iban/${v}?receiver-name=${name}`);
           }
         }}
@@ -518,10 +522,13 @@ function IbanAddressAccount({ field }: { field: 
TextFieldHandler }): VNode {
         fullWidth
         value={name}
         error={name !== undefined && !!errors?.name}
+        disabled={!field.onInput}
         onChange={(v) => {
           setName(v);
-          if (!errors) {
-            
field.onInput(`payto://iban/${number}?receiver-name=${encodeURIComponent(v)}`);
+          if (!errors && field.onInput) {
+            field.onInput(
+              `payto://iban/${number}?receiver-name=${encodeURIComponent(v)}`,
+            );
           }
         }}
       />

-- 
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]