diff options
author | Sebastian <sebasjm@gmail.com> | 2022-09-23 15:18:18 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2022-09-23 15:18:50 -0300 |
commit | 9811e19252ef859099fa5c16d703808f6c778a94 (patch) | |
tree | bc22a113f3a5c69c6b6883d1e6445697c5eb63af /packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts | |
parent | fbf050267244b72afb193e6ab80ea485e0eaf309 (diff) | |
download | wallet-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.ts | 240 |
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); +} |