aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-12-23 15:17:36 -0300
committerSebastian <sebasjm@gmail.com>2021-12-23 15:17:36 -0300
commit2e71117f59e0ae6106930e705ae6a54a9839281b (patch)
treea39856486a2801f56c65de245c871ce596f8ab16 /packages/taler-wallet-webextension/src/wallet
parentb8200de6f6c5ab9be3ff9f556c8acda013e574c3 (diff)
downloadwallet-core-2e71117f59e0ae6106930e705ae6a54a9839281b.tar.xz
deposit from wallet webex: wip
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BalancePage.tsx42
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx52
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage.tsx234
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx10
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>