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.tsx | |
parent | fbf050267244b72afb193e6ab80ea485e0eaf309 (diff) | |
download | wallet-core-9811e19252ef859099fa5c16d703808f6c778a94.tar.xz |
new deposit page
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DepositPage.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/wallet/DepositPage.tsx | 404 |
1 files changed, 0 insertions, 404 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx deleted file mode 100644 index 69249a716..000000000 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx +++ /dev/null @@ -1,404 +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 { AmountJson, Amounts, DepositGroupFees, PaytoUri } from "@gnu-taler/taler-util"; -import { Fragment, h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { Loading } from "../components/Loading.js"; -import { LoadingError } from "../components/LoadingError.js"; -import { SelectList } from "../components/SelectList.js"; -import { - ErrorText, - Input, - InputWithLabel, - SubTitle, - WarningBox, -} from "../components/styled/index.js"; -import { useTranslationContext } from "../context/translation.js"; -import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; -import { Button } from "../mui/Button.js"; -import { - ButtonHandler, - SelectFieldHandler, - TextFieldHandler, -} from "../mui/handlers.js"; -import * as wxApi from "../wxApi.js"; - -interface Props { - amount: string; - onCancel: (currency: string) => void; - onSuccess: (currency: string) => void; -} -export function DepositPage({ amount, onCancel, onSuccess }: Props): VNode { - const state = useComponentState(amount, onCancel, onSuccess, wxApi); - - return <View state={state} />; -} - -interface ViewProps { - state: State; -} - -type State = Loading | NoBalanceState | NoAccountsState | DepositState; - -interface Loading { - status: "loading"; - hook: HookError | undefined; -} - -interface NoBalanceState { - status: "no-balance"; -} -interface NoAccountsState { - status: "no-accounts"; - cancelHandler: ButtonHandler; -} -interface DepositState { - status: "ready"; - currency: string; - amount: TextFieldHandler; - account: SelectFieldHandler; - totalFee: AmountJson; - totalToDeposit: AmountJson; - // currentAccount: PaytoUri; - // parsedAmount: AmountJson | undefined; - cancelHandler: ButtonHandler; - depositHandler: ButtonHandler; -} - -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 useComponentState( - amountOrCurrency: string, - onCancel: (currency: string) => void, - onSuccess: (currency: string) => void, - api: typeof wxApi, -): State { - const parsed = Amounts.parse(amountOrCurrency); - const currency = parsed !== undefined ? parsed.currency : amountOrCurrency; - - const hook = useAsyncAsHook(async () => { - const { balances } = await api.getBalance(); - const { accounts: accountMap } = await api.listKnownBankAccounts(currency); - const accounts = Object.values(accountMap); - const defaultSelectedAccount = - accounts.length > 0 ? accounts[0] : undefined; - return { accounts, balances, defaultSelectedAccount }; - }); - - const initialValue = - parsed !== undefined ? Amounts.stringifyValue(parsed) : "0"; - const [accountIdx, setAccountIdx] = useState(0); - const [amount, setAmount] = useState(initialValue); - - const [selectedAccount, setSelectedAccount] = useState< - PaytoUri | undefined - >(); - - const parsedAmount = Amounts.parse(`${currency}:${amount}`); - - const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined); - - if (!hook || hook.hasError) { - return { - status: "loading", - hook, - }; - } - - const { accounts, balances, defaultSelectedAccount } = hook.response; - const currentAccount = selectedAccount ?? defaultSelectedAccount; - - 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-balance", - }; - } - - if (!currentAccount) { - return { - status: "no-accounts", - cancelHandler: { - onClick: async () => { - onCancel(currency); - }, - }, - }; - } - const accountMap = createLabelsForBankAccount(accounts); - - async function updateAccount(accountStr: string): Promise<void> { - const idx = parseInt(accountStr, 10); - const newSelected = accounts.length > idx ? accounts[idx] : undefined; - if (accountIdx === idx || !newSelected) return; - - if (!parsedAmount) { - setAccountIdx(idx); - setSelectedAccount(newSelected); - } else { - const result = await getFeeForAmount(newSelected, parsedAmount, api); - setAccountIdx(idx); - setSelectedAccount(newSelected); - setFee(result); - } - } - - async function updateAmount(numStr: string): Promise<void> { - // const num = parseFloat(numStr); - // const newAmount = Number.isNaN(num) ? 0 : num; - if (amount === numStr || !currentAccount) return; - const parsed = Amounts.parse(`${currency}:${numStr}`); - if (!parsed) { - setAmount(numStr); - } else { - const result = await getFeeForAmount(currentAccount, parsed, api); - setAmount(numStr); - setFee(result); - } - } - - const totalFee = - fee !== undefined - ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount - : Amounts.getZero(currency); - - const totalToDeposit = parsedAmount - ? 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 || - Amounts.isZero(totalToDeposit) || - fee === undefined || - amountError !== undefined; - - async function doSend(): Promise<void> { - if (!currentAccount || !parsedAmount) return; - - const account = `payto://${currentAccount.targetType}/${currentAccount.targetPath}`; - const amount = Amounts.stringify(parsedAmount); - await api.createDepositGroup(account, amount); - onSuccess(currency); - } - - return { - status: "ready", - currency, - amount: { - value: String(amount), - onInput: updateAmount, - error: amountError, - }, - account: { - list: accountMap, - value: String(accountIdx), - onChange: updateAccount, - }, - cancelHandler: { - onClick: async () => { - onCancel(currency); - }, - }, - depositHandler: { - onClick: unableToDeposit ? undefined : doSend, - }, - totalFee, - totalToDeposit, - // currentAccount, - // parsedAmount, - }; -} - -export function View({ state }: ViewProps): VNode { - const { i18n } = useTranslationContext(); - - if (state === undefined) return <Loading />; - - if (state.status === "loading") { - if (!state.hook) return <Loading />; - return ( - <LoadingError - title={<i18n.Translate>Could not load deposit balance</i18n.Translate>} - error={state.hook} - /> - ); - } - - if (state.status === "no-balance") { - return ( - <div> - <i18n.Translate>no balance</i18n.Translate> - </div> - ); - } - if (state.status === "no-accounts") { - return ( - <Fragment> - <WarningBox> - <p> - <i18n.Translate> - There is no known bank account to send money to - </i18n.Translate> - </p> - </WarningBox> - <footer> - <Button - variant="contained" - color="secondary" - onClick={state.cancelHandler.onClick} - > - <i18n.Translate>Cancel</i18n.Translate> - </Button> - </footer> - </Fragment> - ); - } - - return ( - <Fragment> - <SubTitle> - <i18n.Translate>Send {state.currency} to your account</i18n.Translate> - </SubTitle> - <section> - <Input> - <SelectList - label={<i18n.Translate>Bank account IBAN number</i18n.Translate>} - list={state.account.list} - name="account" - value={state.account.value} - onChange={state.account.onChange} - /> - </Input> - <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> - { - <Fragment> - <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> - } - </section> - <footer> - <Button - variant="contained" - color="secondary" - onClick={state.cancelHandler.onClick} - > - <i18n.Translate>Cancel</i18n.Translate> - </Button> - {!state.depositHandler.onClick ? ( - <Button variant="contained" disabled> - <i18n.Translate>Deposit</i18n.Translate> - </Button> - ) : ( - <Button variant="contained" onClick={state.depositHandler.onClick}> - <i18n.Translate> - Deposit {Amounts.stringifyValue(state.totalToDeposit)}{" "} - {state.currency} - </i18n.Translate> - </Button> - )} - </footer> - </Fragment> - ); -} - -export function createLabelsForBankAccount( - knownBankAccounts: Array<PaytoUri>, -): { - [label: number]: string; -} { - if (!knownBankAccounts) return {}; - return knownBankAccounts.reduce((prev, cur, i) => { - let label = cur.targetPath; - if (cur.isKnown) { - switch (cur.targetType) { - case "x-taler-bank": { - label = cur.account; - break; - } - case "iban": { - label = cur.iban; - break; - } - } - } - return { - ...prev, - [i]: label, - }; - }, {} as { [label: number]: string }); -} |