summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-10-28 13:39:06 -0300
committerSebastian <sebasjm@gmail.com>2022-10-28 13:39:26 -0300
commitfe6e9be70225cf2953822ff64b9e90066cab97ea (patch)
treed3c2f2829a963efd6596dbc7fd04d2a325bd2ae0
parent7c33040ae377548e5589b11ad218c8b95b82c9c5 (diff)
downloadwallet-core-fe6e9be70225cf2953822ff64b9e90066cab97ea.tar.gz
wallet-core-fe6e9be70225cf2953822ff64b9e90066cab97ea.tar.bz2
wallet-core-fe6e9be70225cf2953822ff64b9e90066cab97ea.zip
manage account instead of add account
-rw-r--r--packages/taler-wallet-webextension/src/svg/check_24px.svg1
-rw-r--r--packages/taler-wallet-webextension/src/svg/warning_24px.svg1
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddAccount/stories.tsx29
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddAccount/views.tsx249
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts8
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts27
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx21
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts10
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx98
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts (renamed from packages/taler-wallet-webextension/src/wallet/AddAccount/index.ts)11
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts (renamed from packages/taler-wallet-webextension/src/wallet/AddAccount/state.ts)30
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx208
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts (renamed from packages/taler-wallet-webextension/src/wallet/AddAccount/test.ts)0
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx534
-rw-r--r--packages/taler-wallet-webextension/src/wallet/index.stories.tsx2
15 files changed, 867 insertions, 362 deletions
diff --git a/packages/taler-wallet-webextension/src/svg/check_24px.svg b/packages/taler-wallet-webextension/src/svg/check_24px.svg
new file mode 100644
index 000000000..522695ef3
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/svg/check_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg> \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/svg/warning_24px.svg b/packages/taler-wallet-webextension/src/svg/warning_24px.svg
new file mode 100644
index 000000000..d27c4c6ec
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/svg/warning_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg> \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/wallet/AddAccount/stories.tsx b/packages/taler-wallet-webextension/src/wallet/AddAccount/stories.tsx
deleted file mode 100644
index 696e424c4..000000000
--- a/packages/taler-wallet-webextension/src/wallet/AddAccount/stories.tsx
+++ /dev/null
@@ -1,29 +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 { ReadyView } from "./views.js";
-
-export default {
- title: "example",
-};
-
-export const Ready = createExample(ReadyView, {});
diff --git a/packages/taler-wallet-webextension/src/wallet/AddAccount/views.tsx b/packages/taler-wallet-webextension/src/wallet/AddAccount/views.tsx
deleted file mode 100644
index d6ab7e967..000000000
--- a/packages/taler-wallet-webextension/src/wallet/AddAccount/views.tsx
+++ /dev/null
@@ -1,249 +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 { parsePaytoUri } from "@gnu-taler/taler-util";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { ErrorMessage } from "../../components/ErrorMessage.js";
-import { LoadingError } from "../../components/LoadingError.js";
-import { SelectList } from "../../components/SelectList.js";
-import { Input, LightText, SubTitle } from "../../components/styled/index.js";
-import { useTranslationContext } from "../../context/translation.js";
-import { Button } from "../../mui/Button.js";
-import { TextFieldHandler } from "../../mui/handlers.js";
-import { TextField } from "../../mui/TextField.js";
-import { State } from "./index.js";
-
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
- const { i18n } = useTranslationContext();
-
- return (
- <LoadingError
- title={<i18n.Translate>Could not load</i18n.Translate>}
- error={error}
- />
- );
-}
-
-export function ReadyView({
- currency,
- error,
- accountType,
- alias,
- onAccountAdded,
- onCancel,
- uri,
-}: State.Ready): VNode {
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- <section>
- <SubTitle>
- <i18n.Translate>Add bank account for {currency}</i18n.Translate>
- </SubTitle>
- <LightText>
- <i18n.Translate>
- Enter the URL of an exchange you trust.
- </i18n.Translate>
- </LightText>
-
- {error && (
- <ErrorMessage
- title={<i18n.Translate>Unable add this account</i18n.Translate>}
- description={error}
- />
- )}
- <p>
- <Input>
- <SelectList
- label={<i18n.Translate>Select account type</i18n.Translate>}
- list={accountType.list}
- name="accountType"
- value={accountType.value}
- onChange={accountType.onChange}
- />
- </Input>
- </p>
- {accountType.value === "" ? undefined : (
- <Fragment>
- <p>
- <CustomFieldByAccountType type={accountType.value} field={uri} />
- </p>
- <p>
- <TextField
- label="Account alias"
- variant="standard"
- required
- fullWidth
- disabled={accountType.value === ""}
- value={alias.value}
- onChange={alias.onInput}
- />
- </p>
- </Fragment>
- )}
- </section>
- <footer>
- <Button
- variant="contained"
- color="secondary"
- onClick={onCancel.onClick}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </Button>
- <Button
- variant="contained"
- onClick={onAccountAdded.onClick}
- disabled={!onAccountAdded.onClick}
- >
- <i18n.Translate>Add</i18n.Translate>
- </Button>
- </footer>
- </Fragment>
- );
-}
-
-function BitcoinAddressAccount({ field }: { field: TextFieldHandler }): VNode {
- const { i18n } = useTranslationContext();
- const [value, setValue] = useState<string | undefined>(undefined);
- const errors = undefinedIfEmpty({
- value: !value ? i18n.str`Can't be empty` : undefined,
- });
- return (
- <Fragment>
- <TextField
- label="Bitcoin address"
- variant="standard"
- fullWidth
- value={value}
- error={value !== undefined && !!errors?.value}
- onChange={(v) => {
- setValue(v);
- if (!errors) {
- field.onInput(`payto://bitcoin/${value}`);
- }
- }}
- />
- {value !== undefined && errors?.value && (
- <ErrorMessage title={<span>{errors?.value}</span>} />
- )}
- </Fragment>
- );
-}
-
-function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
- return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
- ? obj
- : undefined;
-}
-
-function TalerBankAddressAccount({
- field,
-}: {
- field: TextFieldHandler;
-}): VNode {
- const { i18n } = useTranslationContext();
- const [host, setHost] = useState<string | undefined>(undefined);
- const [account, setAccount] = useState<string | undefined>(undefined);
- const errors = undefinedIfEmpty({
- host: !host ? i18n.str`Can't be empty` : undefined,
- account: !account ? i18n.str`Can't be empty` : undefined,
- });
- return (
- <Fragment>
- <TextField
- label="Bank host"
- variant="standard"
- fullWidth
- value={host}
- error={host !== undefined && !!errors?.host}
- onChange={(v) => {
- setHost(v);
- if (!errors) {
- field.onInput(`payto://x-taler-bank/${host}/${account}`);
- }
- }}
- />{" "}
- {host !== undefined && errors?.host && (
- <ErrorMessage title={<span>{errors?.host}</span>} />
- )}
- <TextField
- label="Bank account"
- variant="standard"
- fullWidth
- value={account}
- error={account !== undefined && !!errors?.account}
- onChange={(v) => {
- setAccount(v || "");
- if (!errors) {
- field.onInput(`payto://x-taler-bank/${host}/${account}`);
- }
- }}
- />{" "}
- {account !== undefined && errors?.account && (
- <ErrorMessage title={<span>{errors?.account}</span>} />
- )}
- </Fragment>
- );
-}
-
-function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode {
- const { i18n } = useTranslationContext();
- const [value, setValue] = useState<string | undefined>(undefined);
- const errors = undefinedIfEmpty({
- value: !value ? i18n.str`Can't be empty` : undefined,
- });
- return (
- <Fragment>
- <TextField
- label="IBAN number"
- variant="standard"
- fullWidth
- value={value}
- error={value !== undefined && !!errors?.value}
- onChange={(v) => {
- setValue(v);
- if (!errors) {
- field.onInput(`payto://iba/${value}`);
- }
- }}
- />
- {value !== undefined && errors?.value && (
- <ErrorMessage title={<span>{errors?.value}</span>} />
- )}
- </Fragment>
- );
-}
-
-function CustomFieldByAccountType({
- type,
- field,
-}: {
- type: string;
- field: TextFieldHandler;
-}): VNode {
- if (type === "bitcoin") {
- return <BitcoinAddressAccount field={field} />;
- }
- if (type === "x-taler-bank") {
- return <TalerBankAddressAccount field={field} />;
- }
- if (type === "iban") {
- return <IbanAddressAccount field={field} />;
- }
- return <Fragment />;
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index 77661fe15..85896da26 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -24,7 +24,7 @@ import {
} from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js";
import { wxApi } from "../../wxApi.js";
-import { AddAccountPage } from "../AddAccount/index.js";
+import { ManageAccountPage } from "../ManageAccount/index.js";
import { useComponentState } from "./state.js";
import {
AmountOrCurrencyErrorView,
@@ -62,7 +62,7 @@ export namespace State {
}
export interface AddingAccount {
- status: "adding-account";
+ status: "manage-account";
error: undefined;
currency: string;
onAccountAdded: (p: string) => void;
@@ -94,7 +94,7 @@ export namespace State {
error: undefined;
currency: string;
- selectedAccount: PaytoUri | undefined;
+ currentAccount: PaytoUri;
totalFee: AmountJson;
totalToDeposit: AmountJson;
@@ -112,7 +112,7 @@ const viewMapping: StateViewMap<State> = {
"amount-or-currency-error": AmountOrCurrencyErrorView,
"no-enough-balance": NoEnoughBalanceView,
"no-accounts": NoAccountToDepositView,
- "adding-account": AddAccountPage,
+ "manage-account": ManageAccountPage,
ready: ReadyView,
};
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index b3a377040..fe692e80d 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -50,9 +50,7 @@ export function useComponentState(
// const [accountIdx, setAccountIdx] = useState<number>(0);
const [amount, setAmount] = useState(initialValue);
- const [selectedAccount, setSelectedAccount] = useState<
- PaytoUri | undefined
- >();
+ const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
const [addingAccount, setAddingAccount] = useState(false);
@@ -82,7 +80,7 @@ export function useComponentState(
if (addingAccount) {
return {
- status: "adding-account",
+ status: "manage-account",
error: undefined,
currency,
onAccountAdded: (p: string) => {
@@ -92,6 +90,7 @@ export function useComponentState(
},
onCancel: () => {
setAddingAccount(false);
+ hook.retry();
},
};
}
@@ -122,13 +121,12 @@ export function useComponentState(
},
};
}
+ const firstAccount = accounts[0].uri
+ const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
const accountMap = createLabelsForBankAccount(accounts);
- accountMap[""] = "Select one account...";
async function updateAccountFromList(accountStr: string): Promise<void> {
- // const newSelected = !accountMap[accountStr] ? undefined : accountMap[accountStr];
- // if (!newSelected) return;
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
if (uri && parsedAmount) {
try {
@@ -136,7 +134,6 @@ export function useComponentState(
setSelectedAccount(uri);
setFee(result);
} catch (e) {
- console.error(e)
setSelectedAccount(uri);
setFee(undefined);
}
@@ -145,13 +142,12 @@ export function useComponentState(
async function updateAmount(numStr: string): Promise<void> {
const parsed = Amounts.parse(`${currency}:${numStr}`);
- if (parsed && selectedAccount) {
+ if (parsed) {
try {
- const result = await getFeeForAmount(selectedAccount, parsed, api);
+ const result = await getFeeForAmount(currentAccount, parsed, api);
setAmount(numStr);
setFee(result);
} catch (e) {
- console.error(e)
setAmount(numStr);
setFee(undefined);
}
@@ -179,15 +175,14 @@ export function useComponentState(
const unableToDeposit =
!parsedAmount || //no amount specified
- selectedAccount === undefined || //no account selected
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 (!selectedAccount || !parsedAmount || !currency) return;
+ if (!parsedAmount || !currency) return;
- const depositPaytoUri = `payto://${selectedAccount.targetType}/${selectedAccount.targetPath}`;
+ const depositPaytoUri = `payto://${currentAccount.targetType}/${currentAccount.targetPath}`;
const amount = Amounts.stringify(parsedAmount);
await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
amount, depositPaytoUri
@@ -211,10 +206,10 @@ export function useComponentState(
},
account: {
list: accountMap,
- value: !selectedAccount ? "" : stringifyPaytoUri(selectedAccount),
+ value: stringifyPaytoUri(currentAccount),
onChange: updateAccountFromList,
},
- selectedAccount,
+ currentAccount,
cancelHandler: {
onClick: async () => {
onCancel(currency);
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
index ed5945c06..64b2c91a7 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
@@ -55,6 +55,13 @@ export const WithNoAccountForIBAN = createExample(ReadyView, {
null;
},
},
+ currentAccount: {
+ isKnown: true,
+ targetType: "iban",
+ iban: "ABCD1234",
+ params: {},
+ targetPath: "/ABCD1234",
+ },
currency: "USD",
amount: {
onInput: async () => {
@@ -83,6 +90,13 @@ export const WithIBANAccountTypeSelected = createExample(ReadyView, {
null;
},
},
+ currentAccount: {
+ isKnown: true,
+ targetType: "iban",
+ iban: "ABCD1234",
+ params: {},
+ targetPath: "/ABCD1234",
+ },
currency: "USD",
amount: {
onInput: async () => {
@@ -111,6 +125,13 @@ export const NewBitcoinAccountTypeSelected = createExample(ReadyView, {
null;
},
},
+ currentAccount: {
+ isKnown: true,
+ targetType: "iban",
+ iban: "ABCD1234",
+ params: {},
+ targetPath: "/ABCD1234",
+ },
onAddAccount: {},
currency: "USD",
amount: {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 62097c3e4..4a648312e 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -172,7 +172,7 @@ describe("DepositPage states", () => {
if (r.status !== "ready") expect.fail();
expect(r.cancelHandler.onClick).not.undefined;
expect(r.currency).eq(currency);
- expect(r.account.value).eq("");
+ expect(r.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
expect(r.amount.value).eq("0");
expect(r.depositHandler.onClick).undefined;
}
@@ -195,7 +195,7 @@ describe("DepositPage states", () => {
}],
})
handler.addWalletCallResponse(WalletApiOperation.ListKnownBankAccounts, undefined, {
- accounts: [ibanPayto]
+ accounts: [talerBankPayto, ibanPayto]
});
handler.addWalletCallResponse(WalletApiOperation.GetFeeForDeposit, undefined, withoutFee())
handler.addWalletCallResponse(WalletApiOperation.GetFeeForDeposit, undefined, withoutFee())
@@ -221,7 +221,7 @@ describe("DepositPage states", () => {
if (r.status !== "ready") expect.fail();
expect(r.cancelHandler.onClick).not.undefined;
expect(r.currency).eq(currency);
- expect(r.account.value).eq("");
+ expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(r.amount.value).eq("0");
expect(r.depositHandler.onClick).undefined;
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
@@ -328,7 +328,7 @@ describe("DepositPage states", () => {
}],
})
handler.addWalletCallResponse(WalletApiOperation.ListKnownBankAccounts, undefined, {
- accounts: [ibanPayto]
+ accounts: [talerBankPayto, ibanPayto]
});
handler.addWalletCallResponse(WalletApiOperation.GetFeeForDeposit, undefined, withSomeFee())
handler.addWalletCallResponse(WalletApiOperation.GetFeeForDeposit, undefined, withSomeFee())
@@ -353,7 +353,7 @@ describe("DepositPage states", () => {
if (r.status !== "ready") expect.fail();
expect(r.cancelHandler.onClick).not.undefined;
expect(r.currency).eq(currency);
- expect(r.account.value).eq("");
+ expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(r.amount.value).eq("0");
expect(r.depositHandler.onClick).undefined;
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
index ddb23c9bb..e864c8413 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
@@ -160,61 +160,55 @@ export function ReadyView(state: State.Ready): VNode {
variant="text"
style={{ marginLeft: "auto" }}
>
- <i18n.Translate>Add another account</i18n.Translate>
+ <i18n.Translate>Manage accounts</i18n.Translate>
</Button>
</div>
- {state.selectedAccount && (
- <Fragment>
- <p>
- <AccountDetails account={state.selectedAccount} />
- </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)}
- />
- </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)}
- />
- </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)}
- />
- </div>
- </InputWithLabel>
- </Fragment>
- )}
+ <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)}
+ />
+ </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)}
+ />
+ </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)}
+ />
+ </div>
+ </InputWithLabel>
</section>
<footer>
<Button
diff --git a/packages/taler-wallet-webextension/src/wallet/AddAccount/index.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
index 09609a8a1..cd591be74 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddAccount/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
@@ -14,6 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { KnownBankAccountsInfo } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js";
import {
@@ -57,17 +58,23 @@ export namespace State {
alias: TextFieldHandler;
onAccountAdded: ButtonHandler;
onCancel: ButtonHandler;
+ accountByType: AccountByType,
+ deleteAccount: (a: KnownBankAccountsInfo) => Promise<void>,
}
}
+export type AccountByType = {
+ [key: string]: KnownBankAccountsInfo[]
+};
+
const viewMapping: StateViewMap<State> = {
loading: Loading,
"loading-error": LoadingUriView,
ready: ReadyView,
};
-export const AddAccountPage = compose(
- "AddAccount",
+export const ManageAccountPage = compose(
+ "ManageAccountPage",
(p: Props) => useComponentState(p, wxApi),
viewMapping,
);
diff --git a/packages/taler-wallet-webextension/src/wallet/AddAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
index 6c113d732..ad8643133 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddAccount/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
@@ -14,12 +14,12 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { KnownBankAccountsInfo, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { wxApi } from "../../wxApi.js";
-import { Props, State } from "./index.js";
+import { AccountByType, Props, State } from "./index.js";
export function useComponentState(
{ currency, onAccountAdded, onCancel }: Props,
@@ -45,10 +45,10 @@ export function useComponentState(
}
const accountType: Record<string, string> = {
- "": "Choose one account",
+ "": "Choose one account type",
iban: "IBAN",
- bitcoin: "Bitcoin",
- "x-taler-bank": "Taler Bank",
+ // bitcoin: "Bitcoin",
+ // "x-taler-bank": "Taler Bank",
};
const uri = parsePaytoUri(payto);
const found =
@@ -73,6 +73,24 @@ export function useComponentState(
const unableToAdd = !type || !alias || !!paytoUriError || !uri;
+ const accountByType: AccountByType = {
+ iban: [],
+ bitcoin: [],
+ "x-taler-bank": [],
+ }
+
+ hook.response.accounts.forEach(acc => {
+ accountByType[acc.uri.targetType].push(acc)
+ });
+
+ async function deleteAccount(account: KnownBankAccountsInfo): Promise<void> {
+ const payto = stringifyPaytoUri(account.uri);
+ await api.wallet.call(WalletApiOperation.ForgetKnownBankAccounts, {
+ payto
+ })
+ hook?.retry()
+ }
+
return {
status: "ready",
error: undefined,
@@ -97,6 +115,8 @@ export function useComponentState(
setPayto(v);
},
},
+ accountByType,
+ deleteAccount,
onAccountAdded: {
onClick: unableToAdd ? undefined : addAccount,
},
diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx
new file mode 100644
index 000000000..c0d3a38b0
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx
@@ -0,0 +1,208 @@
+/*
+ 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 { ReadyView } from "./views.js";
+
+export default {
+ title: "wallet/manage account",
+};
+
+const nullFunction = async () => {
+ null;
+};
+
+export const JustTwoBitcoinAccounts = createExample(ReadyView, {
+ status: "ready",
+ currency: "ARS",
+ accountType: {
+ list: {
+ "": "Choose one account type",
+ iban: "IBAN",
+ // bitcoin: "Bitcoin",
+ // "x-taler-bank": "Taler Bank",
+ },
+ value: "",
+ },
+ alias: {
+ value: "",
+ onInput: nullFunction,
+ },
+ uri: {
+ value: "",
+ onInput: nullFunction,
+ },
+ accountByType: {
+ iban: [],
+ "x-taler-bank": [],
+ bitcoin: [
+ {
+ alias: "my bitcoin addr",
+ currency: "BTC",
+ kyc_completed: false,
+ uri: {
+ targetType: "bitcoin",
+ segwitAddrs: [],
+ isKnown: true,
+ targetPath: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
+ params: {},
+ },
+ },
+ {
+ alias: "my other addr",
+ currency: "BTC",
+ kyc_completed: true,
+ uri: {
+ targetType: "bitcoin",
+ segwitAddrs: [],
+ isKnown: true,
+ targetPath: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
+ params: {},
+ },
+ },
+ ],
+ },
+ onAccountAdded: {},
+ onCancel: {},
+});
+
+export const WithAllTypeOfAccounts = createExample(ReadyView, {
+ status: "ready",
+ currency: "ARS",
+ accountType: {
+ list: {
+ "": "Choose one account type",
+ iban: "IBAN",
+ // bitcoin: "Bitcoin",
+ // "x-taler-bank": "Taler Bank",
+ },
+ value: "",
+ },
+ alias: {
+ value: "",
+ onInput: nullFunction,
+ },
+ uri: {
+ value: "",
+ onInput: nullFunction,
+ },
+ accountByType: {
+ iban: [
+ {
+ alias: "my bank",
+ currency: "ARS",
+ kyc_completed: true,
+ uri: {
+ targetType: "iban",
+ iban: "ASDQWEQWE",
+ isKnown: true,
+ targetPath: "/ASDQWEQWE",
+ params: {},
+ },
+ },
+ ],
+ "x-taler-bank": [
+ {
+ alias: "my xtaler bank",
+ currency: "ARS",
+ kyc_completed: true,
+ uri: {
+ targetType: "x-taler-bank",
+ host: "localhost",
+ account: "123",
+ isKnown: true,
+ targetPath: "localhost/123",
+ params: {},
+ },
+ },
+ ],
+ bitcoin: [
+ {
+ alias: "my bitcoin addr",
+ currency: "BTC",
+ kyc_completed: false,
+ uri: {
+ targetType: "bitcoin",
+ segwitAddrs: [],
+ isKnown: true,
+ targetPath: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
+ params: {},
+ },
+ },
+ {
+ alias: "my other addr",
+ currency: "BTC",
+ kyc_completed: true,
+ uri: {
+ targetType: "bitcoin",
+ segwitAddrs: [],
+ isKnown: true,
+ targetPath: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
+ params: {},
+ },
+ },
+ ],
+ },
+ onAccountAdded: {},
+ onCancel: {},
+});
+
+export const AddingIbanAccount = createExample(ReadyView, {
+ status: "ready",
+ currency: "ARS",
+ accountType: {
+ list: {
+ "": "Choose one account type",
+ iban: "IBAN",
+ // bitcoin: "Bitcoin",
+ // "x-taler-bank": "Taler Bank",
+ },
+ value: "iban",
+ },
+ alias: {
+ value: "",
+ onInput: nullFunction,
+ },
+ uri: {
+ value: "",
+ onInput: nullFunction,
+ },
+ accountByType: {
+ iban: [
+ {
+ alias: "my bank",
+ currency: "ARS",
+ kyc_completed: true,
+ uri: {
+ targetType: "iban",
+ iban: "ASDQWEQWE",
+ isKnown: true,
+ targetPath: "/ASDQWEQWE",
+ params: {},
+ },
+ },
+ ],
+ "x-taler-bank": [],
+ bitcoin: [],
+ },
+ onAccountAdded: {},
+ onCancel: {},
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/AddAccount/test.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts
index eae4d4ca2..eae4d4ca2 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddAccount/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts
diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
new file mode 100644
index 000000000..9bb9e5814
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
@@ -0,0 +1,534 @@
+/*
+ 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 {
+ KnownBankAccountsInfo,
+ PaytoUriBitcoin,
+ PaytoUriIBAN,
+ PaytoUriTalerBank,
+} from "@gnu-taler/taler-util";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { ErrorMessage } from "../../components/ErrorMessage.js";
+import { LoadingError } from "../../components/LoadingError.js";
+import { SelectList } from "../../components/SelectList.js";
+import {
+ Input,
+ LightText,
+ SubTitle,
+ SvgIcon,
+ WarningText,
+} from "../../components/styled/index.js";
+import { useTranslationContext } from "../../context/translation.js";
+import { Button } from "../../mui/Button.js";
+import { TextFieldHandler } from "../../mui/handlers.js";
+import { TextField } from "../../mui/TextField.js";
+import checkIcon from "../../svg/check_24px.svg";
+import warningIcon from "../../svg/warning_24px.svg";
+import deleteIcon from "../../svg/delete_24px.svg";
+import { State } from "./index.js";
+
+type AccountType = "bitcoin" | "x-taler-bank" | "iban";
+type ComponentFormByAccountType = {
+ [type in AccountType]: (props: { field: TextFieldHandler }) => VNode;
+};
+
+type ComponentListByAccountType = {
+ [type in AccountType]: (props: {
+ list: KnownBankAccountsInfo[];
+ onDelete: (a: KnownBankAccountsInfo) => Promise<void>;
+ }) => VNode;
+};
+
+const formComponentByAccountType: ComponentFormByAccountType = {
+ iban: IbanAddressAccount,
+ bitcoin: BitcoinAddressAccount,
+ "x-taler-bank": TalerBankAddressAccount,
+};
+const tableComponentByAccountType: ComponentListByAccountType = {
+ iban: IbanTable,
+ bitcoin: BitcoinTable,
+ "x-taler-bank": TalerBankTable,
+};
+
+const AccountTable = styled.table`
+ width: 100%;
+
+ border-collapse: separate;
+ border-spacing: 0px 10px;
+ tbody tr:nth-child(odd) > td:not(.actions, .kyc) {
+ background-color: lightgrey;
+ }
+ .actions,
+ .kyc {
+ width: 10px;
+ background-color: inherit;
+ }
+`;
+
+export function LoadingUriView({ error }: State.LoadingUriError): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <LoadingError
+ title={<i18n.Translate>Could not load</i18n.Translate>}
+ error={error}
+ />
+ );
+}
+
+export function ReadyView({
+ currency,
+ error,
+ accountType,
+ accountByType,
+ alias,
+ onAccountAdded,
+ deleteAccount,
+ onCancel,
+ uri,
+}: State.Ready): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Fragment>
+ <section>
+ <SubTitle>
+ <i18n.Translate>Known accounts for {currency}</i18n.Translate>
+ </SubTitle>
+ <p>
+ <i18n.Translate>
+ To add a new account first select the account type.
+ </i18n.Translate>
+ </p>
+
+ {error && (
+ <ErrorMessage
+ title={<i18n.Translate>Unable add this account</i18n.Translate>}
+ description={error}
+ />
+ )}
+ <p>
+ <Input>
+ <SelectList
+ label={<i18n.Translate>Select account type</i18n.Translate>}
+ list={accountType.list}
+ name="accountType"
+ value={accountType.value}
+ onChange={accountType.onChange}
+ />
+ </Input>
+ </p>
+ {accountType.value === "" ? undefined : (
+ <Fragment>
+ <p>
+ <CustomFieldByAccountType
+ type={accountType.value as AccountType}
+ field={uri}
+ />
+ </p>
+ <p>
+ <TextField
+ label="Account alias"
+ variant="standard"
+ required
+ fullWidth
+ disabled={accountType.value === ""}
+ value={alias.value}
+ onChange={alias.onInput}
+ />
+ </p>
+ </Fragment>
+ )}
+ </section>
+ <section>
+ <Button
+ variant="contained"
+ color="secondary"
+ onClick={onCancel.onClick}
+ >
+ <i18n.Translate>Cancel</i18n.Translate>
+ </Button>
+ <Button
+ variant="contained"
+ onClick={onAccountAdded.onClick}
+ disabled={!onAccountAdded.onClick}
+ >
+ <i18n.Translate>Add</i18n.Translate>
+ </Button>
+ </section>
+ <section>
+ {Object.entries(accountByType).map(([type, list]) => {
+ const Table = tableComponentByAccountType[type as AccountType];
+ return <Table key={type} list={list} onDelete={deleteAccount} />;
+ })}
+ </section>
+ </Fragment>
+ );
+}
+
+function IbanTable({
+ list,
+ onDelete,
+}: {
+ list: KnownBankAccountsInfo[];
+ onDelete: (ac: KnownBankAccountsInfo) => void;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ if (list.length === 0) return <Fragment />;
+ return (
+ <div>
+ <h1>
+ <i18n.Translate>IBAN accounts</i18n.Translate>
+ </h1>
+ <AccountTable>
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Alias</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Int. Account Number</i18n.Translate>
+ </th>
+ <th class="kyc">
+ <i18n.Translate>KYC</i18n.Translate>
+ </th>
+ <th class="actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ {list.map((account) => {
+ const p = account.uri as PaytoUriIBAN;
+ return (
+ <tr key={account.alias}>
+ <td>{account.alias}</td>
+ <td>{p.targetPath}</td>
+ <td class="kyc">
+ {account.kyc_completed ? (
+ <SvgIcon
+ title={i18n.str`KYC done`}
+ dangerouslySetInnerHTML={{ __html: checkIcon }}
+ color="green"
+ />
+ ) : (
+ <SvgIcon
+ title={i18n.str`KYC missing`}
+ dangerouslySetInnerHTML={{ __html: warningIcon }}
+ color="orange"
+ />
+ )}
+ </td>
+ <td class="actions">
+ <Button
+ variant="outlined"
+ startIcon={deleteIcon}
+ size="small"
+ onClick={async () => onDelete(account)}
+ color="error"
+ >
+ Forget
+ </Button>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </AccountTable>
+ </div>
+ );
+}
+
+function TalerBankTable({
+ list,
+ onDelete,
+}: {
+ list: KnownBankAccountsInfo[];
+ onDelete: (ac: KnownBankAccountsInfo) => void;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ if (list.length === 0) return <Fragment />;
+ return (
+ <div>
+ <h1>
+ <i18n.Translate>Taler accounts</i18n.Translate>
+ </h1>
+ <AccountTable>
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Alias</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Host</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Account</i18n.Translate>
+ </th>
+ <th class="kyc">
+ <i18n.Translate>KYC</i18n.Translate>
+ </th>
+ <th class="actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ {list.map((account) => {
+ const p = account.uri as PaytoUriTalerBank;
+ return (
+ <tr key={account.alias}>
+ <td>{account.alias}</td>
+ <td>{p.host}</td>
+ <td>{p.account}</td>
+ <td class="kyc">
+ {account.kyc_completed ? (
+ <SvgIcon
+ title={i18n.str`KYC done`}
+ dangerouslySetInnerHTML={{ __html: checkIcon }}
+ color="green"
+ />
+ ) : (
+ <SvgIcon
+ title={i18n.str`KYC missing`}
+ dangerouslySetInnerHTML={{ __html: warningIcon }}
+ color="orange"
+ />
+ )}
+ </td>
+ <td class="actions">
+ <Button
+ variant="outlined"
+ startIcon={deleteIcon}
+ size="small"
+ onClick={async () => onDelete(account)}
+ color="error"
+ >
+ Forget
+ </Button>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </AccountTable>
+ </div>
+ );
+}
+
+function BitcoinTable({
+ list,
+ onDelete,
+}: {
+ list: KnownBankAccountsInfo[];
+ onDelete: (ac: KnownBankAccountsInfo) => void;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ if (list.length === 0) return <Fragment />;
+ return (
+ <div>
+ <h2>
+ <i18n.Translate>Bitcoin accounts</i18n.Translate>
+ </h2>
+ <AccountTable>
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Alias</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Address</i18n.Translate>
+ </th>
+ <th class="kyc">
+ <i18n.Translate>KYC</i18n.Translate>
+ </th>
+ <th class="actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ {list.map((account) => {
+ const p = account.uri as PaytoUriBitcoin;
+ return (
+ <tr key={account.alias}>
+ <td>{account.alias}</td>
+ <td>{p.targetPath}</td>
+ <td class="kyc">
+ {account.kyc_completed ? (
+ <SvgIcon
+ title={i18n.str`KYC done`}
+ dangerouslySetInnerHTML={{ __html: checkIcon }}
+ color="green"
+ />
+ ) : (
+ <SvgIcon
+ title={i18n.str`KYC missing`}
+ dangerouslySetInnerHTML={{ __html: warningIcon }}
+ color="orange"
+ />
+ )}
+ </td>
+ <td class="actions">
+ <Button
+ variant="outlined"
+ startIcon={deleteIcon}
+ size="small"
+ onClick={async () => onDelete(account)}
+ color="error"
+ >
+ Forget
+ </Button>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </AccountTable>
+ </div>
+ );
+}
+
+function BitcoinAddressAccount({ field }: { field: TextFieldHandler }): VNode {
+ const { i18n } = useTranslationContext();
+ const [value, setValue] = useState<string | undefined>(undefined);
+ const errors = undefinedIfEmpty({
+ value: !value ? i18n.str`Can't be empty` : undefined,
+ });
+ return (
+ <Fragment>
+ <TextField
+ label="Bitcoin address"
+ variant="standard"
+ fullWidth
+ value={value}
+ error={value !== undefined && !!errors?.value}
+ onChange={(v) => {
+ setValue(v);
+ if (!errors) {
+ field.onInput(`payto://bitcoin/${v}`);
+ }
+ }}
+ />
+ {value !== undefined && errors?.value && (
+ <ErrorMessage title={<span>{errors?.value}</span>} />
+ )}
+ </Fragment>
+ );
+}
+
+function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
+ return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+ ? obj
+ : undefined;
+}
+
+function TalerBankAddressAccount({
+ field,
+}: {
+ field: TextFieldHandler;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ const [host, setHost] = useState<string | undefined>(undefined);
+ const [account, setAccount] = useState<string | undefined>(undefined);
+ const errors = undefinedIfEmpty({
+ host: !host ? i18n.str`Can't be empty` : undefined,
+ account: !account ? i18n.str`Can't be empty` : undefined,
+ });
+ return (
+ <Fragment>
+ <TextField
+ label="Bank host"
+ variant="standard"
+ fullWidth
+ value={host}
+ error={host !== undefined && !!errors?.host}
+ onChange={(v) => {
+ setHost(v);
+ if (!errors) {
+ field.onInput(`payto://x-taler-bank/${v}/${account}`);
+ }
+ }}
+ />{" "}
+ {host !== undefined && errors?.host && (
+ <ErrorMessage title={<span>{errors?.host}</span>} />
+ )}
+ <TextField
+ label="Bank account"
+ variant="standard"
+ fullWidth
+ value={account}
+ error={account !== undefined && !!errors?.account}
+ onChange={(v) => {
+ setAccount(v || "");
+ if (!errors) {
+ field.onInput(`payto://x-taler-bank/${host}/${v}`);
+ }
+ }}
+ />{" "}
+ {account !== undefined && errors?.account && (
+ <ErrorMessage title={<span>{errors?.account}</span>} />
+ )}
+ </Fragment>
+ );
+}
+
+function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode {
+ const { i18n } = useTranslationContext();
+ const [value, setValue] = useState<string | undefined>(undefined);
+ const errors = undefinedIfEmpty({
+ value: !value ? i18n.str`Can't be empty` : undefined,
+ });
+ return (
+ <Fragment>
+ <TextField
+ label="IBAN number"
+ variant="standard"
+ fullWidth
+ value={value}
+ error={value !== undefined && !!errors?.value}
+ onChange={(v) => {
+ setValue(v);
+ if (!errors) {
+ field.onInput(`payto://iban/${v}`);
+ }
+ }}
+ />
+ {value !== undefined && errors?.value && (
+ <ErrorMessage title={<span>{errors?.value}</span>} />
+ )}
+ </Fragment>
+ );
+}
+
+function CustomFieldByAccountType({
+ type,
+ field,
+}: {
+ type: AccountType;
+ field: TextFieldHandler;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const AccountForm = formComponentByAccountType[type];
+
+ return (
+ <div>
+ <WarningText>
+ <i18n.Translate>
+ We can not validate the account so make sure the value is correct.
+ </i18n.Translate>
+ </WarningText>
+ <AccountForm field={field} />
+ </div>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
index c2f0c6481..d63f25ead 100644
--- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
@@ -37,6 +37,7 @@ import * as a16 from "./DeveloperPage.stories.js";
import * as a17 from "./QrReader.stories.js";
import * as a18 from "./DestinationSelection.stories.js";
import * as a19 from "./ExchangeSelection/stories.js";
+import * as a20 from "./ManageAccount/stories.js";
export default [
a1,
@@ -57,4 +58,5 @@ export default [
a17,
a18,
a19,
+ a20,
];