diff options
author | Sebastian <sebasjm@gmail.com> | 2021-12-23 15:17:36 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-12-23 15:17:36 -0300 |
commit | 2e71117f59e0ae6106930e705ae6a54a9839281b (patch) | |
tree | a39856486a2801f56c65de245c871ce596f8ab16 /packages/taler-wallet-webextension/src/wallet | |
parent | b8200de6f6c5ab9be3ff9f556c8acda013e574c3 (diff) | |
download | wallet-core-2e71117f59e0ae6106930e705ae6a54a9839281b.tar.xz |
deposit from wallet webex: wip
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
4 files changed, 318 insertions, 20 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx index 0a8910646..52edbbe51 100644 --- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx @@ -24,7 +24,9 @@ import * as wxApi from "../wxApi"; export function BalancePage({ goToWalletManualWithdraw, + goToWalletDeposit, }: { + goToWalletDeposit: (currency: string) => void; goToWalletManualWithdraw: () => void; }): VNode { const state = useAsyncAsHook(wxApi.getBalance); @@ -33,6 +35,7 @@ export function BalancePage({ balance={state} Linker={PageLink} goToWalletManualWithdraw={goToWalletManualWithdraw} + goToWalletDeposit={goToWalletDeposit} /> ); } @@ -41,12 +44,14 @@ export interface BalanceViewProps { balance: HookResponse<BalancesResponse>; Linker: typeof PageLink; goToWalletManualWithdraw: () => void; + goToWalletDeposit: (currency: string) => void; } export function BalanceView({ balance, Linker, goToWalletManualWithdraw, + goToWalletDeposit, }: BalanceViewProps): VNode { if (!balance) { return <div>Loading...</div>; @@ -65,28 +70,35 @@ export function BalanceView({ } if (balance.response.balances.length === 0) { return ( - <p> - <Centered style={{ marginTop: 100 }}> - <i18n.Translate> - You have no balance to show. Need some{" "} - <Linker pageName="/welcome">help</Linker> getting started? - </i18n.Translate> - <div> - <ButtonPrimary onClick={goToWalletManualWithdraw}> - Withdraw - </ButtonPrimary> - </div> - </Centered> - </p> + <Fragment> + <p> + <Centered style={{ marginTop: 100 }}> + <i18n.Translate> + You have no balance to show. Need some{" "} + <Linker pageName="/welcome">help</Linker> getting started? + </i18n.Translate> + </Centered> + </p> + <footer style={{ justifyContent: "space-between" }}> + <div /> + <ButtonPrimary onClick={goToWalletManualWithdraw}> + Withdraw + </ButtonPrimary> + </footer> + </Fragment> ); } return ( <Fragment> <section> - <BalanceTable balances={balance.response.balances} /> + <BalanceTable + balances={balance.response.balances} + goToWalletDeposit={goToWalletDeposit} + /> </section> - <footer style={{ justifyContent: "space-around" }}> + <footer style={{ justifyContent: "space-between" }}> + <div /> <ButtonPrimary onClick={goToWalletManualWithdraw}> Withdraw </ButtonPrimary> diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx new file mode 100644 index 000000000..346b85d4f --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx @@ -0,0 +1,52 @@ +/* + This file is part of GNU Taler + (C) 2021 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/> + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { AmountJson, Amounts, parsePaytoUri } from "@gnu-taler/taler-util"; +import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; +import { createExample } from "../test-utils"; +import { View as TestedComponent } from "./DepositPage"; + +export default { + title: "wallet/deposit", + component: TestedComponent, + argTypes: {}, +}; + +async function alwaysReturnFeeToOne(): Promise<DepositFee> { + const fee = { + currency: "EUR", + value: 1, + fraction: 0, + }; + return { coin: fee, refresh: fee, wire: fee }; +} + +export const WithEmptyAccountList = createExample(TestedComponent, { + knownBankAccounts: [], + balance: Amounts.parseOrThrow("USD:10"), + onCalculateFee: alwaysReturnFeeToOne, +}); + +export const WithSomeBankAccounts = createExample(TestedComponent, { + knownBankAccounts: [parsePaytoUri("payto://iban/ES8877998399652238")!], + balance: Amounts.parseOrThrow("EUR:10"), + onCalculateFee: alwaysReturnFeeToOne, +}); diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx new file mode 100644 index 000000000..d4759c537 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx @@ -0,0 +1,234 @@ +/* + This file is part of TALER + (C) 2016 GNUnet e.V. + + 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. + + 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 + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +import { + AmountJson, + Amounts, + AmountString, + PaytoUri, +} from "@gnu-taler/taler-util"; +import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; +import { Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import { Part } from "../components/Part"; +import { SelectList } from "../components/SelectList"; +import { + ButtonPrimary, + ErrorText, + Input, + InputWithLabel, +} from "../components/styled"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; +import * as wxApi from "../wxApi"; + +interface Props { + currency: string; +} +export function DepositPage({ currency }: Props): VNode { + const [success, setSuccess] = useState(false); + + const state = useAsyncAsHook(async () => { + const balance = await wxApi.getBalance(); + const bs = balance.balances.filter((b) => b.available.startsWith(currency)); + const currencyBalance = + bs.length === 0 + ? Amounts.getZero(currency) + : Amounts.parseOrThrow(bs[0].available); + const knownAccounts = await wxApi.listKnownBankAccounts(currency); + return { accounts: knownAccounts.accounts, currencyBalance }; + }); + + const accounts = + state === undefined ? [] : state.hasError ? [] : state.response.accounts; + + const currencyBalance = + state === undefined + ? Amounts.getZero(currency) + : state.hasError + ? Amounts.getZero(currency) + : state.response.currencyBalance; + + async function doSend(account: string, amount: AmountString): Promise<void> { + await wxApi.createDepositGroup(account, amount); + setSuccess(true); + } + + async function getFeeForAmount( + account: string, + amount: AmountString, + ): Promise<DepositFee> { + return await wxApi.getFeeForDeposit(account, amount); + } + + if (accounts.length === 0) return <div>loading..</div>; + if (success) return <div>deposit created</div>; + return ( + <View + knownBankAccounts={accounts} + balance={currencyBalance} + onSend={doSend} + onCalculateFee={getFeeForAmount} + /> + ); +} + +interface ViewProps { + knownBankAccounts: Array<PaytoUri>; + balance: AmountJson; + onSend: (account: string, amount: AmountString) => Promise<void>; + onCalculateFee: ( + account: string, + amount: AmountString, + ) => Promise<DepositFee>; +} + +export function View({ + knownBankAccounts, + balance, + onSend, + onCalculateFee, +}: ViewProps): VNode { + const accountMap = createLabelsForBankAccount(knownBankAccounts); + const [accountIdx, setAccountIdx] = useState(0); + const [amount, setAmount] = useState<number | undefined>(undefined); + const [fee, setFee] = useState<DepositFee | undefined>(undefined); + const currency = balance.currency; + const amountStr: AmountString = `${currency}:${amount}`; + + const account = knownBankAccounts[accountIdx]; + const accountURI = `payto://${account.targetType}/${account.targetPath}`; + useEffect(() => { + if (amount === undefined) return; + onCalculateFee(accountURI, amountStr).then((result) => { + setFee(result); + }); + }, [amount]); + + if (!balance) { + return <div>no balance</div>; + } + if (!knownBankAccounts || !knownBankAccounts.length) { + return <div>there is no known bank account to send money to</div>; + } + const parsedAmount = + amount === undefined ? undefined : Amounts.parse(amountStr); + const isDirty = amount !== 0; + const error = !isDirty + ? undefined + : !parsedAmount + ? "Invalid amount" + : Amounts.cmp(balance, parsedAmount) === -1 + ? `To much, your current balance is ${balance.value}` + : undefined; + + return ( + <Fragment> + <h2>Send {currency} to your account</h2> + <section> + <Input> + <SelectList + label="Bank account IBAN number" + list={accountMap} + name="account" + value={String(accountIdx)} + onChange={(s) => setAccountIdx(parseInt(s, 10))} + /> + </Input> + <InputWithLabel invalid={!!error}> + <label>Amount to send</label> + <div> + <span>{currency}</span> + <input + type="number" + value={amount} + onInput={(e) => { + const num = parseFloat(e.currentTarget.value); + console.log(num); + if (!Number.isNaN(num)) { + setAmount(num); + } else { + setAmount(undefined); + setFee(undefined); + } + }} + /> + </div> + {error && <ErrorText>{error}</ErrorText>} + </InputWithLabel> + {!error && fee && ( + <div style={{ textAlign: "center" }}> + <Part + title="Exchange fee" + text={Amounts.stringify(Amounts.sum([fee.wire, fee.coin]).amount)} + kind="negative" + /> + <Part + title="Change cost" + text={Amounts.stringify(fee.refresh)} + kind="negative" + /> + {parsedAmount && ( + <Part + title="Total received" + text={Amounts.stringify( + Amounts.sub( + parsedAmount, + Amounts.sum([fee.wire, fee.coin]).amount, + ).amount, + )} + kind="positive" + /> + )} + </div> + )} + </section> + <footer> + <div /> + <ButtonPrimary + disabled={!parsedAmount} + onClick={() => onSend(accountURI, amountStr)} + > + Send + </ButtonPrimary> + </footer> + </Fragment> + ); +} + +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 }); +} diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 22947d0c4..8172e02a2 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -369,8 +369,8 @@ export function TransactionView({ if (transaction.type === TransactionType.Deposit) { const fee = Amounts.sub( - Amounts.parseOrThrow(transaction.amountRaw), Amounts.parseOrThrow(transaction.amountEffective), + Amounts.parseOrThrow(transaction.amountRaw), ).amount; return ( <TransactionTemplate> @@ -379,15 +379,15 @@ export function TransactionView({ <br /> <Part big - title="Total deposit" + title="Total send" text={amountToString(transaction.amountEffective)} - kind="negative" + kind="neutral" /> <Part big - title="Purchase amount" + title="Deposit amount" text={amountToString(transaction.amountRaw)} - kind="neutral" + kind="positive" /> <Part big title="Fee" text={amountToString(fee)} kind="negative" /> </TransactionTemplate> |