wallet-core/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts

259 lines
6.8 KiB
TypeScript
Raw Normal View History

2022-09-23 20:18:18 +02:00
/*
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 {
AmountJson,
Amounts,
DepositGroupFees,
KnownBankAccountsInfo,
parsePaytoUri,
PaytoUri,
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
2022-09-23 20:18:18 +02:00
import { useState } from "preact/hooks";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import * as wxApi from "../../wxApi.js";
import { Props, State } from "./index.js";
export function useComponentState(
{ amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props,
api: typeof wxApi,
): State {
2022-09-23 20:18:18 +02:00
const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
const currency = parsed !== undefined ? parsed.currency : currencyStr;
const hook = useAsyncAsHook(async () => {
const { balances } = await api.getBalance();
const { accounts } = await api.listKnownBankAccounts(currency);
return { accounts, balances };
});
const initialValue =
parsed !== undefined ? Amounts.stringifyValue(parsed) : "0";
// const [accountIdx, setAccountIdx] = useState<number>(0);
const [amount, setAmount] = useState(initialValue);
const [selectedAccount, setSelectedAccount] = useState<
PaytoUri | undefined
>();
const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
const [addingAccount, setAddingAccount] = useState(false);
if (!currency) {
return {
status: "amount-or-currency-error",
error: undefined,
};
2022-09-23 20:18:18 +02:00
}
if (!hook) {
return {
status: "loading",
error: undefined,
};
}
if (hook.hasError) {
return {
status: "loading-error",
error: hook,
};
2022-09-23 20:18:18 +02:00
}
const { accounts, balances } = hook.response;
const parsedAmount = Amounts.parse(`${currency}:${amount}`);
if (addingAccount) {
return {
status: "adding-account",
error: undefined,
currency,
onAccountAdded: (p: string) => {
updateAccountFromList(p);
setAddingAccount(false);
hook.retry();
2022-09-23 20:18:18 +02:00
},
onCancel: () => {
setAddingAccount(false);
},
};
2022-09-23 20:18:18 +02:00
}
const bs = balances.filter((b) => b.available.startsWith(currency));
const balance =
bs.length > 0
? Amounts.parseOrThrow(bs[0].available)
: Amounts.getZero(currency);
if (Amounts.isZero(balance)) {
return {
status: "no-enough-balance",
error: undefined,
currency,
};
}
if (accounts.length === 0) {
return {
status: "no-accounts",
error: undefined,
currency,
onAddAccount: {
onClick: async () => {
setAddingAccount(true);
},
2022-09-23 20:18:18 +02:00
},
};
2022-09-23 20:18:18 +02:00
}
const accountMap = createLabelsForBankAccount(accounts);
accountMap[""] = "Select one account...";
2022-09-23 20:18:18 +02:00
async function updateAccountFromList(accountStr: string): Promise<void> {
// const newSelected = !accountMap[accountStr] ? undefined : accountMap[accountStr];
// if (!newSelected) return;
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
setSelectedAccount(uri);
if (uri && parsedAmount) {
try {
const result = await getFeeForAmount(uri, parsedAmount, api);
setFee(result);
} catch (e) {
setFee(undefined);
}
}
}
async function updateAmount(numStr: string): Promise<void> {
setAmount(numStr);
const parsed = Amounts.parse(`${currency}:${numStr}`);
if (parsed && selectedAccount) {
try {
const result = await getFeeForAmount(selectedAccount, parsed, api);
setFee(result);
} catch (e) {
setFee(undefined);
}
}
}
const totalFee =
fee !== undefined
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
: Amounts.getZero(currency);
const totalToDeposit =
parsedAmount && fee !== undefined
? Amounts.sub(parsedAmount, totalFee).amount
: Amounts.getZero(currency);
2022-09-23 20:18:18 +02:00
const isDirty = amount !== initialValue;
const amountError = !isDirty
? undefined
: !parsedAmount
? "Invalid amount"
: Amounts.cmp(balance, parsedAmount) === -1
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
: undefined;
2022-09-23 20:18:18 +02:00
const unableToDeposit =
!parsedAmount ||
selectedAccount === undefined ||
Amounts.isZero(totalToDeposit) ||
fee === undefined ||
amountError !== undefined;
async function doSend(): Promise<void> {
if (!selectedAccount || !parsedAmount || !currency) return;
const account = `payto://${selectedAccount.targetType}/${selectedAccount.targetPath}`;
const amount = Amounts.stringify(parsedAmount);
await api.createDepositGroup(account, amount);
onSuccess(currency);
}
return {
status: "ready",
error: undefined,
currency,
amount: {
value: String(amount),
onInput: updateAmount,
error: amountError,
},
onAddAccount: {
onClick: async () => {
setAddingAccount(true);
},
2022-09-23 20:18:18 +02:00
},
account: {
list: accountMap,
value: !selectedAccount ? "" : stringifyPaytoUri(selectedAccount),
onChange: updateAccountFromList,
},
selectedAccount,
cancelHandler: {
onClick: async () => {
onCancel(currency);
},
},
depositHandler: {
onClick: unableToDeposit ? undefined : doSend,
},
totalFee,
totalToDeposit,
// currentAccount,
// parsedAmount,
};
}
async function getFeeForAmount(
p: PaytoUri,
a: AmountJson,
api: typeof wxApi,
): Promise<DepositGroupFees> {
const account = `payto://${p.targetType}/${p.targetPath}`;
const amount = Amounts.stringify(a);
return await api.getFeeForDeposit(account, amount);
}
export function labelForAccountType(id: string) {
switch (id) {
case "":
return "Choose one";
case "x-taler-bank":
return "Taler Bank";
case "bitcoin":
return "Bitcoin";
case "iban":
return "IBAN";
default:
return id;
2022-09-23 20:18:18 +02:00
}
}
export function createLabelsForBankAccount(
knownBankAccounts: Array<KnownBankAccountsInfo>,
): { [value: string]: string } {
const initialList: Record<string, string> = {};
2022-09-23 20:18:18 +02:00
if (!knownBankAccounts.length) return initialList;
return knownBankAccounts.reduce((prev, cur, i) => {
prev[stringifyPaytoUri(cur.uri)] = cur.alias;
2022-09-23 20:18:18 +02:00
return prev;
}, initialList);
}