/* 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 */ import { buildPayto, KnownBankAccountsInfo, PaytoUriBitcoin, PaytoUriIBAN, PaytoUriTalerBank, stringifyPaytoUri, } 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 { SelectList } from "../../components/SelectList.js"; import { Input, 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 deleteIcon from "../../svg/delete_24px.svg"; import warningIcon from "../../svg/warning_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; }) => 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 ReadyView({ currency, error, accountType, accountByType, alias, onAccountAdded, deleteAccount, onCancel, uri, }: State.Ready): VNode { const { i18n } = useTranslationContext(); return (
Known accounts for {currency}

To add a new account first select the account type.

{error && ( )}

{accountType.value === "" ? undefined : (

)}
{Object.entries(accountByType).map(([type, list]) => { const Table = tableComponentByAccountType[type as AccountType]; return ; })} ); } function IbanTable({ list, onDelete, }: { list: KnownBankAccountsInfo[]; onDelete: (ac: KnownBankAccountsInfo) => void; }): VNode { const { i18n } = useTranslationContext(); if (list.length === 0) return ; return (

IBAN accounts

{list.map((account) => { const p = account.uri as PaytoUriIBAN; return ( ); })} ); } function TalerBankTable({ list, onDelete, }: { list: KnownBankAccountsInfo[]; onDelete: (ac: KnownBankAccountsInfo) => void; }): VNode { const { i18n } = useTranslationContext(); if (list.length === 0) return ; return (

Taler accounts

{list.map((account) => { const p = account.uri as PaytoUriTalerBank; return ( ); })} ); } function BitcoinTable({ list, onDelete, }: { list: KnownBankAccountsInfo[]; onDelete: (ac: KnownBankAccountsInfo) => void; }): VNode { const { i18n } = useTranslationContext(); if (list.length === 0) return ; return (

Bitcoin accounts

{list.map((account) => { const p = account.uri as PaytoUriBitcoin; return ( ); })} ); } function BitcoinAddressAccount({ field }: { field: TextFieldHandler }): VNode { const { i18n } = useTranslationContext(); const [value, setValue] = useState(undefined); const errors = undefinedIfEmpty({ value: !value ? i18n.str`Can't be empty` : undefined, }); return ( { setValue(v); if (!errors && field.onInput) { const p = buildPayto("bitcoin", v, undefined); field.onInput(stringifyPaytoUri(p)); } }} /> ); } function undefinedIfEmpty(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(undefined); const [account, setAccount] = useState(undefined); const errors = undefinedIfEmpty({ host: !host ? i18n.str`Can't be empty` : undefined, account: !account ? i18n.str`Can't be empty` : undefined, }); return ( { setHost(v); if (!errors && field.onInput && account) { const p = buildPayto("x-taler-bank", v, account); field.onInput(stringifyPaytoUri(p)); } }} /> { setAccount(v || ""); if (!errors && field.onInput && host) { const p = buildPayto("x-taler-bank", host, v); field.onInput(stringifyPaytoUri(p)); } }} /> ); } //Taken from libeufin and libeufin took it from the ISO20022 XSD schema const bicRegex = /^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/; const ibanRegex = /^[A-Z]{2}[0-9]{2}[a-zA-Z0-9]{1,30}$/; function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode { const { i18n } = useTranslationContext(); const [bic, setBic] = useState(undefined); const [iban, setIban] = useState(undefined); const [name, setName] = useState(undefined); const errors = undefinedIfEmpty({ bic: !bic ? undefined : !bicRegex.test(bic) ? i18n.str`Invalid bic` : undefined, iban: !iban ? i18n.str`Can't be empty` : !ibanRegex.test(iban) ? i18n.str`Invalid iban` : undefined, name: !name ? i18n.str`Can't be empty` : undefined, }); function sendUpdateIfNoErrors( bic: string | undefined, iban: string, name: string, ): void { if (!errors && field.onInput) { const p = buildPayto("iban", iban, bic); p.params["receiver-name"] = name; field.onInput(stringifyPaytoUri(p)); } } return (

{ setBic(v); sendUpdateIfNoErrors(v, iban || "", name || ""); }} />

{ setIban(v); sendUpdateIfNoErrors(bic, v, name || ""); }} />

{ setName(v); sendUpdateIfNoErrors(bic, iban || "", v); }} />

); } function CustomFieldByAccountType({ type, field, }: { type: AccountType; field: TextFieldHandler; }): VNode { const { i18n } = useTranslationContext(); const AccountForm = formComponentByAccountType[type]; return (
We can not validate the account so make sure the value is correct.
); }
Alias Bank Id Int. Account Number Account name KYC
{account.alias} {p.bic} {p.iban} {p.params["receiver-name"]} {account.kyc_completed ? ( ) : ( )}
Alias Host Account KYC
{account.alias} {p.host} {p.account} {account.kyc_completed ? ( ) : ( )}
Alias Address KYC
{account.alias} {p.targetPath} {account.kyc_completed ? ( ) : ( )}