aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-09-23 15:18:18 -0300
committerSebastian <sebasjm@gmail.com>2022-09-23 15:18:50 -0300
commit9811e19252ef859099fa5c16d703808f6c778a94 (patch)
treebc22a113f3a5c69c6b6883d1e6445697c5eb63af /packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
parentfbf050267244b72afb193e6ab80ea485e0eaf309 (diff)
downloadwallet-core-9811e19252ef859099fa5c16d703808f6c778a94.tar.xz
new deposit page
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts240
1 files changed, 240 insertions, 0 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
new file mode 100644
index 000000000..87705507c
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -0,0 +1,240 @@
+/*
+ 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";
+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 {
+ 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
+ }
+ }
+
+ if (!hook) {
+ return {
+ status: "loading",
+ error: undefined,
+ };
+ }
+ if (hook.hasError) {
+ return {
+ status: "loading-error",
+ error: hook,
+ }
+ }
+ 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()
+ },
+ onCancel: () => {
+ setAddingAccount(false);
+ }
+ ,
+ }
+ }
+
+ 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) }
+ },
+ }
+ }
+
+ 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);
+ 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);
+
+ 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;
+
+ 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) }
+ },
+ 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;
+ }
+}
+
+export function createLabelsForBankAccount(
+ knownBankAccounts: Array<KnownBankAccountsInfo>,
+): { [value: string]: string } {
+ const initialList: Record<string, string> = {
+ }
+ if (!knownBankAccounts.length) return initialList;
+ return knownBankAccounts.reduce((prev, cur, i) => {
+ prev[stringifyPaytoUri(cur.uri)] = cur.alias
+ return prev;
+ }, initialList);
+}