gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: amount field


From: gnunet
Subject: [taler-wallet-core] branch master updated: amount field
Date: Tue, 22 Nov 2022 19:43:48 +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 88618df7b amount field
88618df7b is described below

commit 88618df7b870732f4f29a80686dd4f4cf20887f8
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Nov 22 15:43:39 2022 -0300

    amount field
---
 .../src/components/AmountField.stories.tsx         |  65 +++++
 .../src/components/AmountField.tsx                 | 185 ++++++++++++--
 .../src/components/TransactionItem.tsx             |   6 +-
 .../src/components/index.stories.tsx               |   3 +-
 .../src/mui/TextField.tsx                          |   1 +
 .../taler-wallet-webextension/src/mui/handlers.ts  |   7 +
 .../src/wallet/CreateManualWithdraw.stories.tsx    |  58 -----
 .../src/wallet/CreateManualWithdraw.test.ts        | 232 -----------------
 .../src/wallet/CreateManualWithdraw.tsx            | 282 ---------------------
 .../src/wallet/DepositPage/index.ts                |   3 +-
 .../src/wallet/DepositPage/state.ts                |  55 ++--
 .../src/wallet/DepositPage/stories.tsx             |   6 +-
 .../src/wallet/DepositPage/test.ts                 |  14 +-
 .../src/wallet/DepositPage/views.tsx               |   7 +-
 .../src/wallet/DestinationSelection.tsx            |  33 +--
 .../src/wallet/ExchangeSelection/state.ts          |  13 +-
 .../src/wallet/ManualWithdrawPage.tsx              | 141 -----------
 .../src/wallet/index.stories.tsx                   |   2 -
 18 files changed, 298 insertions(+), 815 deletions(-)

diff --git 
a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
new file mode 100644
index 000000000..3183364a8
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
@@ -0,0 +1,65 @@
+/*
+ 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { useTranslationContext } from "../context/translation.js";
+import { Grid } from "../mui/Grid.js";
+import { AmountFieldHandler, TextFieldHandler } from "../mui/handlers.js";
+import { AmountField } from "./AmountField.js";
+
+export default {
+  title: "components/amountField",
+};
+
+function RenderAmount(): VNode {
+  const [value, setValue] = useState<AmountJson | undefined>(undefined);
+
+  const error = value === undefined ? undefined : undefined;
+
+  const handler: AmountFieldHandler = {
+    value: value ?? Amounts.zeroOfCurrency("USD"),
+    onInput: async (e) => {
+      setValue(e);
+    },
+    error,
+  };
+  const { i18n } = useTranslationContext();
+  return (
+    <Fragment>
+      <AmountField
+        required
+        label={<i18n.Translate>Amount</i18n.Translate>}
+        currency="USD"
+        highestDenom={2000000}
+        lowestDenom={0.01}
+        handler={handler}
+      />
+      <p>
+        <pre>{JSON.stringify(value, undefined, 2)}</pre>
+      </p>
+    </Fragment>
+  );
+}
+
+export const AmountFieldExample = (): VNode => RenderAmount();
diff --git a/packages/taler-wallet-webextension/src/components/AmountField.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.tsx
index 1c57be0df..6081e70ff 100644
--- a/packages/taler-wallet-webextension/src/components/AmountField.tsx
+++ b/packages/taler-wallet-webextension/src/components/AmountField.tsx
@@ -14,51 +14,182 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import {
+  amountFractionalBase,
+  amountFractionalLength,
+  AmountJson,
+  amountMaxValue,
+  Amounts,
+  Result,
+} from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
-import { TextFieldHandler } from "../mui/handlers.js";
+import { useState } from "preact/hooks";
+import { AmountFieldHandler } from "../mui/handlers.js";
 import { TextField } from "../mui/TextField.js";
-import { ErrorText } from "./styled/index.js";
+
+const HIGH_DENOM_SYMBOL = ["", "K", "M", "G", "T", "P"];
+const LOW_DENOM_SYMBOL = ["", "m", "mm", "n", "p", "f"];
 
 export function AmountField({
   label,
   handler,
-  currency,
+  lowestDenom = 1,
+  highestDenom = 1,
   required,
 }: {
   label: VNode;
+  lowestDenom?: number;
+  highestDenom?: number;
   required?: boolean;
-  currency: string;
-  handler: TextFieldHandler;
+  handler: AmountFieldHandler;
 }): VNode {
+  const [unit, setUnit] = useState(1);
+  const [dotAtTheEnd, setDotAtTheEnd] = useState(false);
+  const currency = handler.value.currency;
+
+  let hd = Math.floor(Math.log10(highestDenom || 1) / 3);
+  let ld = Math.ceil((-1 * Math.log10(lowestDenom || 1)) / 3);
+
+  const currencyLabels: Array<{ name: string; unit: number }> = [
+    {
+      name: currency,
+      unit: 1,
+    },
+  ];
+
+  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--;
+  }
+
+  const prev = Amounts.stringifyValue(handler.value);
+
   function positiveAmount(value: string): string {
-    if (!value) return "";
-    try {
-      const num = Number.parseFloat(value);
-      if (Number.isNaN(num) || num < 0) return handler.value;
+    setDotAtTheEnd(value.endsWith("."));
+    if (!value) {
       if (handler.onInput) {
-        handler.onInput(value);
+        handler.onInput(Amounts.zeroOfCurrency(currency));
       }
-      return value;
+      return "";
+    }
+    try {
+      //remove all but last dot
+      const parsed = value.replace(/(\.)(?=.*\1)/g, "");
+      const real = parseValue(currency, parsed);
+
+      if (!real || real.value < 0) {
+        return prev;
+      }
+
+      const normal = normalize(real, unit);
+
+      console.log(real, unit, normal);
+      if (normal && handler.onInput) {
+        handler.onInput(normal);
+      }
+      return parsed;
     } catch (e) {
       // do nothing
     }
-    return handler.value;
+    return prev;
   }
+
+  const normal = denormalize(handler.value, unit) ?? handler.value;
+
+  const textValue = Amounts.stringifyValue(normal) + (dotAtTheEnd ? "." : "");
   return (
-    <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}
-    />
+    <Fragment>
+      <TextField
+        label={label}
+        type="text"
+        min="0"
+        inputmode="decimal"
+        step="0.1"
+        variant="filled"
+        error={handler.error}
+        required={required}
+        startAdornment={
+          currencyLabels.length === 1 ? (
+            <div
+              style={{
+                marginTop: 20,
+                padding: "5px 12px 8px 12px",
+              }}
+            >
+              {currency}
+            </div>
+          ) : (
+            <select
+              disabled={!handler.onInput}
+              onChange={(e) => {
+                const unit = Number.parseFloat(e.currentTarget.value);
+                setUnit(unit);
+              }}
+              value={String(unit)}
+              style={{
+                marginTop: 20,
+                padding: "5px 12px 8px 12px",
+                background: "transparent",
+                border: 0,
+              }}
+            >
+              {currencyLabels.map((c) => (
+                <option key={c} value={c.unit}>
+                  <div>{c.name}</div>
+                </option>
+              ))}
+            </select>
+          )
+        }
+        value={textValue}
+        disabled={!handler.onInput}
+        onInput={positiveAmount}
+      />
+    </Fragment>
   );
 }
+
+function parseValue(currency: string, s: string): AmountJson | undefined {
+  const [intPart, fractPart] = s.split(".");
+  const tail = "." + (fractPart || "0");
+  if (tail.length > amountFractionalLength + 1) {
+    return undefined;
+  }
+  const value = Number.parseInt(intPart, 10);
+  if (Number.isNaN(value) || value > amountMaxValue) {
+    return undefined;
+  }
+  return {
+    currency,
+    fraction: Math.round(amountFractionalBase * Number.parseFloat(tail)),
+    value,
+  };
+}
+
+function normalize(amount: AmountJson, unit: number): AmountJson | undefined {
+  if (unit === 1 || Amounts.isZero(amount)) return amount;
+  const result =
+    unit < 1
+      ? Amounts.divide(amount, 1 / unit)
+      : Amounts.mult(amount, unit).amount;
+  return result;
+}
+
+function denormalize(amount: AmountJson, unit: number): AmountJson | undefined 
{
+  if (unit === 1 || Amounts.isZero(amount)) return amount;
+  const result =
+    unit < 1
+      ? Amounts.mult(amount, 1 / unit).amount
+      : Amounts.divide(amount, unit);
+  return result;
+}
diff --git 
a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx 
b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
index e5ce4140f..f8b23081d 100644
--- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
@@ -57,9 +57,9 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
                 ? !tx.withdrawalDetails.confirmed
                   ? i18n.str`Need approval in the Bank`
                   : i18n.str`Exchange is waiting the wire transfer`
-                : undefined
-              : tx.withdrawalDetails.type === WithdrawalType.ManualTransfer
-              ? i18n.str`Exchange is waiting the wire transfer`
+                : tx.withdrawalDetails.type === WithdrawalType.ManualTransfer
+                ? i18n.str`Exchange is waiting the wire transfer`
+                : "" //pending but no message
               : undefined
           }
         />
diff --git 
a/packages/taler-wallet-webextension/src/components/index.stories.tsx 
b/packages/taler-wallet-webextension/src/components/index.stories.tsx
index d71adf689..2e4e7fa2e 100644
--- a/packages/taler-wallet-webextension/src/components/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/components/index.stories.tsx
@@ -25,5 +25,6 @@ import * as a3 from "./Amount.stories.js";
 import * as a4 from "./ShowFullContractTermPopup.stories.js";
 import * as a5 from "./TermsOfService/stories.js";
 import * as a6 from "./QR.stories";
+import * as a7 from "./AmountField.stories.js";
 
-export default [a1, a2, a3, a4, a5, a6];
+export default [a1, a2, a3, a4, a5, a6, a7];
diff --git a/packages/taler-wallet-webextension/src/mui/TextField.tsx 
b/packages/taler-wallet-webextension/src/mui/TextField.tsx
index ba05158fa..42ac49a00 100644
--- a/packages/taler-wallet-webextension/src/mui/TextField.tsx
+++ b/packages/taler-wallet-webextension/src/mui/TextField.tsx
@@ -41,6 +41,7 @@ export interface Props {
   multiline?: boolean;
   onChange?: (s: string) => void;
   onInput?: (s: string) => string;
+  inputmode?: string;
   min?: string;
   step?: string;
   placeholder?: string;
diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts 
b/packages/taler-wallet-webextension/src/mui/handlers.ts
index 9d393e5b7..655fceef9 100644
--- a/packages/taler-wallet-webextension/src/mui/handlers.ts
+++ b/packages/taler-wallet-webextension/src/mui/handlers.ts
@@ -13,6 +13,7 @@
  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 { AmountJson } from "@gnu-taler/taler-util";
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 
 export interface TextFieldHandler {
@@ -21,6 +22,12 @@ export interface TextFieldHandler {
   error?: string;
 }
 
+export interface AmountFieldHandler {
+  onInput?: (value: AmountJson) => Promise<void>;
+  value: AmountJson;
+  error?: string;
+}
+
 export interface ButtonHandler {
   onClick?: () => Promise<void>;
   error?: TalerError;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
deleted file mode 100644
index 2154d35de..000000000
--- 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- 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/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { createExample } from "../test-utils.js";
-import { CreateManualWithdraw as TestedComponent } from 
"./CreateManualWithdraw.js";
-
-export default {
-  title: "wallet/manual withdraw/creation",
-  component: TestedComponent,
-  argTypes: {},
-};
-
-// ,
-const exchangeUrlWithCurrency = {
-  "http://exchange.taler:8081": "COL",
-  "http://exchange.tal": "EUR",
-};
-
-export const WithoutAnyExchangeKnown = createExample(TestedComponent, {
-  exchangeUrlWithCurrency: {},
-});
-
-export const InitialState = createExample(TestedComponent, {
-  exchangeUrlWithCurrency,
-});
-
-export const WithAmountInitialized = createExample(TestedComponent, {
-  initialAmount: "10",
-  exchangeUrlWithCurrency,
-});
-
-export const WithExchangeError = createExample(TestedComponent, {
-  error: "The exchange url seems invalid",
-  exchangeUrlWithCurrency,
-});
-
-export const WithAmountError = createExample(TestedComponent, {
-  initialAmount: "e",
-  exchangeUrlWithCurrency,
-});
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts
deleted file mode 100644
index 37c50285b..000000000
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- 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/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { expect } from "chai";
-import { SelectFieldHandler, TextFieldHandler } from "../mui/handlers.js";
-import { mountHook } from "../test-utils.js";
-import { useComponentState } from "./CreateManualWithdraw.js";
-
-const exchangeListWithARSandUSD = {
-  url1: "USD",
-  url2: "ARS",
-  url3: "ARS",
-};
-
-const exchangeListEmpty = {};
-
-describe("CreateManualWithdraw states", () => {
-  it("should set noExchangeFound when exchange list is empty", () => {
-    const { pullLastResultOrThrow } = mountHook(() =>
-      useComponentState(exchangeListEmpty, undefined, undefined),
-    );
-
-    const { noExchangeFound } = pullLastResultOrThrow();
-
-    expect(noExchangeFound).equal(true);
-  });
-
-  it("should set noExchangeFound when exchange list doesn't include selected 
currency", () => {
-    const { pullLastResultOrThrow } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "COL"),
-    );
-
-    const { noExchangeFound } = pullLastResultOrThrow();
-
-    expect(noExchangeFound).equal(true);
-  });
-
-  it("should select the first exchange from the list", () => {
-    const { pullLastResultOrThrow } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, undefined),
-    );
-
-    const { exchange } = pullLastResultOrThrow();
-
-    expect(exchange.value).equal("url1");
-  });
-
-  it("should select the first exchange with the selected currency", () => {
-    const { pullLastResultOrThrow } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    const { exchange } = pullLastResultOrThrow();
-
-    expect(exchange.value).equal("url2");
-  });
-
-  it("should change the exchange when currency change", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    {
-      const { exchange, currency } = pullLastResultOrThrow();
-
-      expect(exchange.value).equal("url2");
-      if (currency.onChange === undefined) expect.fail();
-      currency.onChange("USD");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { exchange } = pullLastResultOrThrow();
-      expect(exchange.value).equal("url1");
-    }
-  });
-
-  it("should change the currency when exchange change", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    {
-      const { exchange, currency } = pullLastResultOrThrow();
-
-      expect(exchange.value).equal("url2");
-      expect(currency.value).equal("ARS");
-
-      if (exchange.onChange === undefined) expect.fail();
-      exchange.onChange("url1");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { exchange, currency } = pullLastResultOrThrow();
-
-      expect(exchange.value).equal("url1");
-      expect(currency.value).equal("USD");
-    }
-  });
-
-  it("should update parsed amount when amount change", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    {
-      const { amount, parsedAmount } = pullLastResultOrThrow();
-
-      expect(parsedAmount).equal(undefined);
-
-      expect(amount.onInput).not.undefined;
-      if (!amount.onInput) return;
-      amount.onInput("12");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { parsedAmount } = pullLastResultOrThrow();
-
-      expect(parsedAmount).deep.equals({
-        value: 12,
-        fraction: 0,
-        currency: "ARS",
-      });
-    }
-  });
-
-  it("should have an amount field", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    await defaultTestForInputText(
-      waitForStateUpdate,
-      () => pullLastResultOrThrow().amount,
-    );
-  });
-
-  it("should have an exchange selector ", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    await defaultTestForInputSelect(
-      waitForStateUpdate,
-      () => pullLastResultOrThrow().exchange,
-    );
-  });
-
-  it("should have a currency selector ", async () => {
-    const { pullLastResultOrThrow, waitForStateUpdate } = mountHook(() =>
-      useComponentState(exchangeListWithARSandUSD, undefined, "ARS"),
-    );
-
-    await defaultTestForInputSelect(
-      waitForStateUpdate,
-      () => pullLastResultOrThrow().currency,
-    );
-  });
-});
-
-async function defaultTestForInputText(
-  awaiter: () => Promise<boolean>,
-  getField: () => TextFieldHandler,
-): Promise<void> {
-  let nextValue = "";
-  {
-    const field = getField();
-    const initialValue = field.value;
-    nextValue = `${initialValue} something else`;
-    expect(field.onInput).not.undefined;
-    if (!field.onInput) return;
-    field.onInput(nextValue);
-  }
-
-  expect(await awaiter()).true;
-
-  {
-    const field = getField();
-    expect(field.value).equal(nextValue);
-  }
-}
-
-async function defaultTestForInputSelect(
-  awaiter: () => Promise<boolean>,
-  getField: () => SelectFieldHandler,
-): Promise<void> {
-  let nextValue = "";
-
-  {
-    const field = getField();
-    const initialValue = field.value;
-    const keys = Object.keys(field.list);
-    const nextIdx = keys.indexOf(initialValue) + 1;
-    if (keys.length < nextIdx) {
-      throw new Error("no enough values");
-    }
-    nextValue = keys[nextIdx];
-    if (field.onChange === undefined) expect.fail();
-    field.onChange(nextValue);
-  }
-
-  expect(await awaiter()).true;
-
-  {
-    const field = getField();
-
-    expect(field.value).equal(nextValue);
-  }
-}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
deleted file mode 100644
index dd80faccd..000000000
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- 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/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { ErrorMessage } from "../components/ErrorMessage.js";
-import { SelectList } from "../components/SelectList.js";
-import {
-  BoldLight,
-  Centered,
-  Input,
-  InputWithLabel,
-  LightText,
-  LinkPrimary,
-  SubTitle,
-} from "../components/styled/index.js";
-import { useTranslationContext } from "../context/translation.js";
-import { Button } from "../mui/Button.js";
-import { SelectFieldHandler, TextFieldHandler } from "../mui/handlers.js";
-import { Pages } from "../NavigationBar.js";
-
-export interface Props {
-  error: string | undefined;
-  initialAmount?: string;
-  exchangeUrlWithCurrency: Record<string, string>;
-  onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise<void>;
-  initialCurrency?: string;
-}
-
-export interface State {
-  noExchangeFound: boolean;
-  parsedAmount: AmountJson | undefined;
-  amount: TextFieldHandler;
-  currency: SelectFieldHandler;
-  exchange: SelectFieldHandler;
-}
-
-export function useComponentState(
-  exchangeUrlWithCurrency: Record<string, string>,
-  initialAmount: string | undefined,
-  initialCurrency: string | undefined,
-): State {
-  const exchangeSelectList = Object.keys(exchangeUrlWithCurrency);
-  const currencySelectList = Object.values(exchangeUrlWithCurrency);
-  const exchangeMap = exchangeSelectList.reduce(
-    (p, c) => ({ ...p, [c]: `${c} (${exchangeUrlWithCurrency[c]})` }),
-    {} as Record<string, string>,
-  );
-  const currencyMap = currencySelectList.reduce(
-    (p, c) => ({ ...p, [c]: c }),
-    {} as Record<string, string>,
-  );
-
-  const foundExchangeForCurrency = exchangeSelectList.findIndex(
-    (e) => exchangeUrlWithCurrency[e] === initialCurrency,
-  );
-
-  const initialExchange =
-    foundExchangeForCurrency !== -1
-      ? exchangeSelectList[foundExchangeForCurrency]
-      : !initialCurrency && exchangeSelectList.length > 0
-      ? exchangeSelectList[0]
-      : undefined;
-
-  const [exchange, setExchange] = useState(initialExchange || "");
-  const [currency, setCurrency] = useState(
-    initialExchange ? exchangeUrlWithCurrency[initialExchange] : "",
-  );
-
-  const [amount, setAmount] = useState(initialAmount || "");
-  const parsedAmount = Amounts.parse(`${currency}:${amount}`);
-
-  async function changeExchange(exchange: string): Promise<void> {
-    setExchange(exchange);
-    setCurrency(exchangeUrlWithCurrency[exchange]);
-  }
-
-  async function changeCurrency(currency: string): Promise<void> {
-    setCurrency(currency);
-    const found = Object.entries(exchangeUrlWithCurrency).find(
-      (e) => e[1] === currency,
-    );
-
-    if (found) {
-      setExchange(found[0]);
-    } else {
-      setExchange("");
-    }
-  }
-  return {
-    noExchangeFound: initialExchange === undefined,
-    currency: {
-      list: currencyMap,
-      value: currency,
-      onChange: changeCurrency,
-    },
-    exchange: {
-      list: exchangeMap,
-      value: exchange,
-      onChange: changeExchange,
-    },
-    amount: {
-      value: amount,
-      onInput: async (e: string) => setAmount(e),
-    },
-    parsedAmount,
-  };
-}
-
-export function CreateManualWithdraw({
-  initialAmount,
-  exchangeUrlWithCurrency,
-  error,
-  initialCurrency,
-  onCreate,
-}: Props): VNode {
-  const { i18n } = useTranslationContext();
-
-  const state = useComponentState(
-    exchangeUrlWithCurrency,
-    initialAmount,
-    initialCurrency,
-  );
-
-  if (state.noExchangeFound) {
-    if (initialCurrency) {
-      return (
-        <section>
-          <SubTitle>
-            <i18n.Translate>
-              Manual Withdrawal for {initialCurrency}
-            </i18n.Translate>
-          </SubTitle>
-          <LightText>
-            <i18n.Translate>
-              Choose a exchange from where the coins will be withdrawn. The
-              exchange will send the coins to this wallet after receiving a 
wire
-              transfer with the correct subject.
-            </i18n.Translate>
-          </LightText>
-          <Centered style={{ marginTop: 100 }}>
-            <BoldLight>
-              <i18n.Translate>
-                No exchange found for {initialCurrency}
-              </i18n.Translate>
-            </BoldLight>
-            <LinkPrimary
-              href={Pages.settingsExchangeAdd({ currency: initialCurrency })}
-              style={{ marginLeft: "auto" }}
-            >
-              <i18n.Translate>Add Exchange</i18n.Translate>
-            </LinkPrimary>
-          </Centered>
-        </section>
-      );
-    }
-    return (
-      <section>
-        <SubTitle>
-          <i18n.Translate>
-            Manual Withdrawal for {state.currency.value}
-          </i18n.Translate>
-        </SubTitle>
-        <LightText>
-          <i18n.Translate>
-            Choose a exchange from where the coins will be withdrawn. The
-            exchange will send the coins to this wallet after receiving a wire
-            transfer with the correct subject.
-          </i18n.Translate>
-        </LightText>
-        <Centered style={{ marginTop: 100 }}>
-          <BoldLight>
-            <i18n.Translate>No exchange configured</i18n.Translate>
-          </BoldLight>
-          <LinkPrimary
-            href={Pages.settingsExchangeAdd({})}
-            style={{ marginLeft: "auto" }}
-          >
-            <i18n.Translate>Add Exchange</i18n.Translate>
-          </LinkPrimary>
-        </Centered>
-      </section>
-    );
-  }
-
-  return (
-    <Fragment>
-      <section>
-        {error && (
-          <ErrorMessage
-            title={
-              <i18n.Translate>Can&apos;t create the reserve</i18n.Translate>
-            }
-            description={error}
-          />
-        )}
-        <SubTitle>
-          <i18n.Translate>
-            Manual Withdrawal for {state.currency.value}
-          </i18n.Translate>
-        </SubTitle>
-        <LightText>
-          <i18n.Translate>
-            Choose a exchange from where the coins will be withdrawn. The
-            exchange will send the coins to this wallet after receiving a wire
-            transfer with the correct subject.
-          </i18n.Translate>
-        </LightText>
-        <p>
-          <Input>
-            <SelectList
-              label={<i18n.Translate>Currency</i18n.Translate>}
-              name="currency"
-              {...state.currency}
-            />
-          </Input>
-          <Input>
-            <SelectList
-              label={<i18n.Translate>Exchange</i18n.Translate>}
-              name="exchange"
-              {...state.exchange}
-            />
-          </Input>
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <LinkPrimary
-              href={Pages.settingsExchangeAdd({})}
-              style={{ marginLeft: "auto" }}
-            >
-              <i18n.Translate>Add Exchange</i18n.Translate>
-            </LinkPrimary>
-          </div>
-          {state.currency.value && (
-            <InputWithLabel
-              invalid={!!state.amount.value && !state.parsedAmount}
-            >
-              <label>
-                <i18n.Translate>Amount</i18n.Translate>
-              </label>
-              <div>
-                <span>{state.currency.value}</span>
-                <input
-                  type="number"
-                  value={state.amount.value}
-                  // onInput={(e) => 
state.amount.onInput(e.currentTarget.value)}
-                />
-              </div>
-            </InputWithLabel>
-          )}
-        </p>
-      </section>
-      <footer>
-        <div />
-        <Button
-          variant="contained"
-          disabled={!state.parsedAmount || !state.exchange.value}
-          onClick={() => onCreate(state.exchange.value, state.parsedAmount!)}
-        >
-          <i18n.Translate>Start withdrawal</i18n.Translate>
-        </Button>
-      </footer>
-    </Fragment>
-  );
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index 373045833..3f23515b2 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -18,6 +18,7 @@ import { AmountJson, PaytoUri } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import {
+  AmountFieldHandler,
   ButtonHandler,
   SelectFieldHandler,
   TextFieldHandler,
@@ -98,7 +99,7 @@ export namespace State {
     totalFee: AmountJson;
     totalToDeposit: AmountJson;
 
-    amount: TextFieldHandler;
+    amount: AmountFieldHandler;
     account: SelectFieldHandler;
     cancelHandler: ButtonHandler;
     depositHandler: ButtonHandler;
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index 91883c823..bbf2c2771 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -52,9 +52,13 @@ export function useComponentState(
   });
 
   const initialValue =
-    parsed !== undefined ? Amounts.stringifyValue(parsed) : "0";
+    parsed !== undefined
+      ? parsed
+      : currency !== undefined
+      ? Amounts.zeroOfCurrency(currency)
+      : undefined;
   // const [accountIdx, setAccountIdx] = useState<number>(0);
-  const [amount, setAmount] = useState(initialValue);
+  const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as 
any));
   const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
 
   const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
@@ -81,7 +85,7 @@ export function useComponentState(
   }
   const { accounts, balances } = hook.response;
 
-  const parsedAmount = Amounts.parse(`${currency}:${amount}`);
+  // const parsedAmount = Amounts.parse(`${currency}:${amount}`);
 
   if (addingAccount) {
     return {
@@ -129,8 +133,8 @@ export function useComponentState(
   const firstAccount = accounts[0].uri;
   const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
 
-  if (fee === undefined && parsedAmount) {
-    getFeeForAmount(currentAccount, parsedAmount, api).then((initialFee) => {
+  if (fee === undefined) {
+    getFeeForAmount(currentAccount, amount, api).then((initialFee) => {
       setFee(initialFee);
     });
     return {
@@ -143,9 +147,9 @@ export function useComponentState(
 
   async function updateAccountFromList(accountStr: string): Promise<void> {
     const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
-    if (uri && parsedAmount) {
+    if (uri) {
       try {
-        const result = await getFeeForAmount(uri, parsedAmount, api);
+        const result = await getFeeForAmount(uri, amount, api);
         setSelectedAccount(uri);
         setFee(result);
       } catch (e) {
@@ -155,17 +159,15 @@ export function useComponentState(
     }
   }
 
-  async function updateAmount(numStr: string): Promise<void> {
-    const parsed = Amounts.parse(`${currency}:${numStr}`);
-    if (parsed) {
-      try {
-        const result = await getFeeForAmount(currentAccount, parsed, api);
-        setAmount(numStr);
-        setFee(result);
-      } catch (e) {
-        setAmount(numStr);
-        setFee(undefined);
-      }
+  async function updateAmount(newAmount: AmountJson): Promise<void> {
+    // const parsed = Amounts.parse(`${currency}:${numStr}`);
+    try {
+      const result = await getFeeForAmount(currentAccount, newAmount, api);
+      setAmount(newAmount);
+      setFee(result);
+    } catch (e) {
+      setAmount(newAmount);
+      setFee(undefined);
     }
   }
 
@@ -175,32 +177,29 @@ export function useComponentState(
       : Amounts.zeroOfCurrency(currency);
 
   const totalToDeposit =
-    parsedAmount && fee !== undefined
-      ? Amounts.sub(parsedAmount, totalFee).amount
+    fee !== undefined
+      ? Amounts.sub(amount, totalFee).amount
       : Amounts.zeroOfCurrency(currency);
 
   const isDirty = amount !== initialValue;
   const amountError = !isDirty
     ? undefined
-    : !parsedAmount
-    ? "Invalid amount"
-    : Amounts.cmp(balance, parsedAmount) === -1
+    : Amounts.cmp(balance, amount) === -1
     ? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
     : undefined;
 
   const unableToDeposit =
-    !parsedAmount || //no amount specified
     Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
     fee === undefined || //no fee calculated yet
     amountError !== undefined; //amount field may be invalid
 
   async function doSend(): Promise<void> {
-    if (!parsedAmount || !currency) return;
+    if (!currency) return;
 
     const depositPaytoUri = stringifyPaytoUri(currentAccount);
-    const amount = Amounts.stringify(parsedAmount);
+    const amountStr = Amounts.stringify(amount);
     await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
-      amount,
+      amount: amountStr,
       depositPaytoUri,
     });
     onSuccess(currency);
@@ -211,7 +210,7 @@ export function useComponentState(
     error: undefined,
     currency,
     amount: {
-      value: String(amount),
+      value: amount,
       onInput: updateAmount,
       error: amountError,
     },
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
index f03788d4e..75c544c84 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
@@ -52,7 +52,7 @@ export const WithNoAccountForIBAN = createExample(ReadyView, {
     onInput: async () => {
       null;
     },
-    value: "10:USD",
+    value: Amounts.parseOrThrow("USD:10"),
   },
   onAddAccount: {},
   cancelHandler: {},
@@ -87,7 +87,7 @@ export const WithIBANAccountTypeSelected = 
createExample(ReadyView, {
     onInput: async () => {
       null;
     },
-    value: "10:USD",
+    value: Amounts.parseOrThrow("USD:10"),
   },
   onAddAccount: {},
   cancelHandler: {},
@@ -123,7 +123,7 @@ export const NewBitcoinAccountTypeSelected = 
createExample(ReadyView, {
     onInput: async () => {
       null;
     },
-    value: "10:USD",
+    value: Amounts.parseOrThrow("USD:10"),
   },
   cancelHandler: {},
   depositHandler: {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 17e17d185..3f08c678c 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -194,7 +194,7 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
-      expect(r.amount.value).eq("0");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
       expect(r.depositHandler.onClick).undefined;
     }
 
@@ -269,7 +269,7 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
-      expect(r.amount.value).eq("0");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
       expect(r.depositHandler.onClick).undefined;
       expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
       expect(r.account.onChange).not.undefined;
@@ -285,7 +285,7 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).eq("0");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
       expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
       expect(r.depositHandler.onClick).undefined;
     }
@@ -423,7 +423,7 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
-      expect(r.amount.value).eq("0");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
       expect(r.depositHandler.onClick).undefined;
       expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
       expect(r.account.onChange).not.undefined;
@@ -439,13 +439,13 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).eq("0");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
       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");
+      r.amount.onInput(Amounts.parseOrThrow("EUR:10"));
     }
 
     expect(await waitForStateUpdate()).true;
@@ -456,7 +456,7 @@ describe("DepositPage states", () => {
       expect(r.cancelHandler.onClick).not.undefined;
       expect(r.currency).eq(currency);
       expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).eq("10");
+      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
       expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
       expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`));
       expect(r.depositHandler.onClick).not.undefined;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
index 771db828d..6a28f31e1 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
@@ -173,25 +173,22 @@ export function ReadyView(state: State.Ready): VNode {
           <Grid item xs={1}>
             <AmountField
               label={<i18n.Translate>Amount</i18n.Translate>}
-              currency={state.currency}
               handler={state.amount}
             />
           </Grid>
           <Grid item xs={1}>
             <AmountField
               label={<i18n.Translate>Deposit fee</i18n.Translate>}
-              currency={state.currency}
               handler={{
-                value: Amounts.stringifyValue(state.totalFee),
+                value: state.totalFee,
               }}
             />
           </Grid>
           <Grid item xs={1}>
             <AmountField
               label={<i18n.Translate>Total deposit</i18n.Translate>}
-              currency={state.currency}
               handler={{
-                value: Amounts.stringifyValue(state.totalToDeposit),
+                value: state.totalToDeposit,
               }}
             />
           </Grid>
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
index ba1a560ef..7e4c775e6 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
@@ -33,9 +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";
 import arrowIcon from "../svg/chevron-down.svg";
 import bankIcon from "../svg/ri-bank-line.svg";
@@ -279,12 +277,13 @@ export function DestinationSelectionGetCash({
   const parsedInitialAmount = !initialAmount
     ? undefined
     : Amounts.parse(initialAmount);
-  const parsedInitialAmountValue = !parsedInitialAmount
-    ? "0"
-    : Amounts.stringifyValue(parsedInitialAmount);
+
   const [currency, setCurrency] = useState(parsedInitialAmount?.currency);
 
-  const [amount, setAmount] = useState(parsedInitialAmountValue);
+  const [amount, setAmount] = useState(
+    parsedInitialAmount ?? Amounts.zeroOfCurrency(currency ?? "KUDOS"),
+  );
+
   const { i18n } = useTranslationContext();
   const previous1: Contact[] = [];
   const previous2: Contact[] = [
@@ -313,10 +312,8 @@ export function DestinationSelectionGetCash({
       </div>
     );
   }
-  const currencyAndAmount = `${currency}:${amount}`;
-  const parsedAmount = Amounts.parse(currencyAndAmount);
-  // const dirty = parsedInitialAmountValue !== amount;
-  const invalid = !parsedAmount || Amounts.isZero(parsedAmount);
+  const currencyAndAmount = Amounts.stringify(amount);
+  const invalid = Amounts.isZero(amount);
   return (
     <Container>
       <h1>
@@ -325,7 +322,6 @@ export function DestinationSelectionGetCash({
       <Grid container columns={2} justifyContent="space-between">
         <AmountField
           label={<i18n.Translate>Amount</i18n.Translate>}
-          currency={currency}
           required
           handler={{
             onInput: async (s) => setAmount(s),
@@ -416,12 +412,12 @@ export function DestinationSelectionSendCash({
   const parsedInitialAmount = !initialAmount
     ? undefined
     : Amounts.parse(initialAmount);
-  const parsedInitialAmountValue = !parsedInitialAmount
-    ? ""
-    : Amounts.stringifyValue(parsedInitialAmount);
+
   const currency = parsedInitialAmount?.currency;
 
-  const [amount, setAmount] = useState(parsedInitialAmountValue);
+  const [amount, setAmount] = useState(
+    parsedInitialAmount ?? Amounts.zeroOfCurrency(currency ?? "KUDOS"),
+  );
   const { i18n } = useTranslationContext();
   const previous1: Contact[] = [];
   const previous2: Contact[] = [
@@ -450,9 +446,9 @@ export function DestinationSelectionSendCash({
       </div>
     );
   }
-  const currencyAndAmount = `${currency}:${amount}`;
-  const parsedAmount = Amounts.parse(currencyAndAmount);
-  const invalid = !parsedAmount || Amounts.isZero(parsedAmount);
+  const currencyAndAmount = Amounts.stringify(amount);
+  //const parsedAmount = Amounts.parse(currencyAndAmount);
+  const invalid = Amounts.isZero(amount);
   return (
     <Container>
       <h1>
@@ -462,7 +458,6 @@ export function DestinationSelectionSendCash({
       <div>
         <AmountField
           label={<i18n.Translate>Amount</i18n.Translate>}
-          currency={currency}
           required
           handler={{
             onInput: async (s) => setAmount(s),
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
index 39fbb6ce2..63d545b97 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
@@ -44,21 +44,22 @@ export function useComponentState(
 
   const comparingExchanges = selectedIdx !== initialValue;
 
-  const initialExchange =
-    comparingExchanges ? exchanges[initialValue] : undefined;
+  const initialExchange = comparingExchanges
+    ? exchanges[initialValue]
+    : undefined;
 
   const hook = useAsyncAsHook(async () => {
     const selected = !selectedExchange
       ? undefined
       : await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
-        exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
-      });
+          exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
+        });
 
     const original = !initialExchange
       ? undefined
       : await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
-        exchangeBaseUrl: initialExchange.exchangeBaseUrl,
-      });
+          exchangeBaseUrl: initialExchange.exchangeBaseUrl,
+        });
 
     return {
       exchanges,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
deleted file mode 100644
index e2284a466..000000000
--- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- 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 {
-  AcceptManualWithdrawalResult,
-  AmountJson,
-  Amounts,
-  NotificationType,
-  parsePaytoUri,
-  PaytoUri,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
-import { useTranslationContext } from "../context/translation.js";
-import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
-import { wxApi } from "../wxApi.js";
-import { CreateManualWithdraw } from "./CreateManualWithdraw.js";
-import { ReserveCreated } from "./ReserveCreated.js";
-
-interface Props {
-  amount?: string;
-  onCancel: () => Promise<void>;
-}
-
-export function ManualWithdrawPage({ amount, onCancel }: Props): VNode {
-  const [success, setSuccess] = useState<
-    | {
-        response: AcceptManualWithdrawalResult;
-        exchangeBaseUrl: string;
-        amount: AmountJson;
-        paytoURI: PaytoUri | undefined;
-        payto: string;
-      }
-    | undefined
-  >(undefined);
-  const [error, setError] = useState<string | undefined>(undefined);
-
-  const state = useAsyncAsHook(() =>
-    wxApi.wallet.call(WalletApiOperation.ListExchanges, {}),
-  );
-  useEffect(() => {
-    return wxApi.listener.onUpdateNotification(
-      [NotificationType.ExchangeAdded],
-      state?.retry,
-    );
-  });
-  const { i18n } = useTranslationContext();
-
-  async function doCreate(
-    exchangeBaseUrl: string,
-    amount: AmountJson,
-  ): Promise<void> {
-    try {
-      const response = await wxApi.wallet.call(
-        WalletApiOperation.AcceptManualWithdrawal,
-        {
-          exchangeBaseUrl: exchangeBaseUrl,
-          amount: Amounts.stringify(amount),
-        },
-      );
-      const payto = response.exchangePaytoUris[0];
-      const paytoURI = parsePaytoUri(payto);
-      setSuccess({ exchangeBaseUrl, response, amount, paytoURI, payto });
-    } catch (e) {
-      if (e instanceof Error) {
-        setError(e.message);
-      } else {
-        setError("unexpected error");
-      }
-      setSuccess(undefined);
-    }
-  }
-
-  if (success) {
-    return (
-      <ReserveCreated
-        reservePub={success.response.reservePub}
-        paytoURI={success.paytoURI}
-        // payto={success.payto}
-        exchangeBaseUrl={success.exchangeBaseUrl}
-        amount={success.amount}
-        onCancel={onCancel}
-      />
-    );
-  }
-
-  if (!state) {
-    return <Loading />;
-  }
-  if (state.hasError) {
-    return (
-      <LoadingError
-        title={
-          <i18n.Translate>
-            Could not load the list of known exchanges
-          </i18n.Translate>
-        }
-        error={state}
-      />
-    );
-  }
-
-  const exchangeList = state.response.exchanges.reduce(
-    (p, c) => ({
-      ...p,
-      [c.exchangeBaseUrl]: c.currency || "??",
-    }),
-    {} as Record<string, string>,
-  );
-
-  const parsedAmount = !amount ? undefined : Amounts.parse(amount);
-  const currency = parsedAmount?.currency;
-  const amountValue = !parsedAmount
-    ? undefined
-    : Amounts.stringifyValue(parsedAmount);
-  return (
-    <CreateManualWithdraw
-      error={error}
-      exchangeUrlWithCurrency={exchangeList}
-      onCreate={doCreate}
-      initialCurrency={currency}
-      initialAmount={amountValue}
-    />
-  );
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
index 42808b573..ef1295846 100644
--- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
@@ -20,7 +20,6 @@
  */
 
 import * as a1 from "./Backup.stories.js";
-import * as a3 from "./CreateManualWithdraw.stories.js";
 import * as a4 from "./DepositPage/stories.js";
 import * as a5 from "./ExchangeAddConfirm.stories.js";
 import * as a6 from "./ExchangeAddSetUrl.stories.js";
@@ -40,7 +39,6 @@ import * as a20 from "./ManageAccount/stories.js";
 
 export default [
   a1,
-  a3,
   a4,
   a5,
   a6,

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